Skip to content

Commit

Permalink
feat: update getTransaction to support versioned transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Aug 22, 2022
1 parent c657363 commit 95b85e1
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 16 deletions.
72 changes: 66 additions & 6 deletions web3.js/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ import {NonceAccount} from './nonce-account';
import {PublicKey} from './publickey';
import {Signer} from './keypair';
import {MS_PER_SLOT} from './timing';
import {Transaction, TransactionStatus} from './transaction';
import {Message} from './message';
import {
Transaction,
TransactionStatus,
TransactionVersion,
} from './transaction';
import {Message, MessageV0, VersionedMessage} from './message';
import {AddressLookupTableAccount} from './programs/address-lookup-table/state';
import assert from './utils/assert';
import {sleep} from './utils/sleep';
Expand Down Expand Up @@ -862,6 +866,8 @@ export type ConfirmedTransactionMeta = {
postTokenBalances?: Array<TokenBalance> | null;
/** The error result of transaction processing */
err: TransactionError | null;
/** The collection of addresses loaded using address lookup tables */
loadedAddresses?: LoadedAddresses;
};

/**
Expand All @@ -873,14 +879,16 @@ export type TransactionResponse = {
/** The transaction */
transaction: {
/** The transaction message */
message: Message;
message: VersionedMessage;
/** The transaction signatures */
signatures: string[];
};
/** Metadata produced from the transaction */
meta: ConfirmedTransactionMeta | null;
/** The unix timestamp of when the transaction was processed */
blockTime?: number | null;
/** The transaction version */
version?: TransactionVersion;
};

/**
Expand Down Expand Up @@ -935,6 +943,18 @@ export type ParsedInstruction = {
parsed: any;
};

/**
* A parsed address table lookup
*/
export type ParsedAddressTableLookup = {
/** Address lookup table account key */
accountKey: PublicKey;
/** Parsed instruction info */
writableIndexes: number[];
/** Parsed instruction info */
readonlyIndexes: number[];
};

/**
* A parsed transaction message
*/
Expand All @@ -945,6 +965,8 @@ export type ParsedMessage = {
instructions: (ParsedInstruction | PartiallyDecodedInstruction)[];
/** Recent blockhash */
recentBlockhash: string;
/** Address table lookups used to load additional accounts */
addressTableLookups?: ParsedAddressTableLookup[] | null;
};

/**
Expand Down Expand Up @@ -976,6 +998,8 @@ export type ParsedTransactionWithMeta = {
meta: ParsedTransactionMeta | null;
/** The unix timestamp of when the transaction was processed */
blockTime?: number | null;
/** The version of the transaction message */
version?: TransactionVersion;
};

/**
Expand Down Expand Up @@ -1722,6 +1746,12 @@ const GetSignatureStatusesRpcResult = jsonRpcResultAndContext(
*/
const GetMinimumBalanceForRentExemptionRpcResult = jsonRpcResult(number());

const AddressTableLookupStruct = pick({
accountKey: PublicKeyFromString,
writableIndexes: array(number()),
readonlyIndexes: array(number()),
});

const ConfirmedTransactionResult = pick({
signatures: array(string()),
message: pick({
Expand All @@ -1739,6 +1769,7 @@ const ConfirmedTransactionResult = pick({
}),
),
recentBlockhash: string(),
addressTableLookups: optional(array(AddressTableLookupStruct)),
}),
});

Expand Down Expand Up @@ -1799,6 +1830,7 @@ const ParsedConfirmedTransactionResult = pick({
),
instructions: array(ParsedOrRawInstruction),
recentBlockhash: string(),
addressTableLookups: optional(nullable(array(AddressTableLookupStruct))),
}),
});

Expand Down Expand Up @@ -1946,6 +1978,8 @@ const GetBlockSignaturesRpcResult = jsonRpcResult(
),
);

const TransactionVersionStruct = union([literal(0), literal('legacy')]);

/**
* Expected JSON RPC response for the "getTransaction" message
*/
Expand All @@ -1956,6 +1990,7 @@ const GetTransactionRpcResult = jsonRpcResult(
meta: ConfirmedTransactionMetaResult,
blockTime: optional(nullable(number())),
transaction: ConfirmedTransactionResult,
version: optional(TransactionVersionStruct),
}),
),
);
Expand All @@ -1970,6 +2005,7 @@ const GetParsedTransactionRpcResult = jsonRpcResult(
transaction: ParsedConfirmedTransactionResult,
meta: nullable(ParsedConfirmedTransactionMetaResult),
blockTime: optional(nullable(number())),
version: optional(TransactionVersionStruct),
}),
),
);
Expand Down Expand Up @@ -3742,6 +3778,10 @@ export class Connection {
signature: string,
rawConfig?: GetTransactionConfig,
): Promise<TransactionResponse | null> {
if (rawConfig && !('maxSupportedTransactionVersion' in rawConfig)) {
rawConfig.maxSupportedTransactionVersion = 0;
}

const {commitment, config} = extractCommitmentFromConfig(rawConfig);
const args = this._buildArgsAtLeastConfirmed(
[signature],
Expand All @@ -3758,11 +3798,31 @@ export class Connection {
const result = res.result;
if (!result) return result;

const messageResponse = result.transaction.message;
let message: VersionedMessage;
if (result.version === 0) {
message = new MessageV0({
header: messageResponse.header,
staticAccountKeys: messageResponse.accountKeys.map(
address => new PublicKey(address),
),
recentBlockhash: messageResponse.recentBlockhash,
compiledInstructions: messageResponse.instructions.map(ix => ({
programIdIndex: ix.programIdIndex,
accountKeyIndexes: ix.accounts,
data: bs58.decode(ix.data),
})),
addressTableLookups: messageResponse.addressTableLookups!,
});
} else {
message = new Message(messageResponse);
}

return {
...result,
transaction: {
...result.transaction,
message: new Message(result.transaction.message),
message,
},
};
}
Expand All @@ -3773,7 +3833,7 @@ export class Connection {
async getParsedTransaction(
signature: TransactionSignature,
commitmentOrConfig?: GetTransactionConfig | Finality,
): Promise<ParsedConfirmedTransaction | null> {
): Promise<ParsedTransactionWithMeta | null> {
const {commitment, config} =
extractCommitmentFromConfig(commitmentOrConfig);
const args = this._buildArgsAtLeastConfirmed(
Expand All @@ -3796,7 +3856,7 @@ export class Connection {
async getParsedTransactions(
signatures: TransactionSignature[],
commitmentOrConfig?: GetTransactionConfig | Finality,
): Promise<(ParsedConfirmedTransaction | null)[]> {
): Promise<(ParsedTransactionWithMeta | null)[]> {
const {commitment, config} =
extractCommitmentFromConfig(commitmentOrConfig);
const batch = signatures.map(signature => {
Expand Down
68 changes: 58 additions & 10 deletions web3.js/test/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4400,6 +4400,13 @@ describe('Connection', function () {
const transferIxData = encodeData(SYSTEM_INSTRUCTION_LAYOUTS.Transfer, {
lamports: BigInt(LAMPORTS_PER_SOL),
});
const addressTableLookups = [
{
accountKey: lookupTableKey,
writableIndexes: [0],
readonlyIndexes: [],
},
];
const transaction = new VersionedTransaction(
new MessageV0({
header: {
Expand All @@ -4416,13 +4423,7 @@ describe('Connection', function () {
data: transferIxData,
},
],
addressTableLookups: [
{
accountKey: lookupTableKey,
writableIndexes: [0],
readonlyIndexes: [],
},
],
addressTableLookups,
}),
);
transaction.sign([payer]);
Expand All @@ -4438,16 +4439,63 @@ describe('Connection', function () {
blockhash,
lastValidBlockHeight,
},
'processed',
'confirmed',
);

const transferToKey = lookupTableAddresses[0];
const transferToAccount = await connection.getAccountInfo(
transferToKey,
'processed',
'confirmed',
);
expect(transferToAccount?.lamports).to.be.eq(LAMPORTS_PER_SOL);

// fails without maxSupportedTransactionVersion option
await expect(
connection.getTransaction(signature, {
maxSupportedTransactionVersion: undefined,
commitment: 'confirmed',
}),
).to.be.rejectedWith(
'failed to get transaction: Transaction version (0) is not supported',
);

// fetch v0 transaction
{
const fetchedTransaction = await connection.getTransaction(
signature,
{commitment: 'confirmed'},
);
expect(fetchedTransaction).to.not.be.null;
expect(fetchedTransaction?.version).to.eq(0);
expect(fetchedTransaction?.meta?.loadedAddresses).to.eql({
readonly: [],
writable: [lookupTableAddresses[0]],
});
expect(
fetchedTransaction?.transaction.message.addressTableLookups,
).to.eql(addressTableLookups);
}

// fetch v0 parsed transaction
{
const parsedTransaction = await connection.getParsedTransaction(
signature,
{
maxSupportedTransactionVersion: 0,
commitment: 'confirmed',
},
);
expect(parsedTransaction).to.not.be.null;
expect(parsedTransaction?.version).to.eq(0);
expect(parsedTransaction?.meta?.loadedAddresses).to.eql({
readonly: [],
writable: [lookupTableAddresses[0]],
});
expect(
parsedTransaction?.transaction.message.addressTableLookups,
).to.eql(addressTableLookups);
}
}
});
}).timeout(5 * 1000);
}
});

0 comments on commit 95b85e1

Please sign in to comment.