Skip to content

Commit 8ea27fc

Browse files
authored
feat: Agave v2 RPC: replace getRecentBlockhash with getLatestBlockhash (#3419)
* feat: agave v2 rpc: replace `getConfirmedBlock` with `getBlock` * feat: agave v2 rpc: replace `getConfirmedTransaction` with `getTransaction` * feat: agave v2 rpc: replace `getRecentBlockhash` with `getLatestBlockhash` * protect against json stringify
1 parent a805cb9 commit 8ea27fc

File tree

3 files changed

+132
-78
lines changed

3 files changed

+132
-78
lines changed

src/connection.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,20 +2599,6 @@ const GetParsedTransactionRpcResult = jsonRpcResult(
25992599
),
26002600
);
26012601

2602-
/**
2603-
* Expected JSON RPC response for the "getRecentBlockhash" message
2604-
*
2605-
* @deprecated Deprecated since RPC v1.8.0. Please use {@link GetLatestBlockhashRpcResult} instead.
2606-
*/
2607-
const GetRecentBlockhashAndContextRpcResult = jsonRpcResultAndContext(
2608-
pick({
2609-
blockhash: string(),
2610-
feeCalculator: pick({
2611-
lamportsPerSignature: number(),
2612-
}),
2613-
}),
2614-
);
2615-
26162602
/**
26172603
* Expected JSON RPC response for the "getLatestBlockhash" message
26182604
*/
@@ -4567,13 +4553,29 @@ export class Connection {
45674553
feeCalculator: FeeCalculator;
45684554
}>
45694555
> {
4570-
const args = this._buildArgs([], commitment);
4571-
const unsafeRes = await this._rpcRequest('getRecentBlockhash', args);
4572-
const res = create(unsafeRes, GetRecentBlockhashAndContextRpcResult);
4573-
if ('error' in res) {
4574-
throw new SolanaJSONRPCError(res.error, 'failed to get recent blockhash');
4575-
}
4576-
return res.result;
4556+
const {
4557+
context,
4558+
value: {blockhash},
4559+
} = await this.getLatestBlockhashAndContext(commitment);
4560+
const feeCalculator = {
4561+
get lamportsPerSignature(): number {
4562+
throw new Error(
4563+
'The capability to fetch `lamportsPerSignature` using the `getRecentBlockhash` API is ' +
4564+
'no longer offered by the network. Use the `getFeeForMessage` API to obtain the fee ' +
4565+
'for a given message.',
4566+
);
4567+
},
4568+
toJSON() {
4569+
return {};
4570+
},
4571+
};
4572+
return {
4573+
context,
4574+
value: {
4575+
blockhash,
4576+
feeCalculator,
4577+
},
4578+
};
45774579
}
45784580

45794581
/**

test/connection.test.ts

Lines changed: 74 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -417,30 +417,38 @@ describe('Connection', function () {
417417
});
418418
}
419419

420-
{
421-
await helpers.airdrop({
422-
connection,
423-
address: account1.publicKey,
424-
amount: 0.5 * LAMPORTS_PER_SOL,
425-
});
420+
await helpers.airdrop({
421+
connection,
422+
address: account1.publicKey,
423+
amount: 0.5 * LAMPORTS_PER_SOL,
424+
});
426425

427-
const transaction = new Transaction().add(
428-
SystemProgram.assign({
429-
accountPubkey: account1.publicKey,
430-
programId: programId.publicKey,
431-
}),
432-
);
426+
const {blockhash, lastValidBlockHeight} = await helpers.latestBlockhash({
427+
connection,
428+
});
429+
const feePayer = account1.publicKey;
433430

434-
await helpers.processTransaction({
435-
connection,
436-
transaction,
437-
signers: [account1],
438-
commitment: 'confirmed',
439-
});
440-
}
431+
const transaction = new Transaction({
432+
blockhash,
433+
lastValidBlockHeight,
434+
feePayer,
435+
}).add(
436+
SystemProgram.assign({
437+
accountPubkey: account1.publicKey,
438+
programId: programId.publicKey,
439+
}),
440+
);
441441

442-
const feeCalculator = (await helpers.recentBlockhash({connection}))
443-
.feeCalculator;
442+
const message = transaction._compile();
443+
const fee =
444+
(await helpers.getFeeForMessage({connection, message})).value ?? 0;
445+
446+
await helpers.processTransaction({
447+
connection,
448+
transaction,
449+
signers: [account1],
450+
commitment: 'confirmed',
451+
});
444452

445453
{
446454
await mockRpcResponse({
@@ -454,7 +462,7 @@ describe('Connection', function () {
454462
account: {
455463
data: ['', 'base64'],
456464
executable: false,
457-
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
465+
lamports: LAMPORTS_PER_SOL - fee,
458466
owner: programId.publicKey.toBase58(),
459467
rentEpoch: 20,
460468
space: 0,
@@ -465,8 +473,7 @@ describe('Connection', function () {
465473
account: {
466474
data: ['', 'base64'],
467475
executable: false,
468-
lamports:
469-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
476+
lamports: 0.5 * LAMPORTS_PER_SOL - fee,
470477
owner: programId.publicKey.toBase58(),
471478
rentEpoch: 20,
472479
space: 0,
@@ -485,13 +492,11 @@ describe('Connection', function () {
485492
expect(programAccounts).to.have.length(2);
486493
programAccounts.forEach(function (keyedAccount) {
487494
if (keyedAccount.pubkey.equals(account0.publicKey)) {
488-
expect(keyedAccount.account.lamports).to.eq(
489-
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
490-
);
495+
expect(keyedAccount.account.lamports).to.eq(LAMPORTS_PER_SOL - fee);
491496
} else {
492497
expect(keyedAccount.pubkey).to.eql(account1.publicKey);
493498
expect(keyedAccount.account.lamports).to.eq(
494-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
499+
0.5 * LAMPORTS_PER_SOL - fee,
495500
);
496501
}
497502
});
@@ -509,7 +514,7 @@ describe('Connection', function () {
509514
account: {
510515
data: ['', 'base64'],
511516
executable: false,
512-
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
517+
lamports: LAMPORTS_PER_SOL - fee,
513518
owner: programId.publicKey.toBase58(),
514519
rentEpoch: 20,
515520
space: 0,
@@ -520,8 +525,7 @@ describe('Connection', function () {
520525
account: {
521526
data: ['', 'base64'],
522527
executable: false,
523-
lamports:
524-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
528+
lamports: 0.5 * LAMPORTS_PER_SOL - fee,
525529
owner: programId.publicKey.toBase58(),
526530
rentEpoch: 20,
527531
space: 0,
@@ -538,13 +542,11 @@ describe('Connection', function () {
538542
expect(programAccounts).to.have.length(2);
539543
programAccounts.forEach(function (keyedAccount) {
540544
if (keyedAccount.pubkey.equals(account0.publicKey)) {
541-
expect(keyedAccount.account.lamports).to.eq(
542-
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
543-
);
545+
expect(keyedAccount.account.lamports).to.eq(LAMPORTS_PER_SOL - fee);
544546
} else {
545547
expect(keyedAccount.pubkey).to.eql(account1.publicKey);
546548
expect(keyedAccount.account.lamports).to.eq(
547-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
549+
0.5 * LAMPORTS_PER_SOL - fee,
548550
);
549551
}
550552
});
@@ -570,7 +572,7 @@ describe('Connection', function () {
570572
account: {
571573
data: ['', 'base64'],
572574
executable: false,
573-
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
575+
lamports: LAMPORTS_PER_SOL - fee,
574576
owner: programId.publicKey.toBase58(),
575577
rentEpoch: 20,
576578
space: 0,
@@ -581,8 +583,7 @@ describe('Connection', function () {
581583
account: {
582584
data: ['', 'base64'],
583585
executable: false,
584-
lamports:
585-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
586+
lamports: 0.5 * LAMPORTS_PER_SOL - fee,
586587
owner: programId.publicKey.toBase58(),
587588
rentEpoch: 20,
588589
space: 0,
@@ -652,7 +653,7 @@ describe('Connection', function () {
652653
account: {
653654
data: ['', 'base64'],
654655
executable: false,
655-
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
656+
lamports: LAMPORTS_PER_SOL - fee,
656657
owner: programId.publicKey.toBase58(),
657658
rentEpoch: 20,
658659
space: 0,
@@ -663,8 +664,7 @@ describe('Connection', function () {
663664
account: {
664665
data: ['', 'base64'],
665666
executable: false,
666-
lamports:
667-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
667+
lamports: 0.5 * LAMPORTS_PER_SOL - fee,
668668
owner: programId.publicKey.toBase58(),
669669
rentEpoch: 20,
670670
space: 0,
@@ -684,14 +684,10 @@ describe('Connection', function () {
684684

685685
programAccounts.forEach(function (element) {
686686
if (element.pubkey.equals(account0.publicKey)) {
687-
expect(element.account.lamports).to.eq(
688-
LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
689-
);
687+
expect(element.account.lamports).to.eq(LAMPORTS_PER_SOL - fee);
690688
} else {
691689
expect(element.pubkey).to.eql(account1.publicKey);
692-
expect(element.account.lamports).to.eq(
693-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
694-
);
690+
expect(element.account.lamports).to.eq(0.5 * LAMPORTS_PER_SOL - fee);
695691
}
696692
});
697693
}
@@ -746,7 +742,7 @@ describe('Connection', function () {
746742
account: {
747743
data: ['', 'base64'],
748744
executable: false,
749-
lamports: LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
745+
lamports: LAMPORTS_PER_SOL - fee,
750746
owner: programId.publicKey.toBase58(),
751747
rentEpoch: 20,
752748
space: 0,
@@ -757,8 +753,7 @@ describe('Connection', function () {
757753
account: {
758754
data: ['', 'base64'],
759755
executable: false,
760-
lamports:
761-
0.5 * LAMPORTS_PER_SOL - feeCalculator.lamportsPerSignature,
756+
lamports: 0.5 * LAMPORTS_PER_SOL - fee,
762757
owner: programId.publicKey.toBase58(),
763758
rentEpoch: 20,
764759
space: 0,
@@ -4620,14 +4615,42 @@ describe('Connection', function () {
46204615
});
46214616

46224617
it('get recent blockhash', async () => {
4618+
const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];
4619+
for (const commitment of commitments) {
4620+
const {blockhash} = await helpers.recentBlockhash({
4621+
connection,
4622+
commitment,
4623+
});
4624+
expect(bs58.decode(blockhash)).to.have.length(32);
4625+
}
4626+
});
4627+
4628+
it('get recent blockhash can stringify', async () => {
4629+
const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];
4630+
for (const commitment of commitments) {
4631+
const response = await helpers.recentBlockhash({
4632+
connection,
4633+
commitment,
4634+
});
4635+
expect(JSON.stringify(response)).to.match(
4636+
/{"blockhash":"\w+","feeCalculator":{}}/,
4637+
);
4638+
}
4639+
});
4640+
4641+
it('get recent blockhash LPS field throws', async () => {
46234642
const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];
46244643
for (const commitment of commitments) {
46254644
const {blockhash, feeCalculator} = await helpers.recentBlockhash({
46264645
connection,
46274646
commitment,
46284647
});
46294648
expect(bs58.decode(blockhash)).to.have.length(32);
4630-
expect(feeCalculator.lamportsPerSignature).to.be.at.least(0);
4649+
// Accessing the blockhash field works fine.
4650+
// When attempting to access `lamportsPerSignature`, throws.
4651+
expect(() => feeCalculator.lamportsPerSignature).to.throw(
4652+
'The capability to fetch `lamportsPerSignature` using the `getRecentBlockhash` API is no longer offered by the network. Use the `getFeeForMessage` API to obtain the fee for a given message.',
4653+
);
46314654
}
46324655
});
46334656

test/mocks/rpc-http.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import BN from 'bn.js';
33
import * as mockttp from 'mockttp';
44

55
import {mockRpcMessage} from './rpc-websocket';
6-
import {Connection, PublicKey, Transaction, Signer} from '../../src';
6+
import {
7+
Connection,
8+
PublicKey,
9+
Transaction,
10+
Signer,
11+
VersionedMessage,
12+
} from '../../src';
713
import invariant from '../../src/utils/assert';
814
import type {Commitment, HttpHeaders, RpcParams} from '../../src/connection';
915

@@ -146,6 +152,30 @@ const latestBlockhash = async ({
146152
return await connection.getLatestBlockhash(commitment);
147153
};
148154

155+
const getFeeForMessage = async ({
156+
connection,
157+
commitment,
158+
message,
159+
}: {
160+
connection: Connection;
161+
commitment?: Commitment;
162+
message: VersionedMessage;
163+
}) => {
164+
const params: Array<Object> = [];
165+
if (commitment) {
166+
params.push({commitment});
167+
}
168+
169+
await mockRpcResponse({
170+
method: 'getFeeForMessage',
171+
params,
172+
value: 42,
173+
withContext: true,
174+
});
175+
176+
return await connection.getFeeForMessage(message, commitment);
177+
};
178+
149179
const recentBlockhash = async ({
150180
connection,
151181
commitment,
@@ -160,13 +190,11 @@ const recentBlockhash = async ({
160190
}
161191

162192
await mockRpcResponse({
163-
method: 'getRecentBlockhash',
193+
method: 'getLatestBlockhash',
164194
params,
165195
value: {
166196
blockhash,
167-
feeCalculator: {
168-
lamportsPerSignature: 42,
169-
},
197+
lastValidBlockHeight: 100,
170198
},
171199
withContext: true,
172200
});
@@ -267,7 +295,8 @@ const airdrop = async ({
267295

268296
export const helpers = {
269297
airdrop,
298+
getFeeForMessage,
299+
latestBlockhash,
270300
processTransaction,
271301
recentBlockhash,
272-
latestBlockhash,
273302
};

0 commit comments

Comments
 (0)