Skip to content

Commit 7540459

Browse files
authored
Merge pull request #7289 from BitGo/derek/SC-3563-verify-versioned-transactions
feat(sdk-coin-sol): update verifyTransaction for versioned sol tx
2 parents 062be3d + 11b1723 commit 7540459

File tree

1 file changed

+78
-0
lines changed
  • modules/sdk-coin-sol/src

1 file changed

+78
-0
lines changed

modules/sdk-coin-sol/src/sol.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)