diff --git a/web3.js/src/connection.ts b/web3.js/src/connection.ts index 39fa03801d7347..71163d0a065977 100644 --- a/web3.js/src/connection.ts +++ b/web3.js/src/connection.ts @@ -711,13 +711,20 @@ export type SimulatedTransactionAccountInfo = { rentEpoch?: number; }; +export type TransactionReturnDataEncoding = 'base64'; + +export type TransactionReturnData = { + programId: string; + data: [string, TransactionReturnDataEncoding]; +}; + export type SimulatedTransactionResponse = { err: TransactionError | string | null; logs: Array | null; accounts?: (SimulatedTransactionAccountInfo | null)[] | null; unitsConsumed?: number; + returnData?: TransactionReturnData | null; }; - const SimulatedTransactionResponseStruct = jsonRpcResultAndContext( pick({ err: nullable(union([pick({}), string()])), @@ -738,6 +745,14 @@ const SimulatedTransactionResponseStruct = jsonRpcResultAndContext( ), ), unitsConsumed: optional(number()), + returnData: optional( + nullable( + pick({ + programId: string(), + data: tuple([string(), literal('base64')]), + }), + ), + ), }), ); diff --git a/web3.js/test/bpf-loader.test.ts b/web3.js/test/bpf-loader.test.ts index 4d4d5e820f58c3..47c0a9a96bde5e 100644 --- a/web3.js/test/bpf-loader.test.ts +++ b/web3.js/test/bpf-loader.test.ts @@ -147,6 +147,25 @@ if (process.env.TEST_LIVE) { ); }); + it('simulate transaction with returnData', async () => { + const simulatedTransaction = new Transaction().add({ + keys: [ + {pubkey: payerAccount.publicKey, isSigner: true, isWritable: true}, + ], + programId: program.publicKey, + }); + const {err, returnData} = ( + await connection.simulateTransaction(simulatedTransaction, [ + payerAccount, + ]) + ).value; + const expectedReturnData = new Uint8Array([1, 2, 3]); + var decodedData = Buffer.from(returnData.data[0], returnData.data[1]); + expect(err).to.be.null; + expect(returnData.programId).to.eql(program.publicKey.toString()); + expect(decodedData).to.eql(expectedReturnData); + }); + it('deprecated - simulate transaction without signature verification', async () => { const simulatedTransaction = new Transaction().add({ keys: [ diff --git a/web3.js/test/connection.test.ts b/web3.js/test/connection.test.ts index c7520aa9ac1b2f..c6e85a557e443f 100644 --- a/web3.js/test/connection.test.ts +++ b/web3.js/test/connection.test.ts @@ -3756,6 +3756,53 @@ describe('Connection', function () { verifySignatureStatus(response, expectedErr); }); + if (mockServer) { + it('returnData on simulateTransaction', async () => { + const tx = new Transaction(); + tx.feePayer = Keypair.generate().publicKey; + + const getLatestBlockhashResponse = { + method: 'getLatestBlockhash', + params: [], + value: { + blockhash: 'CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR', + feeCalculator: { + lamportsPerSignature: 5000, + }, + lastValidBlockHeight: 51, + }, + withContext: true, + }; + const simulateTransactionResponse = { + method: 'simulateTransaction', + params: [], + value: { + err: null, + accounts: null, + logs: [ + 'Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri invoke [1]', + 'Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri consumed 2366 of 1400000 compute units', + 'Program return: 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri KgAAAAAAAAA=', + 'Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success', + ], + returnData: { + data: ['KgAAAAAAAAA==', 'base64'], + programId: '83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri', + }, + unitsConsumed: 2366, + }, + withContext: true, + }; + await mockRpcResponse(getLatestBlockhashResponse); + await mockRpcResponse(simulateTransactionResponse); + const response = (await connection.simulateTransaction(tx)).value; + expect(response.returnData).to.eql({ + data: ['KgAAAAAAAAA==', 'base64'], + programId: '83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri', + }); + }); + } + if (process.env.TEST_LIVE) { it('simulate transaction with message', async () => { connection._commitment = 'confirmed'; diff --git a/web3.js/test/fixtures/noop-program/solana_bpf_rust_noop.so b/web3.js/test/fixtures/noop-program/solana_bpf_rust_noop.so index c9ccefa7a313b3..cde65c43a7f203 100755 Binary files a/web3.js/test/fixtures/noop-program/solana_bpf_rust_noop.so and b/web3.js/test/fixtures/noop-program/solana_bpf_rust_noop.so differ diff --git a/web3.js/test/fixtures/noop-program/src/lib.rs b/web3.js/test/fixtures/noop-program/src/lib.rs index 5caf947917ed9f..038aeacac62a1a 100644 --- a/web3.js/test/fixtures/noop-program/src/lib.rs +++ b/web3.js/test/fixtures/noop-program/src/lib.rs @@ -1,7 +1,8 @@ //! Example Rust-based BPF program that prints out the parameters passed to it use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, log::*, msg, pubkey::Pubkey, + account_info::AccountInfo, entrypoint::ProgramResult, log::*, msg, program::set_return_data, + pubkey::Pubkey, }; #[derive(Debug, PartialEq)] @@ -55,6 +56,12 @@ fn process_instruction( panic!(); } + { + // return data in simulate transaction + let return_data: &[u8] = &[1, 2, 3]; + set_return_data(return_data); + } + Ok(()) }