33 explainTransaction ,
44 TransactionType as WasmTransactionType ,
55 type ExplainedTransaction as WasmExplainedTransaction ,
6+ type ParsedMethod ,
67} from '@bitgo/wasm-dot' ;
7- import type { TransactionExplanation , Material } from './iface' ;
8+ import type { BatchCallObject , ProxyType , TransactionExplanation , Material , TxData } from './iface' ;
89
910/**
1011 * Input entry for a DOT transaction.
@@ -33,9 +34,24 @@ function mapTransactionType(wasmType: WasmTransactionType): TransactionType {
3334 return TransactionType [ wasmType as keyof typeof TransactionType ] ?? TransactionType . Send ;
3435}
3536
36- /**
37- * Options for the WASM-based DOT transaction explanation adapter.
38- */
37+ /** Call WASM explainTransaction, returns the raw WASM result */
38+ function callWasmExplain ( params : {
39+ txHex : string ;
40+ material : Material ;
41+ senderAddress ?: string ;
42+ referenceBlock ?: string ;
43+ blockNumber ?: number ;
44+ } ) : WasmExplainedTransaction {
45+ return explainTransaction ( params . txHex , {
46+ context : {
47+ material : params . material ,
48+ sender : params . senderAddress ,
49+ referenceBlock : params . referenceBlock ,
50+ blockNumber : params . blockNumber ,
51+ } ,
52+ } ) ;
53+ }
54+
3955export interface ExplainDotTransactionParams {
4056 txHex : string ;
4157 material : Material ;
@@ -47,24 +63,19 @@ export interface ExplainDotTransactionParams {
4763 *
4864 * Thin adapter over @bitgo/wasm-dot's explainTransaction that maps
4965 * WASM types to BitGoJS TransactionExplanation format.
50- * Analogous to explainSolTransaction for Solana.
5166 */
5267export function explainDotTransaction ( params : ExplainDotTransactionParams ) : DotWasmExplanation {
53- const explained : WasmExplainedTransaction = explainTransaction ( params . txHex , {
54- context : { material : params . material , sender : params . senderAddress } ,
55- } ) ;
68+ const explained = callWasmExplain ( params ) ;
5669
5770 const sender = explained . sender || params . senderAddress || '' ;
5871 const type = mapTransactionType ( explained . type ) ;
5972 const methodName = `${ explained . method . pallet } .${ explained . method . name } ` ;
6073
61- // Map WASM outputs to BitGoJS format
6274 const outputs = explained . outputs . map ( ( o ) => ( {
6375 address : o . address ,
6476 amount : o . amount === 'ALL' ? '0' : o . amount ,
6577 } ) ) ;
6678
67- // Map WASM inputs to BitGoJS format (with numeric value for legacy compat)
6879 const inputs : DotInput [ ] = explained . inputs . map ( ( i ) => {
6980 const value = i . value === 'ALL' ? 0 : parseInt ( i . value || '0' , 10 ) ;
7081 return { address : i . address , value, valueString : i . value } ;
@@ -86,3 +97,145 @@ export function explainDotTransaction(params: ExplainDotTransactionParams): DotW
8697 inputs,
8798 } ;
8899}
100+
101+ // =============================================================================
102+ // toJsonFromWasm — WASM-based replacement for toJson()
103+ // =============================================================================
104+
105+ export interface ToJsonFromWasmParams {
106+ txHex : string ;
107+ material : Material ;
108+ senderAddress : string ;
109+ coinConfigName : string ;
110+ referenceBlock ?: string ;
111+ blockNumber ?: number ;
112+ }
113+
114+ /**
115+ * Produce a TxData object using WASM parsing instead of the JS txwrapper.
116+ *
117+ * This replaces the legacy `toJson()` path for chains where WASM parsing is enabled.
118+ * The WASM parser decodes the extrinsic bytes (with metadata-aware signed extension handling),
119+ * and this function maps the result to the TxData interface that consumers expect.
120+ */
121+ export function toJsonFromWasm ( params : ToJsonFromWasmParams ) : TxData {
122+ const explained = callWasmExplain ( params ) ;
123+ const type = mapTransactionType ( explained . type ) ;
124+ const method = explained . method ;
125+ const args = method . args as Record < string , unknown > ;
126+
127+ const result : TxData = {
128+ id : explained . id || '' ,
129+ sender : explained . sender || params . senderAddress ,
130+ referenceBlock : explained . referenceBlock || '' ,
131+ blockNumber : explained . blockNumber || 0 ,
132+ genesisHash : explained . genesisHash || '' ,
133+ nonce : explained . nonce ,
134+ specVersion : explained . specVersion || 0 ,
135+ transactionVersion : explained . transactionVersion || 0 ,
136+ chainName : explained . chainName || '' ,
137+ tip : Number ( explained . tip ) || 0 ,
138+ eraPeriod : explained . era . type === 'mortal' ? ( explained . era as { period : number } ) . period : 0 ,
139+ } ;
140+
141+ if ( type === TransactionType . Send ) {
142+ populateSendFields ( result , method , args ) ;
143+ } else if ( type === TransactionType . StakingActivate ) {
144+ populateStakingActivateFields ( result , method , args , params . senderAddress ) ;
145+ } else if ( type === TransactionType . StakingUnlock ) {
146+ result . amount = String ( args . value ?? '' ) ;
147+ } else if ( type === TransactionType . StakingWithdraw ) {
148+ result . numSlashingSpans = String ( args . numSlashingSpans ?? '0' ) ;
149+ } else if ( type === TransactionType . StakingClaim ) {
150+ result . validatorStash = String ( args . validatorStash ?? '' ) ;
151+ result . claimEra = String ( args . era ?? '' ) ;
152+ } else if ( type === TransactionType . AddressInitialization ) {
153+ populateAddressInitFields ( result , method , args ) ;
154+ } else if ( type === TransactionType . Batch ) {
155+ result . batchCalls = mapBatchCalls ( args . calls as ParsedMethod [ ] ) ;
156+ }
157+
158+ return result ;
159+ }
160+
161+ function populateSendFields ( result : TxData , method : ParsedMethod , args : Record < string , unknown > ) : void {
162+ const key = `${ method . pallet } .${ method . name } ` ;
163+
164+ if ( key === 'proxy.proxy' ) {
165+ // Proxy-wrapped transfer
166+ const innerCall = args . call as ParsedMethod | undefined ;
167+ result . owner = String ( args . real ?? '' ) ;
168+ result . forceProxyType = ( args . forceProxyType as ProxyType ) ?? undefined ;
169+ if ( innerCall ?. args ) {
170+ const innerArgs = innerCall . args as Record < string , unknown > ;
171+ result . to = String ( innerArgs . dest ?? '' ) ;
172+ result . amount = String ( innerArgs . value ?? '' ) ;
173+ }
174+ } else if ( key === 'balances.transferAll' ) {
175+ result . to = String ( args . dest ?? '' ) ;
176+ result . keepAlive = Boolean ( args . keepAlive ) ;
177+ } else {
178+ // transfer, transferKeepAlive, transferAllowDeath
179+ result . to = String ( args . dest ?? '' ) ;
180+ result . amount = String ( args . value ?? '' ) ;
181+ }
182+ }
183+
184+ function populateStakingActivateFields (
185+ result : TxData ,
186+ method : ParsedMethod ,
187+ args : Record < string , unknown > ,
188+ senderAddress : string
189+ ) : void {
190+ if ( method . name === 'bondExtra' ) {
191+ result . amount = String ( args . value ?? '' ) ;
192+ } else {
193+ // bond
194+ result . controller = senderAddress ;
195+ result . amount = String ( args . value ?? '' ) ;
196+ result . payee = String ( args . payee ?? '' ) ;
197+ }
198+ }
199+
200+ function populateAddressInitFields ( result : TxData , method : ParsedMethod , args : Record < string , unknown > ) : void {
201+ const key = `${ method . pallet } .${ method . name } ` ;
202+ result . method = key ;
203+ result . proxyType = String ( args . proxy_type ?? '' ) ;
204+ result . delay = String ( args . delay ?? '' ) ;
205+
206+ if ( key === 'proxy.createPure' ) {
207+ result . index = String ( args . index ?? '' ) ;
208+ } else {
209+ // addProxy, removeProxy
210+ result . owner = String ( args . delegate ?? '' ) ;
211+ }
212+ }
213+
214+ function mapBatchCalls ( calls : ParsedMethod [ ] | undefined ) : BatchCallObject [ ] {
215+ if ( ! calls ) return [ ] ;
216+ return calls . map ( ( call ) => ( {
217+ callIndex : `0x${ call . palletIndex . toString ( 16 ) . padStart ( 2 , '0' ) } ${ call . methodIndex . toString ( 16 ) . padStart ( 2 , '0' ) } ` ,
218+ args : transformBatchCallArgs ( ( call . args ?? { } ) as Record < string , unknown > ) ,
219+ } ) ) ;
220+ }
221+
222+ /** Transform WASM-decoded batch call args to match the Polkadot.js format that consumers expect */
223+ function transformBatchCallArgs ( args : Record < string , unknown > ) : Record < string , unknown > {
224+ const result : Record < string , unknown > = { } ;
225+ for ( const [ key , value ] of Object . entries ( args ) ) {
226+ if ( key === 'delegate' && typeof value === 'string' ) {
227+ // MultiAddress Id variant: string → { id: string }
228+ result [ key ] = { id : value } ;
229+ } else if ( key === 'value' && typeof value === 'string' ) {
230+ // Compact u128: string → number (matches Polkadot.js behavior)
231+ result [ key ] = Number ( value ) ;
232+ } else if ( key === 'payee' && typeof value === 'string' ) {
233+ // Enum unit variant: "Staked" → { staked: null }
234+ const variantName = value . charAt ( 0 ) . toLowerCase ( ) + value . slice ( 1 ) ;
235+ result [ key ] = { [ variantName ] : null } ;
236+ } else {
237+ result [ key ] = value ;
238+ }
239+ }
240+ return result ;
241+ }
0 commit comments