Skip to content

Commit 9a13185

Browse files
committed
Auto-detect transaction version
1 parent 76be8be commit 9a13185

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed

packages/client-core/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ pnpm add @solana/client-core
1414
## Quick start
1515

1616
```ts
17-
import { createClient } from '@solana/client-core';
17+
import { createClient } from "@solana/client-core";
1818

1919
const client = createClient({
20-
endpoint: 'https://api.devnet.solana.com',
20+
endpoint: "https://api.devnet.solana.com",
2121
});
2222

2323
// Fetch an account once.
@@ -26,7 +26,7 @@ console.log(account.lamports?.toString());
2626

2727
// Watch lamports in real time.
2828
const watcher = client.watchers.watchBalance({ address }, (lamports) => {
29-
console.log('balance:', lamports.toString());
29+
console.log("balance:", lamports.toString());
3030
});
3131

3232
// Later…
@@ -50,8 +50,8 @@ watcher.abort();
5050

5151
```ts
5252
const prepared = await client.helpers.transaction.prepare({
53-
authority: walletSession,
54-
instructions: [instruction],
53+
authority: walletSession,
54+
instructions: [instruction],
5555
});
5656

5757
const signature = await client.helpers.transaction.send(prepared);
@@ -62,6 +62,7 @@ console.log(signature.toString());
6262
- `sign` / `toWire` let you collect signatures or emit Base64 manually.
6363
- `send` submits the prepared transaction (or uses `signAndSend` if the wallet supports it).
6464
- `prepareAndSend` runs everything plus an optional simulation/logging pass via `prepareTransaction`.
65+
- Versions default to `0` automatically when any instruction references address lookup tables, otherwise `legacy`; pass `version` if you need to override.
6566

6667
Need just the tuning step? Call `client.prepareTransaction` directly with your unsigned message.
6768

packages/client-core/src/features/transactions.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,45 @@ describe('createTransactionHelper.prepareAndSend', () => {
165165
);
166166
});
167167
});
168+
169+
describe('createTransactionHelper.prepare', () => {
170+
const runtime = {
171+
rpc: {
172+
getLatestBlockhash: vi.fn(() => ({
173+
send: vi.fn().mockResolvedValue({ value: { blockhash: 'abc', lastValidBlockHeight: 123n } }),
174+
})),
175+
sendTransaction: vi.fn(),
176+
},
177+
rpcSubscriptions: {} as never,
178+
};
179+
const getFallbackCommitment = () => 'confirmed' as Commitment;
180+
const authority: TransactionSigner = { address: 'payer' } as TransactionSigner;
181+
182+
beforeEach(() => {
183+
vi.clearAllMocks();
184+
});
185+
186+
it('defaults to legacy when instructions do not reference address tables', async () => {
187+
const helper = createTransactionHelper(runtime as never, getFallbackCommitment);
188+
await helper.prepare({
189+
authority,
190+
instructions: [{ programAddress: 'Demo1111111111111111111111111111111111', data: new Uint8Array([1]) }],
191+
});
192+
expect(createTransactionMessageMock).toHaveBeenCalledWith({ version: 'legacy' });
193+
});
194+
195+
it('selects version 0 automatically when instructions reference address tables', async () => {
196+
const helper = createTransactionHelper(runtime as never, getFallbackCommitment);
197+
await helper.prepare({
198+
authority,
199+
instructions: [
200+
{
201+
programAddress: 'Demo1111111111111111111111111111111111',
202+
data: new Uint8Array([1]),
203+
addressTableLookups: [{}],
204+
},
205+
],
206+
});
207+
expect(createTransactionMessageMock).toHaveBeenCalledWith({ version: 0 });
208+
});
209+
});

packages/client-core/src/features/transactions.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,28 @@ function hasSetComputeUnitPriceInstruction(instructions: readonly TransactionIns
132132
);
133133
}
134134

135-
function resolveVersion(requested?: TransactionVersion | 'auto'): TransactionVersion {
135+
function instructionUsesAddressLookup(instruction: TransactionInstruction): boolean {
136+
if ('addressTableLookup' in instruction && instruction.addressTableLookup != null) {
137+
return true;
138+
}
139+
if (
140+
'addressTableLookups' in instruction &&
141+
Array.isArray(instruction.addressTableLookups) &&
142+
instruction.addressTableLookups.length > 0
143+
) {
144+
return true;
145+
}
146+
return false;
147+
}
148+
149+
function resolveVersion(
150+
requested: TransactionVersion | 'auto' | undefined,
151+
instructions: readonly TransactionInstruction[],
152+
): TransactionVersion {
136153
if (requested && requested !== 'auto') {
137154
return requested;
138155
}
139-
return 'legacy';
156+
return instructions.some(instructionUsesAddressLookup) ? 0 : 'legacy';
140157
}
141158

142159
function normaliseCommitment(request: TransactionPrepareRequest, getFallbackCommitment: () => Commitment): Commitment {
@@ -225,9 +242,8 @@ export function createTransactionHelper(
225242
}
226243
}
227244

228-
const version = resolveVersion(request.version);
229-
230245
const baseInstructions = [...request.instructions];
246+
const version = resolveVersion(request.version, baseInstructions);
231247

232248
const lifetime =
233249
request.lifetime ??

0 commit comments

Comments
 (0)