Skip to content

Commit

Permalink
feat: support versioned txs in sendTransaction and simulateTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Sep 1, 2022
1 parent 8c1e193 commit d5d2468
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 2 deletions.
103 changes: 101 additions & 2 deletions web3.js/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
Transaction,
TransactionStatus,
TransactionVersion,
VersionedTransaction,
} from './transaction';
import {Message, MessageHeader, MessageV0, VersionedMessage} from './message';
import {AddressLookupTableAccount} from './programs/address-lookup-table/state';
Expand Down Expand Up @@ -801,6 +802,17 @@ export type TransactionReturnData = {
data: [string, TransactionReturnDataEncoding];
};

export type SimulateTransactionConfig = {
sigVerify?: boolean;
replaceRecentBlockhash?: boolean;
commitment?: Commitment;
accounts?: {
encoding: 'base64';
addresses: string[];
};
minContextSlot?: number;
};

export type SimulatedTransactionResponse = {
err: TransactionError | string | null;
logs: Array<string> | null;
Expand Down Expand Up @@ -4619,12 +4631,58 @@ export class Connection {

/**
* Simulate a transaction
*
* @deprecated Instead, call {@link simulateTransaction} with {@link
* VersionedTransaction} and {@link SimulateTransactionConfig} parameters
*/
async simulateTransaction(
simulateTransaction(
transactionOrMessage: Transaction | Message,
signers?: Array<Signer>,
includeAccounts?: boolean | Array<PublicKey>,
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;

/**
* Simulate a transaction
*/
// eslint-disable-next-line no-dupe-class-members
simulateTransaction(
transaction: VersionedTransaction,
config?: SimulateTransactionConfig,
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;

/**
* Simulate a transaction
*/
// eslint-disable-next-line no-dupe-class-members
async simulateTransaction(
transactionOrMessage: VersionedTransaction | Transaction | Message,
configOrSigners?: SimulateTransactionConfig | Array<Signer>,
includeAccounts?: boolean | Array<PublicKey>,
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
if ('message' in transactionOrMessage) {
const versionedTx = transactionOrMessage;
const wireTransaction = versionedTx.serialize();
const encodedTransaction =
Buffer.from(wireTransaction).toString('base64');
if (Array.isArray(configOrSigners) || includeAccounts !== undefined) {
throw new Error('Invalid arguments');
}

const config: any = configOrSigners || {};
config.encoding = 'base64';
if (!('commitment' in config)) {
config.commitment = this.commitment;
}

const args = [encodedTransaction, config];
const unsafeRes = await this._rpcRequest('simulateTransaction', args);
const res = create(unsafeRes, SimulatedTransactionResponseStruct);
if ('error' in res) {
throw new Error('failed to simulate transaction: ' + res.error.message);
}
return res.result;
}

let transaction;
if (transactionOrMessage instanceof Transaction) {
let originalTx: Transaction = transactionOrMessage;
Expand All @@ -4639,6 +4697,11 @@ export class Connection {
transaction._message = transaction._json = undefined;
}

if (!Array.isArray(configOrSigners)) {
throw new Error('Invalid arguments');
}

const signers = configOrSigners;
if (transaction.nonceInfo && signers) {
transaction.sign(...signers);
} else {
Expand Down Expand Up @@ -4725,12 +4788,48 @@ export class Connection {

/**
* Sign and send a transaction
*
* @deprecated Instead, call {@link sendTransaction} with a {@link
* VersionedTransaction}
*/
async sendTransaction(
sendTransaction(
transaction: Transaction,
signers: Array<Signer>,
options?: SendOptions,
): Promise<TransactionSignature>;

/**
* Send a signed transaction
*/
// eslint-disable-next-line no-dupe-class-members
sendTransaction(
transaction: VersionedTransaction,
options?: SendOptions,
): Promise<TransactionSignature>;

/**
* Sign and send a transaction
*/
// eslint-disable-next-line no-dupe-class-members
async sendTransaction(
transaction: VersionedTransaction | Transaction,
signersOrOptions?: Array<Signer> | SendOptions,
options?: SendOptions,
): Promise<TransactionSignature> {
if ('message' in transaction) {
if (signersOrOptions && Array.isArray(signersOrOptions)) {
throw new Error('Invalid arguments');
}

const wireTransaction = transaction.serialize();
return await this.sendRawTransaction(wireTransaction, options);
}

if (signersOrOptions === undefined || !Array.isArray(signersOrOptions)) {
throw new Error('Invalid arguments');
}

const signers = signersOrOptions;
if (transaction.nonceInfo) {
transaction.sign(...signers);
} else {
Expand Down
77 changes: 77 additions & 0 deletions web3.js/test/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3813,6 +3813,83 @@ describe('Connection', function () {
const {value} = await connection.getStakeMinimumDelegation();
expect(value).to.be.a('number');
});

it('sendTransaction', async () => {
const connection = new Connection(url, 'confirmed');
const payer = Keypair.generate();

await helpers.airdrop({
connection,
address: payer.publicKey,
amount: LAMPORTS_PER_SOL,
});

const recentBlockhash = await (
await helpers.latestBlockhash({connection})
).blockhash;

const versionedTx = new VersionedTransaction(
new Message({
header: {
numRequiredSignatures: 1,
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 0,
},
recentBlockhash,
instructions: [],
accountKeys: [payer.publicKey.toBase58()],
}),
);

versionedTx.sign([payer]);
await connection.sendTransaction(versionedTx);
});

it('simulateTransaction', async () => {
const connection = new Connection(url, 'confirmed');
const payer = Keypair.generate();

await helpers.airdrop({
connection,
address: payer.publicKey,
amount: LAMPORTS_PER_SOL,
});

const recentBlockhash = await (
await helpers.latestBlockhash({connection})
).blockhash;

const versionedTx = new VersionedTransaction(
new Message({
header: {
numRequiredSignatures: 1,
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 0,
},
recentBlockhash,
instructions: [],
accountKeys: [payer.publicKey.toBase58()],
}),
);

const response = await connection.simulateTransaction(versionedTx, {
accounts: {
encoding: 'base64',
addresses: [payer.publicKey.toBase58()],
},
});
expect(response.value.err).to.be.null;
expect(response.value.accounts).to.eql([
{
data: ['', 'base64'],
executable: false,
lamports: LAMPORTS_PER_SOL - 5000,
owner: SystemProgram.programId.toBase58(),
rentEpoch: 0,
},
]);
});

it('simulate transaction with message', async () => {
connection._commitment = 'confirmed';

Expand Down

0 comments on commit d5d2468

Please sign in to comment.