@@ -44,6 +44,7 @@ import {
4444 RecoveryTxRequest ,
4545 SignedTransaction ,
4646 SignTransactionOptions ,
47+ SolVersionedTransactionData ,
4748 TokenEnablement ,
4849 TokenEnablementConfig ,
4950 TransactionExplanation ,
@@ -315,6 +316,78 @@ export class Sol extends BaseCoin {
315316 }
316317 }
317318
319+ private hasSolVersionedTransactionData (
320+ txParams : TransactionParams
321+ ) : txParams is TransactionParams & { solVersionedTransactionData : SolVersionedTransactionData } {
322+ return 'solVersionedTransactionData' in txParams && txParams . solVersionedTransactionData !== undefined ;
323+ }
324+
325+ /**
326+ * Verify a versioned Solana transaction with basic structural validation
327+ * @param params - verification parameters
328+ * @returns true if verification passes
329+ */
330+ private async verifyVersionedTransaction ( params : SolVerifyTransactionOptions ) : Promise < boolean > {
331+ const { txParams, txPrebuild } = params ;
332+ const rawTx = txPrebuild . txBase64 || txPrebuild . txHex ;
333+
334+ if ( ! rawTx ) {
335+ throw new Error ( 'missing required tx prebuild property txBase64 or txHex' ) ;
336+ }
337+
338+ // Validate that the versioned transaction data is well-formed
339+ if ( ! this . hasSolVersionedTransactionData ( txParams ) ) {
340+ throw new Error ( 'solVersionedTransactionData is required for versioned transaction verification' ) ;
341+ }
342+
343+ const versionedData = txParams . solVersionedTransactionData ;
344+
345+ if ( ! versionedData . versionedInstructions || versionedData . versionedInstructions . length === 0 ) {
346+ throw new Error ( 'versioned transaction must have at least one instruction' ) ;
347+ }
348+
349+ if ( ! versionedData . staticAccountKeys || versionedData . staticAccountKeys . length === 0 ) {
350+ throw new Error ( 'versioned transaction must have at least one static account key' ) ;
351+ }
352+
353+ if ( ! versionedData . messageHeader ) {
354+ throw new Error ( 'versioned transaction must have a message header' ) ;
355+ }
356+
357+ // Validate that we can deserialize the transaction
358+ let rawTxBase64 = rawTx ;
359+ if ( HEX_REGEX . test ( rawTx ) ) {
360+ rawTxBase64 = Buffer . from ( rawTx , 'hex' ) . toString ( 'base64' ) ;
361+ }
362+
363+ try {
364+ const txBytes = Buffer . from ( rawTxBase64 , 'base64' ) ;
365+ if ( txBytes . length < 1 ) {
366+ throw new Error ( 'transaction bytes are empty' ) ;
367+ }
368+
369+ // Check version byte (after signatures)
370+ const numSignatures = txBytes [ 0 ] ;
371+ const signatureSize = 64 ;
372+ const versionByteOffset = 1 + numSignatures * signatureSize ;
373+
374+ if ( txBytes . length <= versionByteOffset ) {
375+ throw new Error ( 'transaction bytes are too short to contain version byte' ) ;
376+ }
377+
378+ const versionByte = txBytes [ versionByteOffset ] ;
379+ const isVersioned = ( versionByte & 0x80 ) !== 0 ;
380+
381+ if ( ! isVersioned ) {
382+ throw new Error ( 'transaction does not have versioned format' ) ;
383+ }
384+ } catch ( error ) {
385+ throw new Error ( `failed to validate versioned transaction format: ${ error . message } ` ) ;
386+ }
387+
388+ return true ;
389+ }
390+
318391 async verifyTransaction ( params : SolVerifyTransactionOptions ) : Promise < boolean > {
319392 // asset name to transfer amount map
320393 const totalAmount : Record < string , BigNumber > = { } ;
@@ -326,6 +399,11 @@ export class Sol extends BaseCoin {
326399 durableNonce : durableNonce ,
327400 verification : verificationOptions ,
328401 } = params ;
402+
403+ if ( this . hasSolVersionedTransactionData ( txParams ) ) {
404+ return this . verifyVersionedTransaction ( params ) ;
405+ }
406+
329407 const transaction = new Transaction ( coinConfig ) ;
330408 const rawTx = txPrebuild . txBase64 || txPrebuild . txHex ;
331409 const consolidateId = txPrebuild . consolidateId ;
0 commit comments