diff --git a/modules/sdk-coin-sol/package.json b/modules/sdk-coin-sol/package.json index a894ae1ef7..af75ef72c8 100644 --- a/modules/sdk-coin-sol/package.json +++ b/modules/sdk-coin-sol/package.json @@ -61,6 +61,7 @@ "@bitgo/sdk-core": "^36.31.0", "@bitgo/sdk-lib-mpc": "^10.9.0", "@bitgo/statics": "^58.25.0", + "@bitgo/wasm-solana": "^2.0.0", "@solana/spl-stake-pool": "1.1.8", "@solana/spl-token": "0.4.9", "@solana/web3.js": "1.92.1", diff --git a/modules/sdk-coin-sol/src/lib/explainTransactionWasm.ts b/modules/sdk-coin-sol/src/lib/explainTransactionWasm.ts new file mode 100644 index 0000000000..98d589a7c5 --- /dev/null +++ b/modules/sdk-coin-sol/src/lib/explainTransactionWasm.ts @@ -0,0 +1,110 @@ +import { ITokenEnablement } from '@bitgo/sdk-core'; +import { + explainTransaction as wasmExplainTransaction, + type ExplainedTransaction as WasmExplainedTransaction, + type StakingAuthorizeInfo, +} from '@bitgo/wasm-solana'; +import { UNAVAILABLE_TEXT } from './constants'; +import { StakingAuthorizeParams, TransactionExplanation as SolLibTransactionExplanation } from './iface'; +import { findTokenName } from './instructionParamsFactory'; + +export interface ExplainTransactionWasmOptions { + txBase64: string; + feeInfo?: { fee: string }; + tokenAccountRentExemptAmount?: string; + coinName: string; +} + +/** + * Map WASM staking authorize info to the legacy BitGoJS shape. + * Legacy uses different field names for Staker vs Withdrawer authority changes. + */ +function mapStakingAuthorize(info: StakingAuthorizeInfo): StakingAuthorizeParams { + if (info.authorizeType === 'Withdrawer') { + return { + stakingAddress: info.stakingAddress, + oldWithdrawAddress: info.oldAuthorizeAddress, + newWithdrawAddress: info.newAuthorizeAddress, + custodianAddress: info.custodianAddress, + }; + } + // Staker authority change + return { + stakingAddress: info.stakingAddress, + oldWithdrawAddress: '', + newWithdrawAddress: '', + oldStakingAuthorityAddress: info.oldAuthorizeAddress, + newStakingAuthorityAddress: info.newAuthorizeAddress, + }; +} + +/** + * Standalone WASM-based transaction explanation — no class instance needed. + * Thin adapter over @bitgo/wasm-solana's explainTransaction that resolves + * token names via @bitgo/statics and maps to BitGoJS TransactionExplanation. + */ +export function explainSolTransaction(params: ExplainTransactionWasmOptions): SolLibTransactionExplanation { + const txBytes = Buffer.from(params.txBase64, 'base64'); + const explained: WasmExplainedTransaction = wasmExplainTransaction(txBytes, { + lamportsPerSignature: BigInt(params.feeInfo?.fee || '0'), + tokenAccountRentExemptAmount: params.tokenAccountRentExemptAmount + ? BigInt(params.tokenAccountRentExemptAmount) + : undefined, + }); + + // Resolve token mint addresses → human-readable names (e.g. "tsol:usdc") + // Convert bigint amounts to strings at this serialization boundary. + const outputs = explained.outputs.map((o) => ({ + address: o.address, + amount: String(o.amount), + ...(o.tokenName ? { tokenName: findTokenName(o.tokenName, undefined, true) } : {}), + })); + + const inputs = explained.inputs.map((i) => ({ + address: i.address, + value: String(i.value), + })); + + // Build tokenEnablements with resolved token names + const tokenEnablements: ITokenEnablement[] = explained.tokenEnablements.map((te) => ({ + address: te.address, + tokenName: findTokenName(te.mintAddress, undefined, true), + tokenAddress: te.mintAddress, + })); + + return { + displayOrder: [ + 'id', + 'type', + 'blockhash', + 'durableNonce', + 'outputAmount', + 'changeAmount', + 'outputs', + 'changeOutputs', + 'tokenEnablements', + 'fee', + 'memo', + ], + id: explained.id ?? UNAVAILABLE_TEXT, + // WASM returns "StakingAuthorize" but when deserializing from bytes, BitGoJS + // always treats these as "StakingAuthorizeRaw" (the non-raw type only exists during building). + type: explained.type === 'StakingAuthorize' ? 'StakingAuthorizeRaw' : explained.type, + changeOutputs: [], + changeAmount: '0', + outputAmount: String(explained.outputAmount), + outputs, + inputs, + feePayer: explained.feePayer, + fee: { + fee: params.feeInfo ? String(explained.fee) : '0', + feeRate: params.feeInfo ? Number(params.feeInfo.fee) : undefined, + }, + memo: explained.memo, + blockhash: explained.blockhash, + durableNonce: explained.durableNonce, + tokenEnablements, + ataOwnerMap: explained.ataOwnerMap, + ...(explained.stakingAuthorize ? { stakingAuthorize: mapStakingAuthorize(explained.stakingAuthorize) } : {}), + }; +} diff --git a/modules/sdk-coin-sol/src/lib/iface.ts b/modules/sdk-coin-sol/src/lib/iface.ts index c12d8741b3..951b0e9da5 100644 --- a/modules/sdk-coin-sol/src/lib/iface.ts +++ b/modules/sdk-coin-sol/src/lib/iface.ts @@ -287,6 +287,9 @@ export interface TransactionExplanation extends BaseTransactionExplanation { memo?: string; stakingAuthorize?: StakingAuthorizeParams; stakingDelegate?: StakingDelegateParams; + inputs?: { address: string; value: string; coin?: string }[]; + feePayer?: string; + ataOwnerMap?: Record; } export class TokenAssociateRecipient { diff --git a/modules/sdk-coin-sol/src/lib/index.ts b/modules/sdk-coin-sol/src/lib/index.ts index 9a325fe6f2..1ad02ffe29 100644 --- a/modules/sdk-coin-sol/src/lib/index.ts +++ b/modules/sdk-coin-sol/src/lib/index.ts @@ -20,3 +20,4 @@ export { TransferBuilderV2 } from './transferBuilderV2'; export { WalletInitializationBuilder } from './walletInitializationBuilder'; export { Interface, Utils }; export { MessageBuilderFactory } from './messages'; +export { explainSolTransaction, ExplainTransactionWasmOptions } from './explainTransactionWasm'; diff --git a/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts b/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts index 34587efc54..ddadd0b1b1 100644 --- a/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts +++ b/modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts @@ -1239,7 +1239,7 @@ function parseCustomInstructions( return instructionData; } -function findTokenName( +export function findTokenName( mintAddress: string, instructionMetadata?: InstructionParams[], _useTokenAddressTokenName?: boolean diff --git a/modules/sdk-coin-sol/src/lib/transaction.ts b/modules/sdk-coin-sol/src/lib/transaction.ts index 1ddbc53c79..6c8f219b88 100644 --- a/modules/sdk-coin-sol/src/lib/transaction.ts +++ b/modules/sdk-coin-sol/src/lib/transaction.ts @@ -19,7 +19,7 @@ import { } from '@solana/web3.js'; import BigNumber from 'bignumber.js'; import base58 from 'bs58'; -import { KeyPair } from '.'; +import { explainSolTransaction, KeyPair } from '.'; import { InstructionBuilderTypes, UNAVAILABLE_TEXT, @@ -503,6 +503,18 @@ export class Transaction extends BaseTransaction { /** @inheritDoc */ explainTransaction(): TransactionExplanation { + // Testnet uses WASM-based parsing (no @solana/web3.js dependency). + // This validates the WASM path against production traffic before + // replacing the legacy implementation for all networks. + if (this._coinConfig.name === 'tsol') { + return explainSolTransaction({ + txBase64: this.toBroadcastFormat(), + feeInfo: this._lamportsPerSignature ? { fee: this._lamportsPerSignature.toString() } : undefined, + tokenAccountRentExemptAmount: this._tokenAccountRentExemptAmount, + coinName: this._coinConfig.name, + }); + } + if (validateRawMsgInstruction(this._solTransaction.instructions)) { return this.explainRawMsgAuthorizeTransaction(); } diff --git a/modules/sdk-coin-sol/src/sol.ts b/modules/sdk-coin-sol/src/sol.ts index 0e32bcb33e..4199ddf76b 100644 --- a/modules/sdk-coin-sol/src/sol.ts +++ b/modules/sdk-coin-sol/src/sol.ts @@ -57,7 +57,14 @@ import { } from '@bitgo/sdk-core'; import { auditEddsaPrivateKey, getDerivationPath } from '@bitgo/sdk-lib-mpc'; import { BaseNetwork, CoinFamily, coins, SolCoin, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; -import { KeyPair as SolKeyPair, Transaction, TransactionBuilder, TransactionBuilderFactory } from './lib'; +import { + KeyPair as SolKeyPair, + Transaction, + TransactionBuilder, + TransactionBuilderFactory, + explainSolTransaction, +} from './lib'; +import { TransactionExplanation as SolLibTransactionExplanation } from './lib/iface'; import { getAssociatedTokenAccountAddress, getSolTokenFromAddress, @@ -80,6 +87,7 @@ export interface ExplainTransactionOptions { txBase64: string; feeInfo: TransactionFee; tokenAccountRentExemptAmount?: string; + coinName?: string; } export interface TxInfo { @@ -696,6 +704,7 @@ export class Sol extends BaseCoin { } async parseTransaction(params: SolParseTransactionOptions): Promise { + // explainTransaction now uses WASM for testnet automatically const transactionExplanation = await this.explainTransaction({ txBase64: params.txBase64, feeInfo: params.feeInfo, @@ -741,9 +750,16 @@ export class Sol extends BaseCoin { /** * Explain a Solana transaction from txBase64 + * Uses WASM-based parsing for testnet, with fallback to legacy builder approach. * @param params */ async explainTransaction(params: ExplainTransactionOptions): Promise { + // Use WASM-based parsing for testnet (simpler, faster, no @solana/web3.js rebuild) + if (this.getChain() === 'tsol') { + return this.explainTransactionWithWasm(params) as SolTransactionExplanation; + } + + // Legacy approach for mainnet (until WASM is fully validated) const factory = this.getBuilder(); let rebuiltTransaction; @@ -767,6 +783,14 @@ export class Sol extends BaseCoin { return explainedTransaction as SolTransactionExplanation; } + /** + * Explain a Solana transaction using WASM parsing (bypasses @solana/web3.js rebuild). + * Delegates to standalone explainSolTransaction(). + */ + explainTransactionWithWasm(params: ExplainTransactionOptions): SolLibTransactionExplanation { + return explainSolTransaction({ ...params, coinName: params.coinName ?? this.getChain() }); + } + /** @inheritDoc */ async getSignablePayload(serializedTx: string): Promise { const factory = this.getBuilder(); diff --git a/modules/sdk-coin-sol/test/unit/jitoWasmVerification.ts b/modules/sdk-coin-sol/test/unit/jitoWasmVerification.ts new file mode 100644 index 0000000000..d5c5745631 --- /dev/null +++ b/modules/sdk-coin-sol/test/unit/jitoWasmVerification.ts @@ -0,0 +1,50 @@ +/** + * Verification test: Jito WASM parsing works in BitGoJS + */ +import * as should from 'should'; +import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { BitGoAPI } from '@bitgo/sdk-api'; +import { Tsol } from '../../src'; + +describe('Jito WASM Verification', function () { + let bitgo: TestBitGoAPI; + let tsol: Tsol; + + // From BitGoJS test/resources/sol.ts - JITO_STAKING_ACTIVATE_SIGNED_TX + const JITO_TX_BASE64 = + 'AdOUrFCk9yyhi1iB1EfOOXHOeiaZGQnLRwnypt+be8r9lrYMx8w7/QTnithrqcuBApg1ctJAlJMxNZ925vMP2Q0BAAQKReV5vPklPPaLR9/x+zo6XCwhusWyPAmuEqbgVWvwi0Ecg6pe+BOG2OETfAVS9ftz6va1oE4onLBolJ2N+ZOOhJ6naP7fZEyKrpuOIYit0GvFUPv3Fsgiuc5jx3g9lS4fCeaj/uz5kDLhwd9rlyLcs2NOe440QJNrw0sMwcjrUh/80UHpgyyvEK2RdJXKDycbWyk81HAn6nNwB+1A6zmgvQSKPgjDtJW+F/RUJ9ib7FuAx+JpXBhk12dD2zm+00bWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABU5Z4kwFGooUp7HpeX8OEs36dJAhZlMZWmpRKm8WZgKwaBTtTK9ooXRnL9rIYDGmPoTqFe+h1EtyKT9tvbABZQBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKnjMtr5L6vs6LY/96RABeX9/Zr6FYdWthxalfkEs7jQgQEICgUHAgABAwEEBgkJDuCTBAAAAAAA'; + + before(function () { + bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' }); + bitgo.safeRegister('tsol', Tsol.createInstance); + bitgo.initializeTestVars(); + tsol = bitgo.coin('tsol') as Tsol; + }); + + it('should parse Jito DepositSol transaction via WASM', function () { + // Verify the raw WASM parsing returns StakePoolDepositSol + const { parseTransaction } = require('@bitgo/wasm-solana'); + const txBytes = Buffer.from(JITO_TX_BASE64, 'base64'); + const wasmTx = parseTransaction(txBytes); + const wasmParsed = wasmTx.parse(); + + // Verify WASM returns StakePoolDepositSol instruction + const depositSolInstr = wasmParsed.instructionsData.find((i: { type: string }) => i.type === 'StakePoolDepositSol'); + should.exist(depositSolInstr, 'WASM should parse StakePoolDepositSol instruction'); + depositSolInstr.lamports.should.equal(300000n); + + // Now test explainTransactionWithWasm - should map to StakingActivate + const explained = tsol.explainTransactionWithWasm({ + txBase64: JITO_TX_BASE64, + feeInfo: { fee: '5000' }, + }); + + // Verify the transaction is correctly interpreted + should.exist(explained.id); + explained.type.should.equal('StakingActivate'); + explained.outputAmount.should.equal('300000'); + explained.outputs.length.should.equal(1); + explained.outputs[0].address.should.equal('Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb'); + explained.outputs[0].amount.should.equal('300000'); + }); +}); diff --git a/modules/sdk-coin-sol/test/unit/sol.ts b/modules/sdk-coin-sol/test/unit/sol.ts index 2c5c79e6e7..446f1ebc54 100644 --- a/modules/sdk-coin-sol/test/unit/sol.ts +++ b/modules/sdk-coin-sol/test/unit/sol.ts @@ -901,6 +901,13 @@ describe('SOL:', function () { amount: '300000', }, ], + inputs: [ + { + address: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + value: '300000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '5000', feeRate: 5000, @@ -912,6 +919,7 @@ describe('SOL:', function () { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -947,6 +955,13 @@ describe('SOL:', function () { amount: '300000', }, ], + inputs: [ + { + address: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + value: '300000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '5000', feeRate: 5000, @@ -958,6 +973,7 @@ describe('SOL:', function () { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -994,6 +1010,13 @@ describe('SOL:', function () { amount: '300000', }, ], + inputs: [ + { + address: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + value: '300000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '10000', feeRate: 5000, @@ -1002,6 +1025,7 @@ describe('SOL:', function () { durableNonce: undefined, memo: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1038,6 +1062,13 @@ describe('SOL:', function () { amount: '300000', }, ], + inputs: [ + { + address: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + value: '300000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '10000', feeRate: 5000, @@ -1046,6 +1077,7 @@ describe('SOL:', function () { durableNonce: undefined, memo: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1082,6 +1114,13 @@ describe('SOL:', function () { tokenName: 'tsol:usdc', }, ], + inputs: [ + { + address: '12f6D3WubGVeQoH2m8kTvvcrasWdXWwtVzUCyRNDZxA2', + value: '300000', + }, + ], + feePayer: '12f6D3WubGVeQoH2m8kTvvcrasWdXWwtVzUCyRNDZxA2', fee: { fee: '5000', feeRate: 5000, @@ -1093,6 +1132,7 @@ describe('SOL:', function () { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1129,6 +1169,13 @@ describe('SOL:', function () { tokenName: 'tsol:usdc', }, ], + inputs: [ + { + address: '12f6D3WubGVeQoH2m8kTvvcrasWdXWwtVzUCyRNDZxA2', + value: '300000', + }, + ], + feePayer: '12f6D3WubGVeQoH2m8kTvvcrasWdXWwtVzUCyRNDZxA2', fee: { fee: '5000', feeRate: 5000, @@ -1140,6 +1187,7 @@ describe('SOL:', function () { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1186,6 +1234,13 @@ describe('SOL:', function () { amount: '10000', }, ], + inputs: [ + { + address: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + value: '10000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '10000', feeRate: 5000, @@ -1194,6 +1249,7 @@ describe('SOL:', function () { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1233,6 +1289,8 @@ describe('SOL:', function () { changeAmount: '0', outputAmount: '0', outputs: [], + inputs: [], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '5000', feeRate: 5000, @@ -1241,6 +1299,7 @@ describe('SOL:', function () { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1286,6 +1345,13 @@ describe('SOL:', function () { amount: '10000', }, ], + inputs: [ + { + address: '7dRuGFbU2y2kijP6o1LYNzVyz4yf13MooqoionCzv5Za', + value: '10000', + }, + ], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '5000', feeRate: 5000, @@ -1294,6 +1360,7 @@ describe('SOL:', function () { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -1337,6 +1404,8 @@ describe('SOL:', function () { changeAmount: '0', outputAmount: '0', outputs: [], + inputs: [], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '3005000', feeRate: 5000, @@ -1351,6 +1420,9 @@ describe('SOL:', function () { tokenName: 'tsol:usdc', }, ], + ataOwnerMap: { + '141BFNem3pknc8CzPVLv1Ri3btgKdCsafYP5nXwmXfxU': '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + }, }); }); @@ -1404,6 +1476,8 @@ describe('SOL:', function () { changeAmount: '0', outputAmount: '0', outputs: [], + inputs: [], + feePayer: '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', fee: { fee: '6005000', feeRate: 5000, @@ -1423,6 +1497,10 @@ describe('SOL:', function () { tokenName: 'tsol:ray', }, ], + ataOwnerMap: { + '141BFNem3pknc8CzPVLv1Ri3btgKdCsafYP5nXwmXfxU': '5hr5fisPi6DXNuuRpm5XUbzpiEnmdyxXuBDTwzwZj5Pe', + '9KaLinZFNW5chL4J8UoKnTECppWVMz3ewgx4FAkxUDcf': '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', + }, }); }); @@ -1460,6 +1538,13 @@ describe('SOL:', function () { tokenName: 'tsol:usdc', }, ], + inputs: [ + { + address: '5U3bH5b6XtG99aVWLqwVzYPVpQiFHytBD68Rz2eFPZd7', + value: '10000', + }, + ], + feePayer: '5U3bH5b6XtG99aVWLqwVzYPVpQiFHytBD68Rz2eFPZd7', fee: { fee: '3005000', feeRate: 5000 }, memo: undefined, blockhash: '27E3MXFvXMUNYeMJeX1pAbERGsJfUbkaZTfgMgpmNN5g', @@ -1471,6 +1556,9 @@ describe('SOL:', function () { tokenName: 'tsol:usdc', }, ], + ataOwnerMap: { + '2eKjVtzV3oPTXFdtRSDj3Em9k1MV7k8WjKkBszQUwizS': 'C4zCqCaDm4D78zgaH3CeBEEBEAoMNhzSCj23qE92ndiP', + }, }); }); }); diff --git a/modules/sdk-coin-sol/test/unit/transaction.ts b/modules/sdk-coin-sol/test/unit/transaction.ts index effb9aa0fc..a5f72c69d0 100644 --- a/modules/sdk-coin-sol/test/unit/transaction.ts +++ b/modules/sdk-coin-sol/test/unit/transaction.ts @@ -259,6 +259,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -267,6 +274,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -304,8 +312,15 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { - fee: 'UNAVAILABLE', + fee: '0', feeRate: undefined, }, memo: undefined, @@ -315,6 +330,7 @@ describe('Sol Transaction', () => { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -363,8 +379,23 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + { + address: sender, + value: '10000', + }, + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { - fee: 'UNAVAILABLE', + fee: '0', feeRate: undefined, }, memo: 'memo text', @@ -374,6 +405,7 @@ describe('Sol Transaction', () => { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -413,6 +445,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -421,6 +460,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -460,6 +500,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '10000', feeRate: 5000, @@ -468,6 +515,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -507,8 +555,15 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { - fee: 'UNAVAILABLE', + fee: '0', feeRate: undefined, }, memo: 'memo text', @@ -518,6 +573,7 @@ describe('Sol Transaction', () => { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -558,6 +614,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '10000', feeRate: 5000, @@ -566,6 +629,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -680,6 +744,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '10000', feeRate: 5000, @@ -688,6 +759,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -723,6 +795,8 @@ describe('Sol Transaction', () => { changeAmount: '0', outputAmount: '0', outputs: [], + inputs: [], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -731,6 +805,7 @@ describe('Sol Transaction', () => { blockhash: 'GHtXQBsoZHVnNFa9YevAzFr17DJjgHXk3ycTKD5xD3Zi', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -771,6 +846,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: stakeAccount.pub, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -779,6 +861,7 @@ describe('Sol Transaction', () => { blockhash: 'GHtXQBsoZHVnNFa9YevAzFr17DJjgHXk3ycTKD5xD3Zi', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -820,6 +903,13 @@ describe('Sol Transaction', () => { amount: '10000', }, ], + inputs: [ + { + address: stakeAccount.pub, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -831,6 +921,7 @@ describe('Sol Transaction', () => { authWalletAddress: sender, }, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -870,6 +961,13 @@ describe('Sol Transaction', () => { tokenName: 'tsol:usdc', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -878,6 +976,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -921,9 +1020,18 @@ describe('Sol Transaction', () => { { address: 'DesU7XscZjng8yj5VX6AZsk3hWSW4sQ3rTG2LuyQ2P4H', amount: '10000', - tokenName: 'tsol:ams', + // WASM path resolves token name via @bitgo/statics; this mint is not registered, + // so the raw mint address is returned instead of the human-readable name. + tokenName: 'F4uLeXioFz3hw13MposuwaQbMcZbCjqvEGPPeRRB1Byf', + }, + ], + inputs: [ + { + address: sender, + value: '10000', }, ], + feePayer: sender, fee: { fee: '5000', feeRate: 5000, @@ -932,6 +1040,7 @@ describe('Sol Transaction', () => { blockhash: '5ne7phA48Jrvpn39AtupB8ZkCCAy8gLTfpGihZPuDqen', durableNonce: undefined, tokenEnablements: [], + ataOwnerMap: {}, }); }); @@ -983,8 +1092,23 @@ describe('Sol Transaction', () => { tokenName: 'tsol:usdc', }, ], + inputs: [ + { + address: sender, + value: '10000', + }, + { + address: sender, + value: '10000', + }, + { + address: sender, + value: '10000', + }, + ], + feePayer: sender, fee: { - fee: 'UNAVAILABLE', + fee: '0', feeRate: undefined, }, memo: 'memo text', @@ -994,6 +1118,7 @@ describe('Sol Transaction', () => { walletNonceAddress: '8Y7RM6JfcX4ASSNBkrkrmSbRu431YVi9Y3oLFnzC2dCh', }, tokenEnablements: [], + ataOwnerMap: {}, }); }); }); diff --git a/webpack/bitgojs.config.js b/webpack/bitgojs.config.js index cafdef7b5b..04c9ce5fa5 100644 --- a/webpack/bitgojs.config.js +++ b/webpack/bitgojs.config.js @@ -19,6 +19,7 @@ module.exports = { // Note: We can't use global `conditionNames: ['browser', 'import', ...]` because // third-party packages like @solana/spl-token and @bufbuild/protobuf have broken ESM builds. '@bitgo/wasm-utxo': path.resolve('../../node_modules/@bitgo/wasm-utxo/dist/esm/js/index.js'), + '@bitgo/wasm-solana': path.resolve('../../node_modules/@bitgo/wasm-solana/dist/esm/js/index.js'), '@bitgo/utxo-ord': path.resolve('../utxo-ord/dist/esm/index.js'), }, fallback: { diff --git a/yarn.lock b/yarn.lock index deb11a611e..91627716b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -996,6 +996,11 @@ monocle-ts "^2.3.13" newtype-ts "^0.3.5" +"@bitgo/wasm-solana@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@bitgo/wasm-solana/-/wasm-solana-2.0.1.tgz#a2f3c8e45386a1fad9f24a6564d506ed03e2d5ad" + integrity sha512-XzLKVJh1zwr28CC2/kSXWJkFrGUGCf59B9IVtlGrHrZm5ZdFNHOEjwjYcu75kOzNR5afBXOn19l4jt/KexJ4UA== + "@bitgo/wasm-utxo@^1.36.0": version "1.36.0" resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-1.36.0.tgz#4a0e9998e159ed9b6ecae7b9dad6f27971dd8690"