From 907cf1f57ab23d67241ec5385546c42681be1a58 Mon Sep 17 00:00:00 2001 From: Callum McIntyre Date: Fri, 12 Apr 2024 13:30:25 +0100 Subject: [PATCH] Refactor signers sign-transaction to use transaction messages (#2476) The signers now all take a `NewTransaction` as input. So these functions take a transaction message (with signers) and compile it to a `NewTransaction` before sending it to the signers. I think this PR will enable me to remove the old signer functions and clean up/rename things back, but I'll do that in a separate PR to make it easier to review. --- packages/signers/src/__tests__/__setup__.ts | 6 +- .../src/__tests__/sign-transaction-test.ts | 491 +++++++++++++----- packages/signers/src/sign-transaction.ts | 70 +-- .../__tests__/new-compile-transaction-test.ts | 25 +- .../src/new-compile-transaction.ts | 14 +- 5 files changed, 427 insertions(+), 179 deletions(-) diff --git a/packages/signers/src/__tests__/__setup__.ts b/packages/signers/src/__tests__/__setup__.ts index da38d33cba71..9b961ee60563 100644 --- a/packages/signers/src/__tests__/__setup__.ts +++ b/packages/signers/src/__tests__/__setup__.ts @@ -69,19 +69,19 @@ export function createMockMessageModifyingSigner( export function createMockTransactionPartialSigner( address: Address, -): TransactionPartialSigner & { signTransactions: jest.Mock } { +): TransactionPartialSigner & { newSignTransactions: jest.Mock; signTransactions: jest.Mock } { return { address, newSignTransactions: jest.fn(), signTransactions: jest.fn() }; } export function createMockTransactionModifyingSigner( address: Address, -): TransactionModifyingSigner & { modifyAndSignTransactions: jest.Mock } { +): TransactionModifyingSigner & { modifyAndSignTransactions: jest.Mock; newModifyAndSignTransactions: jest.Mock } { return { address, modifyAndSignTransactions: jest.fn(), newModifyAndSignTransactions: jest.fn() }; } export function createMockTransactionSendingSigner( address: Address, -): TransactionSendingSigner & { signAndSendTransactions: jest.Mock } { +): TransactionSendingSigner & { newSignAndSendTransactions: jest.Mock; signAndSendTransactions: jest.Mock } { return { address, newSignAndSendTransactions: jest.fn(), signAndSendTransactions: jest.fn() }; } diff --git a/packages/signers/src/__tests__/sign-transaction-test.ts b/packages/signers/src/__tests__/sign-transaction-test.ts index 20cc42850630..d563bf44e48d 100644 --- a/packages/signers/src/__tests__/sign-transaction-test.ts +++ b/packages/signers/src/__tests__/sign-transaction-test.ts @@ -6,25 +6,36 @@ import { SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, SolanaError, } from '@solana/errors'; -import { CompilableTransaction, IFullySignedTransaction, ITransactionWithSignatures } from '@solana/transactions'; +import { + compileTransaction, + FullySignedTransaction, + NewTransaction, + TransactionMessageBytes, +} from '@solana/transactions'; +import { ReadonlyUint8Array } from '../../../codecs-core/dist/types'; import { - partiallySignTransactionWithSigners, - signAndSendTransactionWithSigners, - signTransactionWithSigners, + partiallySignTransactionMessageWithSigners, + signAndSendTransactionMessageWithSigners, + signTransactionMessageWithSigners, } from '../sign-transaction'; import { - assertIsTransactionWithSingleSendingSigner, - ITransactionWithSingleSendingSigner, + assertIsTransactionMessageWithSingleSendingSigner, + ITransactionMessageWithSingleSendingSigner, } from '../transaction-with-single-sending-signer'; import { createMockTransactionCompositeSigner, + createMockTransactionMessageWithSigners, createMockTransactionModifyingSigner, createMockTransactionPartialSigner, createMockTransactionSendingSigner, - createMockTransactionWithSigners, } from './__setup__'; +jest.mock('@solana/transactions', () => ({ + ...jest.requireActual('@solana/transactions'), + compileTransaction: jest.fn(), +})); + describe('partiallySignTransactionWithSigners', () => { it('signs the transaction with its extracted signers', async () => { expect.assertions(3); @@ -32,15 +43,23 @@ describe('partiallySignTransactionWithSigners', () => { // Given a transaction with two signers A and B in its account metas. const signerA = createMockTransactionModifyingSigner('1111' as Address); const signerB = createMockTransactionPartialSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given signer A and B are mocked to provide the following signatures. - const modifiedTransaction = { ...transaction, signatures: { '1111': '1111_signature' } }; - signerA.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); - signerB.signTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); + const modifiedTransaction = { ...unsignedTransaction, signatures: { '1111': '1111_signature', '2222': null } }; + signerA.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + signerB.newSignTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then it contains the expected signatures. expect(signedTransaction.signatures).toStrictEqual({ @@ -49,8 +68,10 @@ describe('partiallySignTransactionWithSigners', () => { }); // And the signers were called with the expected parameters. - expect(signerA.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerB.signTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerA.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); + expect(signerB.newSignTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); }); it('signs modifying signers before partial signers', async () => { @@ -62,20 +83,28 @@ describe('partiallySignTransactionWithSigners', () => { // And mock implementations for both signers such that they append events to an array. const events: string[] = []; - signerA.modifyAndSignTransactions.mockImplementation((transactions: CompilableTransaction[]) => { + signerA.newModifyAndSignTransactions.mockImplementation((transactions: NewTransaction[]) => { events.push('signerA'); return transactions.map(tx => ({ ...tx, signatures: { '1111': '1111_signature' } })); }); - signerB.signTransactions.mockImplementation((transactions: CompilableTransaction[]) => { + signerB.newSignTransactions.mockImplementation((transactions: NewTransaction[]) => { events.push('signerB'); return transactions.map(() => ({ '2222': '2222_signature' })); }); // And given a transaction that contains theses signers in its account metas (in any order). - const transaction = createMockTransactionWithSigners([signerB, signerA]); + const transactionMessage = createMockTransactionMessageWithSigners([signerB, signerA]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then the modifying signer was called before the partial signer. expect(events).toStrictEqual(['signerA', 'signerB']); @@ -96,25 +125,31 @@ describe('partiallySignTransactionWithSigners', () => { // And mock implementations for both signers such that they append events to an array. const events: string[] = []; - const mockImplementation = - (signerId: string, address: string) => - async (transactions: (CompilableTransaction & Partial)[]) => { - events.push(`${signerId} starts`); - await new Promise(r => setTimeout(r, 500)); - events.push(`${signerId} ends`); - return transactions.map(tx => ({ - ...tx, - signatures: { ...tx.signatures, [address]: `${address}_signature` }, - })); - }; - signerA.modifyAndSignTransactions.mockImplementation(mockImplementation('signerA', '1111')); - signerB.modifyAndSignTransactions.mockImplementation(mockImplementation('signerB', '2222')); + const mockImplementation = (signerId: string, address: string) => async (transactions: NewTransaction[]) => { + events.push(`${signerId} starts`); + await new Promise(r => setTimeout(r, 500)); + events.push(`${signerId} ends`); + return transactions.map(tx => ({ + ...tx, + signatures: { ...tx.signatures, [address]: `${address}_signature` }, + })); + }; + signerA.newModifyAndSignTransactions.mockImplementation(mockImplementation('signerA', '1111')); + signerB.newModifyAndSignTransactions.mockImplementation(mockImplementation('signerB', '2222')); // And given a transaction that contains theses two signers in its account metas. - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then the first modifying signer finished signing before the second one started. expect(events).toStrictEqual(['signerA starts', 'signerA ends', 'signerB starts', 'signerB ends']); @@ -136,21 +171,28 @@ describe('partiallySignTransactionWithSigners', () => { // And mock implementations for both signers such that they append events to an array. const events: string[] = []; const mockImplementation = - (signerId: string, address: string, timeout: number) => - async (transactions: (CompilableTransaction & Partial)[]) => { + (signerId: string, address: string, timeout: number) => async (transactions: NewTransaction[]) => { events.push(`${signerId} starts`); await new Promise(r => setTimeout(r, timeout)); events.push(`${signerId} ends`); return transactions.map(() => ({ [address]: `${address}_signature` })); }; - signerA.signTransactions.mockImplementation(mockImplementation('signerA', '1111', 500)); - signerB.signTransactions.mockImplementation(mockImplementation('signerB', '2222', 600)); + signerA.newSignTransactions.mockImplementation(mockImplementation('signerA', '1111', 500)); + signerB.newSignTransactions.mockImplementation(mockImplementation('signerB', '2222', 600)); // And given a transaction that contains theses two signers in its account metas. - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then the second partial signer started signing before the first one finished. expect(events).toStrictEqual(['signerA starts', 'signerB starts', 'signerA ends', 'signerB ends']); @@ -172,21 +214,29 @@ describe('partiallySignTransactionWithSigners', () => { ...createMockTransactionSendingSigner('2222' as Address), ...createMockTransactionPartialSigner('2222' as Address), }; - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given signer B's partial interface is mocked to provide the following signatures. - signerB.signTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); + signerB.newSignTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then it only contains signer B's signature. - expect(signedTransaction.signatures).toStrictEqual({ '2222': '2222_signature' }); + expect(signedTransaction.signatures).toStrictEqual({ '1111': null, '2222': '2222_signature' }); // And only the partial signer function was called. - expect(signerB.signTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerA.signAndSendTransactions).not.toHaveBeenCalled(); - expect(signerB.signAndSendTransactions).not.toHaveBeenCalled(); + expect(signerB.newSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { abortSignal: undefined }); + expect(signerA.newSignAndSendTransactions).not.toHaveBeenCalled(); + expect(signerB.newSignAndSendTransactions).not.toHaveBeenCalled(); }); it('uses a composite signer as a modifying signer when there are no other modifying signers', async () => { @@ -198,20 +248,30 @@ describe('partiallySignTransactionWithSigners', () => { ...createMockTransactionModifyingSigner('1111' as Address), }; const signerB = createMockTransactionPartialSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given the following mocked signatures. - const modifiedTransaction = { ...transaction, signatures: { '1111': '1111_signature' } }; - signerA.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); - signerB.signTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); + const modifiedTransaction = { ...unsignedTransaction, signatures: { '1111': '1111_signature' } }; + signerA.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + signerB.newSignTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then signer A was used as a modifying signer. - expect(signerA.signTransactions).not.toHaveBeenCalled(); - expect(signerA.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerB.signTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerA.newSignTransactions).not.toHaveBeenCalled(); + expect(signerA.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); + expect(signerB.newSignTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); // And it contains the expected signatures. expect(signedTransaction.signatures).toStrictEqual({ @@ -229,20 +289,30 @@ describe('partiallySignTransactionWithSigners', () => { ...createMockTransactionModifyingSigner('1111' as Address), }; const signerB = createMockTransactionModifyingSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given the following mocked signatures. - const modifiedTransaction = { ...transaction, signatures: { '2222': '2222_signature' } }; - signerA.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - signerB.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + const modifiedTransaction = { ...unsignedTransaction, signatures: { '2222': '2222_signature' } }; + signerA.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + signerB.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then signer A was used as a partial signer. - expect(signerA.signTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); - expect(signerA.modifyAndSignTransactions).not.toHaveBeenCalled(); - expect(signerB.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); + expect(signerA.newSignTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerA.newModifyAndSignTransactions).not.toHaveBeenCalled(); + expect(signerB.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); // And it contains the expected signatures. expect(signedTransaction.signatures).toStrictEqual({ @@ -256,11 +326,20 @@ describe('partiallySignTransactionWithSigners', () => { // Given a transaction with a mocked partial signer. const signer = createMockTransactionPartialSigner('1111' as Address); - const transaction = createMockTransactionWithSigners([signer]); - signer.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); + + signer.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); // When we partially sign this transaction. - const signedTransaction = await partiallySignTransactionWithSigners(transaction); + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage); // Then the signed transaction and its signature dictionary are frozen. expect(signedTransaction).toBeFrozenObject(); @@ -273,11 +352,19 @@ describe('partiallySignTransactionWithSigners', () => { // Given a transaction with a mocked partial signer. const signer = createMockTransactionPartialSigner('1111' as Address); signer.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - const transaction = createMockTransactionWithSigners([signer]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given we've started partially signing this transaction whilst providing an abort signal. const abortController = new AbortController(); - const promise = partiallySignTransactionWithSigners(transaction, { + const promise = partiallySignTransactionMessageWithSigners(transactionMessage, { abortSignal: abortController.signal, }); @@ -287,6 +374,24 @@ describe('partiallySignTransactionWithSigners', () => { // Then we expect the partially signing promise to fail. await expect(promise).rejects.toThrow(/(The|This) operation was aborted/); }); + + it('compiles the input transaction message', async () => { + expect.assertions(1); + + // Given a transaction + const transactionMessage = createMockTransactionMessageWithSigners([]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: {}, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); + + // When we partially sign it + await partiallySignTransactionMessageWithSigners(transactionMessage); + + // Then we expect the compile function to have been called + expect(compileTransaction).toHaveBeenCalled(); + }); }); describe('signTransactionWithSigners', () => { @@ -296,15 +401,23 @@ describe('signTransactionWithSigners', () => { // Given a transaction with two signers A and B in its account metas. const signerA = createMockTransactionModifyingSigner('1111' as Address); const signerB = createMockTransactionPartialSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given signer A and B are mocked to provide the following signatures. - const modifiedTransaction = { ...transaction, signatures: { '1111': '1111_signature' } }; - signerA.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); - signerB.signTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); + const modifiedTransaction = { ...unsignedTransaction, signatures: { '1111': '1111_signature' } }; + signerA.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + signerB.newSignTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); // When we sign this transaction. - const signedTransaction = await signTransactionWithSigners(transaction); + const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); // Then it contains the expected signatures. expect(signedTransaction.signatures).toStrictEqual({ @@ -313,11 +426,13 @@ describe('signTransactionWithSigners', () => { }); // And the transaction is fully signed. - signedTransaction satisfies IFullySignedTransaction; + signedTransaction satisfies FullySignedTransaction; // And the signers were called with the expected parameters. - expect(signerA.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerB.signTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerA.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); + expect(signerB.newSignTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); }); it('asserts the transaction is fully signed', async () => { @@ -326,13 +441,21 @@ describe('signTransactionWithSigners', () => { // Given a transaction with a partial signer A and a sending signer B. const signerA = createMockTransactionPartialSigner('1111' as Address); const signerB = createMockTransactionSendingSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given signer A is mocked to provide the following signatures. - signerA.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + signerA.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); // When we try to sign this transaction. - const promise = signTransactionWithSigners(transaction); + const promise = signTransactionMessageWithSigners(transactionMessage); // Then we expect an error letting us know the transaction is not fully signed. // This is because sending signers are ignored by signTransactionWithSigners. @@ -349,11 +472,19 @@ describe('signTransactionWithSigners', () => { // Given a transaction with a mocked partial signer. const signer = createMockTransactionPartialSigner('1111' as Address); signer.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - const transaction = createMockTransactionWithSigners([signer]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given we've started signing this transaction whilst providing an abort signal. const abortController = new AbortController(); - const promise = signTransactionWithSigners(transaction, { + const promise = signTransactionMessageWithSigners(transactionMessage, { abortSignal: abortController.signal, }); @@ -363,6 +494,24 @@ describe('signTransactionWithSigners', () => { // Then we expect the signing promise to fail. await expect(promise).rejects.toThrow(/(The|This) operation was aborted/); }); + + it('compiles the input transaction message', async () => { + expect.assertions(1); + + // Given a transaction + const transactionMessage = createMockTransactionMessageWithSigners([]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: {}, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); + + // When we partially sign it + await signTransactionMessageWithSigners(transactionMessage); + + // Then we expect the compile function to have been called + expect(compileTransaction).toHaveBeenCalled(); + }); }); describe('signAndSendTransactionWithSigners', () => { @@ -372,20 +521,28 @@ describe('signAndSendTransactionWithSigners', () => { // Given a transaction with a partial signer A and a sending signer B. const signerA = createMockTransactionPartialSigner('1111' as Address); const signerB = createMockTransactionSendingSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given signer A and B are mocked to provide the following return values. - signerA.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - signerB.signAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + signerA.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + signerB.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); // When we sign and send this transaction. - assertIsTransactionWithSingleSendingSigner(transaction); - const transactionSignature = await signAndSendTransactionWithSigners(transaction); + assertIsTransactionMessageWithSingleSendingSigner(transactionMessage); + const transactionSignature = await signAndSendTransactionMessageWithSigners(transactionMessage); // Then the sending signer was used to send the transaction. - expect(signerA.signTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerB.signAndSendTransactions).toHaveBeenCalledWith( - [{ ...transaction, signatures: { '1111': '1111_signature' } }], + expect(signerA.newSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { abortSignal: undefined }); + expect(signerB.newSignAndSendTransactions).toHaveBeenCalledWith( + [{ ...unsignedTransaction, signatures: { '1111': '1111_signature', '2222': null } }], { abortSignal: undefined }, ); @@ -398,12 +555,20 @@ describe('signAndSendTransactionWithSigners', () => { // Given a transaction with a mocked partial signer but no sending signer. const signer = createMockTransactionPartialSigner('1111' as Address); - signer.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - const transaction = createMockTransactionWithSigners([signer]); + signer.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // When we try to force sign and send this transaction. - const promise = signAndSendTransactionWithSigners( - transaction as ITransactionWithSingleSendingSigner & typeof transaction, + const promise = signAndSendTransactionMessageWithSigners( + transactionMessage as ITransactionMessageWithSingleSendingSigner & typeof transactionMessage, ); // Then we expect an error letting us know no sending mechanism was provided. @@ -418,21 +583,30 @@ describe('signAndSendTransactionWithSigners', () => { // Given a transaction with a composite (partial, modifying & sending) signer A and a partial signer B. const signerA = createMockTransactionCompositeSigner('1111' as Address); const signerB = createMockTransactionPartialSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); - signerA.signAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); - signerB.signTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); + const transactionMessage = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); + + signerA.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + signerB.newSignTransactions.mockResolvedValueOnce([{ '2222': '2222_signature' }]); // When we sign and send this transaction. - assertIsTransactionWithSingleSendingSigner(transaction); - const transactionSignature = await signAndSendTransactionWithSigners(transaction); + assertIsTransactionMessageWithSingleSendingSigner(transactionMessage); + const transactionSignature = await signAndSendTransactionMessageWithSigners(transactionMessage); // Then the composite signer was used as a sending signer. - expect(signerA.signAndSendTransactions).toHaveBeenCalledWith( - [{ ...transaction, signatures: { '2222': '2222_signature' } }], + expect(signerA.newSignAndSendTransactions).toHaveBeenCalledWith( + [{ ...unsignedTransaction, signatures: { '1111': null, '2222': '2222_signature' } }], { abortSignal: undefined }, ); - expect(signerA.signTransactions).not.toHaveBeenCalled(); - expect(signerA.modifyAndSignTransactions).not.toHaveBeenCalled(); + expect(signerA.newSignTransactions).not.toHaveBeenCalled(); + expect(signerA.newModifyAndSignTransactions).not.toHaveBeenCalled(); // And the returned signature matches the one returned by that sending signer. expect(transactionSignature).toStrictEqual(new Uint8Array([1, 2, 3])); @@ -444,25 +618,37 @@ describe('signAndSendTransactionWithSigners', () => { // Given a transaction with a composite (partial, modifying & sending) signer A and a sending signer B. const signerA = createMockTransactionCompositeSigner('1111' as Address); const signerB = createMockTransactionSendingSigner('2222' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB]); + const transaction = createMockTransactionMessageWithSigners([signerA, signerB]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given the following mocked signatures for these signers. - const modifiedTransaction = { ...transaction, signatures: { '1111': '1111_signature' } }; - signerA.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); - signerB.signAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + const modifiedTransaction = { ...unsignedTransaction, signatures: { '1111': '1111_signature', '2222': null } }; + signerA.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + signerB.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); // When we sign and send this transaction. - assertIsTransactionWithSingleSendingSigner(transaction); - const transactionSignature = await signAndSendTransactionWithSigners(transaction); + assertIsTransactionMessageWithSingleSendingSigner(transaction); + const transactionSignature = await signAndSendTransactionMessageWithSigners(transaction); // Then the composite signer was used as a modifying signer. - expect(signerA.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); - expect(signerA.signTransactions).not.toHaveBeenCalled(); - expect(signerA.signAndSendTransactions).not.toHaveBeenCalled(); + expect(signerA.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); + expect(signerA.newSignTransactions).not.toHaveBeenCalled(); + expect(signerA.newSignAndSendTransactions).not.toHaveBeenCalled(); // And the sending only signer was used to send the transaction. expect(transactionSignature).toStrictEqual(new Uint8Array([1, 2, 3])); - expect(signerB.signAndSendTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerB.newSignAndSendTransactions).toHaveBeenCalledWith([modifiedTransaction], { + abortSignal: undefined, + }); }); it('uses a composite signer as a partial signer when other sending and modifying signers exist', async () => { @@ -473,28 +659,47 @@ describe('signAndSendTransactionWithSigners', () => { const signerA = createMockTransactionCompositeSigner('1111' as Address); const signerB = createMockTransactionSendingSigner('2222' as Address); const signerC = createMockTransactionModifyingSigner('3333' as Address); - const transaction = createMockTransactionWithSigners([signerA, signerB, signerC]); + const transaction = createMockTransactionMessageWithSigners([signerA, signerB, signerC]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + ['2222' as Address]: null, + ['3333' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given the following mocked signatures for these signers. - signerA.signTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); - signerB.signAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); - const modifiedTransaction = { ...transaction, signatures: { '3333': '3333_signature' } }; - signerC.modifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); + signerA.newSignTransactions.mockResolvedValueOnce([{ '1111': '1111_signature' }]); + signerB.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + const modifiedTransaction = { + ...unsignedTransaction, + signatures: { '1111': null, '2222': null, '3333': '3333_signature' }, + }; + signerC.newModifyAndSignTransactions.mockResolvedValueOnce([modifiedTransaction]); // When we sign and send this transaction. - assertIsTransactionWithSingleSendingSigner(transaction); - const transactionSignature = await signAndSendTransactionWithSigners(transaction); + assertIsTransactionMessageWithSingleSendingSigner(transaction); + const transactionSignature = await signAndSendTransactionMessageWithSigners(transaction); // Then the composite signer was used as a partial signer. - expect(signerA.signTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); - expect(signerA.modifyAndSignTransactions).not.toHaveBeenCalled(); - expect(signerA.signAndSendTransactions).not.toHaveBeenCalled(); + expect(signerA.newSignTransactions).toHaveBeenCalledWith([modifiedTransaction], { abortSignal: undefined }); + expect(signerA.newModifyAndSignTransactions).not.toHaveBeenCalled(); + expect(signerA.newSignAndSendTransactions).not.toHaveBeenCalled(); // And the other signers were used as expected. - expect(signerC.modifyAndSignTransactions).toHaveBeenCalledWith([transaction], { abortSignal: undefined }); + expect(signerC.newModifyAndSignTransactions).toHaveBeenCalledWith([unsignedTransaction], { + abortSignal: undefined, + }); expect(transactionSignature).toStrictEqual(new Uint8Array([1, 2, 3])); - expect(signerB.signAndSendTransactions).toHaveBeenCalledWith( - [{ ...transaction, signatures: { '1111': '1111_signature', '3333': '3333_signature' } }], + expect(signerB.newSignAndSendTransactions).toHaveBeenCalledWith( + [ + { + ...unsignedTransaction, + signatures: { '1111': '1111_signature', '2222': null, '3333': '3333_signature' }, + }, + ], { abortSignal: undefined }, ); }); @@ -504,13 +709,20 @@ describe('signAndSendTransactionWithSigners', () => { // Given a transaction with a mocked sending signer. const signer = createMockTransactionSendingSigner('1111' as Address); - signer.signAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); - const transaction = createMockTransactionWithSigners([signer]); + signer.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); // And given we've started signing this transaction whilst providing an abort signal. const abortController = new AbortController(); - assertIsTransactionWithSingleSendingSigner(transaction); - const promise = signAndSendTransactionWithSigners(transaction, { + assertIsTransactionMessageWithSingleSendingSigner(transactionMessage); + const promise = signAndSendTransactionMessageWithSigners(transactionMessage, { abortSignal: abortController.signal, }); @@ -520,4 +732,27 @@ describe('signAndSendTransactionWithSigners', () => { // Then we expect the signing promise to fail. await expect(promise).rejects.toThrow(/(The|This) operation was aborted/); }); + + it('compiles the input transaction message', async () => { + expect.assertions(1); + + // Given a transaction with a mocked sending signer. + const signer = createMockTransactionSendingSigner('1111' as Address); + signer.newSignAndSendTransactions.mockResolvedValueOnce([new Uint8Array([1, 2, 3])]); + const transactionMessage = createMockTransactionMessageWithSigners([signer]); + const unsignedTransaction: NewTransaction = { + messageBytes: new Uint8Array() as ReadonlyUint8Array as TransactionMessageBytes, + signatures: { + ['1111' as Address]: null, + }, + }; + jest.mocked(compileTransaction).mockReturnValue(unsignedTransaction); + + // When we sign and send it + assertIsTransactionMessageWithSingleSendingSigner(transactionMessage); + await signAndSendTransactionMessageWithSigners(transactionMessage); + + // Then we expect the compile function to have been called + expect(compileTransaction).toHaveBeenCalled(); + }); }); diff --git a/packages/signers/src/sign-transaction.ts b/packages/signers/src/sign-transaction.ts index d242ef5e0278..9d7526e0e3fb 100644 --- a/packages/signers/src/sign-transaction.ts +++ b/packages/signers/src/sign-transaction.ts @@ -1,42 +1,38 @@ import { SOLANA_ERROR__SIGNER__TRANSACTION_SENDING_SIGNER_MISSING, SolanaError } from '@solana/errors'; import { SignatureBytes } from '@solana/keys'; +import { CompilableTransactionMessage } from '@solana/transaction-messages'; import { - assertTransactionIsFullySigned, - CompilableTransaction, - IFullySignedTransaction, - ITransactionWithSignatures, + compileTransaction, + FullySignedTransaction, + newAssertTransactionIsFullySigned, + NewTransaction, } from '@solana/transactions'; -import { getSignersFromTransaction, ITransactionWithSigners } from './account-signer-meta'; +import { getSignersFromTransaction, ITransactionMessageWithSigners } from './account-signer-meta'; import { deduplicateSigners } from './deduplicate-signers'; import { isTransactionModifyingSigner, TransactionModifyingSigner } from './transaction-modifying-signer'; import { isTransactionPartialSigner, TransactionPartialSigner } from './transaction-partial-signer'; import { isTransactionSendingSigner, TransactionSendingSigner } from './transaction-sending-signer'; import { isTransactionSigner, TransactionSigner } from './transaction-signer'; -import { ITransactionWithSingleSendingSigner } from './transaction-with-single-sending-signer'; +import { ITransactionMessageWithSingleSendingSigner } from './transaction-with-single-sending-signer'; -type CompilableTransactionWithSigners = CompilableTransaction & - ITransactionWithSigners & - Partial; +type CompilableTransactionMessageWithSigners = CompilableTransactionMessage & ITransactionMessageWithSigners; /** * Signs a transaction using any signers that may be stored in IAccountSignerMeta instruction accounts * as well as any signers provided explicitly to this function. * It will ignore TransactionSendingSigners since this function does not send the transaction. */ -export async function partiallySignTransactionWithSigners< - TTransaction extends CompilableTransactionWithSigners = CompilableTransactionWithSigners, ->( - transaction: TTransaction, - config: { abortSignal?: AbortSignal } = {}, -): Promise { +export async function partiallySignTransactionMessageWithSigners< + TTransactionMessage extends CompilableTransactionMessageWithSigners = CompilableTransactionMessageWithSigners, +>(transactionMessage: TTransactionMessage, config: { abortSignal?: AbortSignal } = {}): Promise { const { partialSigners, modifyingSigners } = categorizeTransactionSigners( - deduplicateSigners(getSignersFromTransaction(transaction).filter(isTransactionSigner)), + deduplicateSigners(getSignersFromTransaction(transactionMessage).filter(isTransactionSigner)), { identifySendingSigner: false }, ); return await signModifyingAndPartialTransactionSigners( - transaction, + transactionMessage, modifyingSigners, partialSigners, config.abortSignal, @@ -49,14 +45,14 @@ export async function partiallySignTransactionWithSigners< * It will assert that the transaction is fully signed before returning. * It will ignore TransactionSendingSigners since this function does not send the transaction. */ -export async function signTransactionWithSigners< - TTransaction extends CompilableTransactionWithSigners = CompilableTransactionWithSigners, +export async function signTransactionMessageWithSigners< + TTransactionMessage extends CompilableTransactionMessageWithSigners = CompilableTransactionMessageWithSigners, >( - transaction: TTransaction, + transactionMessage: TTransactionMessage, config: { abortSignal?: AbortSignal } = {}, -): Promise { - const signedTransaction = await partiallySignTransactionWithSigners(transaction, config); - assertTransactionIsFullySigned(signedTransaction); +): Promise { + const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage, config); + newAssertTransactionIsFullySigned(signedTransaction); return signedTransaction; } @@ -66,10 +62,11 @@ export async function signTransactionWithSigners< * It will identify a single TransactionSendingSigners to use for sending the transaction, if any. * Otherwise, it will send the transaction using the provided fallbackSender. */ -export async function signAndSendTransactionWithSigners< - TTransaction extends CompilableTransactionWithSigners & - ITransactionWithSingleSendingSigner = CompilableTransactionWithSigners & ITransactionWithSingleSendingSigner, ->(transaction: TTransaction, config: { abortSignal?: AbortSignal } = {}): Promise { +export async function signAndSendTransactionMessageWithSigners< + TTransactionMessage extends CompilableTransactionMessageWithSigners & + ITransactionMessageWithSingleSendingSigner = CompilableTransactionMessageWithSigners & + ITransactionMessageWithSingleSendingSigner, +>(transaction: TTransactionMessage, config: { abortSignal?: AbortSignal } = {}): Promise { const abortSignal = config.abortSignal; const { partialSigners, modifyingSigners, sendingSigner } = categorizeTransactionSigners( deduplicateSigners(getSignersFromTransaction(transaction).filter(isTransactionSigner)), @@ -88,7 +85,7 @@ export async function signAndSendTransactionWithSigners< } abortSignal?.throwIfAborted(); - const [signature] = await sendingSigner.signAndSendTransactions([signedTransaction], { abortSignal }); + const [signature] = await sendingSigner.newSignAndSendTransactions([signedTransaction], { abortSignal }); abortSignal?.throwIfAborted(); return signature; @@ -172,32 +169,35 @@ function identifyTransactionModifyingSigners( * sequentially followed by the TransactionPartialSigners in parallel. */ async function signModifyingAndPartialTransactionSigners< - TTransaction extends CompilableTransactionWithSigners = CompilableTransactionWithSigners, + TTransactionMessage extends CompilableTransactionMessageWithSigners = CompilableTransactionMessageWithSigners, >( - transaction: TTransaction, + transactionMessage: TTransactionMessage, modifyingSigners: readonly TransactionModifyingSigner[] = [], partialSigners: readonly TransactionPartialSigner[] = [], abortSignal?: AbortSignal, -): Promise { +): Promise { + // serialize the transaction + const transaction = compileTransaction(transactionMessage); + // Handle modifying signers sequentially. const modifiedTransaction = await modifyingSigners.reduce( async (transaction, modifyingSigner) => { abortSignal?.throwIfAborted(); - const [tx] = await modifyingSigner.modifyAndSignTransactions([await transaction], { abortSignal }); + const [tx] = await modifyingSigner.newModifyAndSignTransactions([await transaction], { abortSignal }); return Object.freeze(tx); }, - Promise.resolve(transaction) as Promise, + Promise.resolve(transaction) as Promise, ); // Handle partial signers in parallel. abortSignal?.throwIfAborted(); const signatureDictionaries = await Promise.all( partialSigners.map(async partialSigner => { - const [signatures] = await partialSigner.signTransactions([modifiedTransaction], { abortSignal }); + const [signatures] = await partialSigner.newSignTransactions([modifiedTransaction], { abortSignal }); return signatures; }), ); - const signedTransaction: ITransactionWithSignatures & TTransaction = { + const signedTransaction: NewTransaction = { ...modifiedTransaction, signatures: Object.freeze( signatureDictionaries.reduce((signatures, signatureDictionary) => { diff --git a/packages/transactions/src/__tests__/new-compile-transaction-test.ts b/packages/transactions/src/__tests__/new-compile-transaction-test.ts index 1c2fb0924fdf..330c0139d80e 100644 --- a/packages/transactions/src/__tests__/new-compile-transaction-test.ts +++ b/packages/transactions/src/__tests__/new-compile-transaction-test.ts @@ -1,13 +1,19 @@ import '@solana/test-matchers/toBeFrozenObject'; import { Address } from '@solana/addresses'; +import { + CompiledTransactionMessage, + getCompiledTransactionMessageEncoder, + newCompileTransactionMessage, +} from '@solana/transaction-messages'; -import { CompiledMessage, compileTransactionMessage } from '../message'; import { compileTransaction } from '../new-compile-transaction'; -import { getCompiledMessageEncoder } from '../serializers'; -jest.mock('../message'); -jest.mock('../serializers/message'); +jest.mock('@solana/transaction-messages', () => ({ + ...jest.requireActual('@solana/transaction-messages'), + getCompiledTransactionMessageEncoder: jest.fn(), + newCompileTransactionMessage: jest.fn(), +})); type TransactionMessage = Parameters[0]; @@ -16,14 +22,19 @@ describe('compileTransactionMessage', () => { const mockAddressB = '1aaa' as Address; const mockCompiledMessage = { header: { + numReadonlyNonSignerAccounts: 0, + numReadonlySignerAccounts: 0, numSignerAccounts: 2, }, + instructions: [], + lifetimeToken: 'a', staticAccounts: [mockAddressA, mockAddressB], - } as CompiledMessage; + version: 0, + } as CompiledTransactionMessage; const mockCompiledMessageBytes = new Uint8Array(Array(100)).fill(1); beforeEach(() => { - (compileTransactionMessage as jest.Mock).mockReturnValue(mockCompiledMessage); - (getCompiledMessageEncoder as jest.Mock).mockReturnValue({ + (newCompileTransactionMessage as jest.Mock).mockReturnValue(mockCompiledMessage); + (getCompiledTransactionMessageEncoder as jest.Mock).mockReturnValue({ encode: jest.fn().mockReturnValue(mockCompiledMessageBytes), }); }); diff --git a/packages/transactions/src/new-compile-transaction.ts b/packages/transactions/src/new-compile-transaction.ts index 787b0f0e61cd..046fd9342375 100644 --- a/packages/transactions/src/new-compile-transaction.ts +++ b/packages/transactions/src/new-compile-transaction.ts @@ -1,15 +1,17 @@ import { Address } from '@solana/addresses'; import { ReadonlyUint8Array } from '@solana/codecs-core'; import { SignatureBytes } from '@solana/keys'; +import { + CompilableTransactionMessage, + getCompiledTransactionMessageEncoder, + newCompileTransactionMessage, +} from '@solana/transaction-messages'; -import { CompilableTransaction } from './compilable-transaction'; -import { compileTransactionMessage } from './message'; -import { getCompiledMessageEncoder } from './serializers/message'; import { NewTransaction, OrderedMap, TransactionMessageBytes } from './transaction'; -export function compileTransaction(transactionMessage: CompilableTransaction): NewTransaction { - const compiledMessage = compileTransactionMessage(transactionMessage); - const messageBytes = getCompiledMessageEncoder().encode( +export function compileTransaction(transactionMessage: CompilableTransactionMessage): NewTransaction { + const compiledMessage = newCompileTransactionMessage(transactionMessage); + const messageBytes = getCompiledTransactionMessageEncoder().encode( compiledMessage, ) as ReadonlyUint8Array as TransactionMessageBytes;