@@ -20,6 +20,7 @@ import {
2020} from '@aptos-labs/ts-sdk' ;
2121import { CustomTransactionParams } from '../iface' ;
2222import { validateModuleName , validateFunctionName } from '../utils/validation' ;
23+ import utils from '../utils' ;
2324
2425/**
2526 * Transaction class for custom Aptos transactions.
@@ -69,6 +70,7 @@ export class CustomTransaction extends Transaction {
6970
7071 /**
7172 * Parse a transaction payload to extract the custom transaction data
73+ * Requires ABI information for proper type-aware conversion
7274 *
7375 * @param {TransactionPayload } payload - The transaction payload to parse
7476 */
@@ -94,16 +96,23 @@ export class CustomTransaction extends Transaction {
9496 this . _functionName = functionIdentifier ;
9597 this . _typeArguments = entryFunction . type_args . map ( ( typeArg ) => typeArg . toString ( ) ) ;
9698
97- this . _functionArguments = entryFunction . args . map ( ( arg : any ) => {
98- if ( typeof arg === 'string' || typeof arg === 'number' || typeof arg === 'boolean' ) {
99- return arg ;
100- }
101- if ( arg && typeof arg === 'object' && 'data' in arg && arg . data ) {
102- const bytes = Array . from ( arg . data ) as number [ ] ;
103- return '0x' + bytes . map ( ( b : number ) => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
104- }
105- return arg ;
106- } ) ;
99+ // Parse arguments using ABI information
100+ // If ABI is available, parse with type awareness; otherwise store raw args for later processing
101+ if ( this . _entryFunctionAbi ?. parameters ) {
102+ this . _functionArguments = entryFunction . args . map ( ( arg : any , index : number ) => {
103+ const paramType = this . _entryFunctionAbi ?. parameters ?. [ index ] ;
104+ if ( ! paramType ) {
105+ throw new InvalidTransactionError (
106+ `Missing ABI parameter type for argument ${ index } in ${ this . _moduleName } ::${ this . _functionName } . ` +
107+ 'ABI parameter count mismatch.'
108+ ) ;
109+ }
110+ return this . convertArgumentByABI ( arg , paramType ) ;
111+ } ) ;
112+ } else {
113+ // Store raw arguments temporarily - transaction will be re-parsed when ABI is provided
114+ this . _functionArguments = entryFunction . args . map ( ( arg : any ) => arg ) ;
115+ }
107116 }
108117
109118 /**
@@ -112,24 +121,8 @@ export class CustomTransaction extends Transaction {
112121 protected getTransactionPayloadData ( ) : InputGenerateTransactionPayloadData {
113122 const functionName = this . getValidatedFullFunctionName ( ) ;
114123
115- // Convert arguments based on ABI information if available
116- const processedArguments = this . _functionArguments . map ( ( arg : any , index : number ) => {
117- // Use ABI to identify the expected type for this argument
118- const paramType = this . _entryFunctionAbi ?. parameters ?. [ index ] ;
119- if ( paramType ) {
120- return this . convertArgumentByABI ( arg , paramType ) ;
121- }
122-
123- // Fallback: basic conversion for common cases
124- if ( typeof arg === 'string' && arg . startsWith ( '0x' ) && arg . length === 66 ) {
125- try {
126- return AccountAddress . fromString ( arg ) ;
127- } catch {
128- return arg ;
129- }
130- }
131- return arg ;
132- } ) ;
124+ // Arguments are pre-processed during parsing phase
125+ const processedArguments = this . _functionArguments ;
133126
134127 return {
135128 function : functionName ,
@@ -184,6 +177,16 @@ export class CustomTransaction extends Transaction {
184177
185178 // Handle nested BCS structures with 'value' property
186179 if ( arg && typeof arg === 'object' && 'value' in arg && arg . value ) {
180+ // Check if inner value is a Uint8Array (common for U64 arguments)
181+ if ( arg . value . value && arg . value . value instanceof Uint8Array ) {
182+ const bytes = Array . from ( arg . value . value ) as number [ ] ;
183+ if ( this . isNumericType ( paramType ) ) {
184+ return this . convertNumericArgument ( bytes , paramType ) ;
185+ }
186+ // For non-numeric types, convert to hex string
187+ return bytesToHex ( bytes ) ;
188+ }
189+
187190 // Simple value wrapper
188191 if ( ! ( 'value' in arg . value ) || typeof arg . value . value !== 'object' ) {
189192 return this . convertArgumentByABI ( arg . value , paramType ) ;
@@ -200,24 +203,13 @@ export class CustomTransaction extends Transaction {
200203 const bytes = keys . map ( ( k ) => bytesObj [ k ] ) ;
201204 let extractedValue : any ;
202205
203- // Convert bytes based on parameter type
204- if (
205- paramType instanceof TypeTagAddress ||
206- paramType instanceof TypeTagU64 ||
207- paramType instanceof TypeTagU128 ||
208- paramType instanceof TypeTagU256
209- ) {
206+ // Convert bytes based on parameter type using unified approach
207+ if ( this . isNumericType ( paramType ) ) {
208+ extractedValue = this . convertNumericArgument ( bytes , paramType ) ;
209+ } else if ( paramType instanceof TypeTagAddress ) {
210210 extractedValue = bytesToHex ( bytes ) ;
211211 } else if ( paramType instanceof TypeTagBool ) {
212212 extractedValue = bytes [ 0 ] === 1 ;
213- } else if ( paramType instanceof TypeTagU8 || paramType instanceof TypeTagU16 || paramType instanceof TypeTagU32 ) {
214- // Convert little-endian bytes to number using the original algorithm
215- // to ensure consistent behavior with large numbers
216- let result = 0 ;
217- for ( let i = bytes . length - 1 ; i >= 0 ; i -- ) {
218- result = result * 256 + bytes [ i ] ;
219- }
220- extractedValue = result ;
221213 } else {
222214 extractedValue = bytesToHex ( bytes ) ;
223215 }
@@ -274,4 +266,40 @@ export class CustomTransaction extends Transaction {
274266
275267 return fullName as `${string } ::${string } ::${string } `;
276268 }
269+
270+ /**
271+ * Check if a parameter type is a numeric type
272+ */
273+ private isNumericType ( paramType : any ) : boolean {
274+ return (
275+ paramType instanceof TypeTagU8 ||
276+ paramType instanceof TypeTagU16 ||
277+ paramType instanceof TypeTagU32 ||
278+ paramType instanceof TypeTagU64 ||
279+ paramType instanceof TypeTagU128 ||
280+ paramType instanceof TypeTagU256
281+ ) ;
282+ }
283+
284+ /**
285+ * Convert numeric argument using consistent little-endian byte handling
286+ */
287+ private convertNumericArgument ( bytes : number [ ] , paramType : any ) : any {
288+ if ( paramType instanceof TypeTagU8 || paramType instanceof TypeTagU16 || paramType instanceof TypeTagU32 ) {
289+ // Small integers: use Number for compatibility
290+ let result = 0 ;
291+ for ( let i = bytes . length - 1 ; i >= 0 ; i -- ) {
292+ result = result * 256 + bytes [ i ] ;
293+ }
294+ return result ;
295+ }
296+
297+ if ( paramType instanceof TypeTagU64 || paramType instanceof TypeTagU128 || paramType instanceof TypeTagU256 ) {
298+ // Large integers: reuse the existing utility method
299+ return utils . getAmountFromPayloadArgs ( new Uint8Array ( bytes ) ) ;
300+ }
301+
302+ // Fallback for unexpected numeric types
303+ return '0x' + bytes . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
304+ }
277305}
0 commit comments