@solana/web3.js 1.31.0 → 1.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/sysvar.ts CHANGED
@@ -4,6 +4,14 @@ export const SYSVAR_CLOCK_PUBKEY = new PublicKey(
4
4
  'SysvarC1ock11111111111111111111111111111111',
5
5
  );
6
6
 
7
+ export const SYSVAR_EPOCH_SCHEDULE_PUBKEY = new PublicKey(
8
+ 'SysvarEpochSchedu1e111111111111111111111111',
9
+ );
10
+
11
+ export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
12
+ 'Sysvar1nstructions1111111111111111111111111',
13
+ );
14
+
7
15
  export const SYSVAR_RECENT_BLOCKHASHES_PUBKEY = new PublicKey(
8
16
  'SysvarRecentB1ockHashes11111111111111111111',
9
17
  );
@@ -16,10 +24,14 @@ export const SYSVAR_REWARDS_PUBKEY = new PublicKey(
16
24
  'SysvarRewards111111111111111111111111111111',
17
25
  );
18
26
 
19
- export const SYSVAR_STAKE_HISTORY_PUBKEY = new PublicKey(
20
- 'SysvarStakeHistory1111111111111111111111111',
27
+ export const SYSVAR_SLOT_HASHES_PUBKEY = new PublicKey(
28
+ 'SysvarS1otHashes111111111111111111111111111',
21
29
  );
22
30
 
23
- export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
24
- 'Sysvar1nstructions1111111111111111111111111',
31
+ export const SYSVAR_SLOT_HISTORY_PUBKEY = new PublicKey(
32
+ 'SysvarS1otHistory11111111111111111111111111',
33
+ );
34
+
35
+ export const SYSVAR_STAKE_HISTORY_PUBKEY = new PublicKey(
36
+ 'SysvarStakeHistory1111111111111111111111111',
25
37
  );
@@ -259,9 +259,12 @@ export class Transaction {
259
259
 
260
260
  // Sort. Prioritizing first by signer, then by writable
261
261
  accountMetas.sort(function (x, y) {
262
+ const pubkeySorting = x.pubkey
263
+ .toBase58()
264
+ .localeCompare(y.pubkey.toBase58());
262
265
  const checkSigner = x.isSigner === y.isSigner ? 0 : x.isSigner ? -1 : 1;
263
266
  const checkWritable =
264
- x.isWritable === y.isWritable ? 0 : x.isWritable ? -1 : 1;
267
+ x.isWritable === y.isWritable ? pubkeySorting : x.isWritable ? -1 : 1;
265
268
  return checkSigner || checkWritable;
266
269
  });
267
270
 
@@ -2,12 +2,12 @@ const endpoint = {
2
2
  http: {
3
3
  devnet: 'http://api.devnet.solana.com',
4
4
  testnet: 'http://api.testnet.solana.com',
5
- 'mainnet-beta': 'http://api.mainnet-beta.solana.com',
5
+ 'mainnet-beta': 'http://api.mainnet-beta.solana.com/',
6
6
  },
7
7
  https: {
8
8
  devnet: 'https://api.devnet.solana.com',
9
9
  testnet: 'https://api.testnet.solana.com',
10
- 'mainnet-beta': 'https://api.mainnet-beta.solana.com',
10
+ 'mainnet-beta': 'https://api.mainnet-beta.solana.com/',
11
11
  },
12
12
  };
13
13
 
@@ -24,6 +24,7 @@ export async function sendAndConfirmTransaction(
24
24
  const sendOptions = options && {
25
25
  skipPreflight: options.skipPreflight,
26
26
  preflightCommitment: options.preflightCommitment || options.commitment,
27
+ maxRetries: options.maxRetries,
27
28
  };
28
29
 
29
30
  const signature = await connection.sendTransaction(
@@ -23,6 +23,22 @@ export type EpochCredits = {
23
23
  prevCredits: number;
24
24
  };
25
25
 
26
+ export type AuthorizedVoter = {
27
+ epoch: number;
28
+ authorizedVoter: PublicKey;
29
+ };
30
+
31
+ export type PriorVoter = {
32
+ authorizedPubkey: PublicKey;
33
+ epochOfLastAuthorizedSwitch: number;
34
+ targetEpoch: number;
35
+ };
36
+
37
+ export type BlockTimestamp = {
38
+ slot: number;
39
+ timetamp: number;
40
+ };
41
+
26
42
  /**
27
43
  * See https://github.com/solana-labs/solana/blob/8a12ed029cfa38d4a45400916c2463fb82bbec8c/programs/vote_api/src/vote_state.rs#L68-L88
28
44
  *
@@ -30,8 +46,7 @@ export type EpochCredits = {
30
46
  */
31
47
  const VoteAccountLayout = BufferLayout.struct([
32
48
  Layout.publicKey('nodePubkey'),
33
- Layout.publicKey('authorizedVoterPubkey'),
34
- Layout.publicKey('authorizedWithdrawerPubkey'),
49
+ Layout.publicKey('authorizedWithdrawer'),
35
50
  BufferLayout.u8('commission'),
36
51
  BufferLayout.nu64(), // votes.length
37
52
  BufferLayout.seq(
@@ -44,9 +59,31 @@ const VoteAccountLayout = BufferLayout.struct([
44
59
  ),
45
60
  BufferLayout.u8('rootSlotValid'),
46
61
  BufferLayout.nu64('rootSlot'),
47
- BufferLayout.nu64('epoch'),
48
- BufferLayout.nu64('credits'),
49
- BufferLayout.nu64('lastEpochCredits'),
62
+ BufferLayout.nu64(), // authorizedVoters.length
63
+ BufferLayout.seq(
64
+ BufferLayout.struct([
65
+ BufferLayout.nu64('epoch'),
66
+ Layout.publicKey('authorizedVoter'),
67
+ ]),
68
+ BufferLayout.offset(BufferLayout.u32(), -8),
69
+ 'authorizedVoters',
70
+ ),
71
+ BufferLayout.struct(
72
+ [
73
+ BufferLayout.seq(
74
+ BufferLayout.struct([
75
+ Layout.publicKey('authorizedPubkey'),
76
+ BufferLayout.nu64('epochOfLastAuthorizedSwitch'),
77
+ BufferLayout.nu64('targetEpoch'),
78
+ ]),
79
+ 32,
80
+ 'buf',
81
+ ),
82
+ BufferLayout.nu64('idx'),
83
+ BufferLayout.u8('isEmpty'),
84
+ ],
85
+ 'priorVoters',
86
+ ),
50
87
  BufferLayout.nu64(), // epochCredits.length
51
88
  BufferLayout.seq(
52
89
  BufferLayout.struct([
@@ -57,19 +94,22 @@ const VoteAccountLayout = BufferLayout.struct([
57
94
  BufferLayout.offset(BufferLayout.u32(), -8),
58
95
  'epochCredits',
59
96
  ),
97
+ BufferLayout.struct(
98
+ [BufferLayout.nu64('slot'), BufferLayout.nu64('timestamp')],
99
+ 'lastTimestamp',
100
+ ),
60
101
  ]);
61
102
 
62
103
  type VoteAccountArgs = {
63
104
  nodePubkey: PublicKey;
64
- authorizedVoterPubkey: PublicKey;
65
- authorizedWithdrawerPubkey: PublicKey;
105
+ authorizedWithdrawer: PublicKey;
66
106
  commission: number;
67
- votes: Array<Lockout>;
68
107
  rootSlot: number | null;
69
- epoch: number;
70
- credits: number;
71
- lastEpochCredits: number;
72
- epochCredits: Array<EpochCredits>;
108
+ votes: Lockout[];
109
+ authorizedVoters: AuthorizedVoter[];
110
+ priorVoters: PriorVoter[];
111
+ epochCredits: EpochCredits[];
112
+ lastTimestamp: BlockTimestamp;
73
113
  };
74
114
 
75
115
  /**
@@ -77,30 +117,28 @@ type VoteAccountArgs = {
77
117
  */
78
118
  export class VoteAccount {
79
119
  nodePubkey: PublicKey;
80
- authorizedVoterPubkey: PublicKey;
81
- authorizedWithdrawerPubkey: PublicKey;
120
+ authorizedWithdrawer: PublicKey;
82
121
  commission: number;
83
- votes: Array<Lockout>;
84
122
  rootSlot: number | null;
85
- epoch: number;
86
- credits: number;
87
- lastEpochCredits: number;
88
- epochCredits: Array<EpochCredits>;
123
+ votes: Lockout[];
124
+ authorizedVoters: AuthorizedVoter[];
125
+ priorVoters: PriorVoter[];
126
+ epochCredits: EpochCredits[];
127
+ lastTimestamp: BlockTimestamp;
89
128
 
90
129
  /**
91
130
  * @internal
92
131
  */
93
132
  constructor(args: VoteAccountArgs) {
94
133
  this.nodePubkey = args.nodePubkey;
95
- this.authorizedVoterPubkey = args.authorizedVoterPubkey;
96
- this.authorizedWithdrawerPubkey = args.authorizedWithdrawerPubkey;
134
+ this.authorizedWithdrawer = args.authorizedWithdrawer;
97
135
  this.commission = args.commission;
98
- this.votes = args.votes;
99
136
  this.rootSlot = args.rootSlot;
100
- this.epoch = args.epoch;
101
- this.credits = args.credits;
102
- this.lastEpochCredits = args.lastEpochCredits;
137
+ this.votes = args.votes;
138
+ this.authorizedVoters = args.authorizedVoters;
139
+ this.priorVoters = args.priorVoters;
103
140
  this.epochCredits = args.epochCredits;
141
+ this.lastTimestamp = args.lastTimestamp;
104
142
  }
105
143
 
106
144
  /**
@@ -112,7 +150,8 @@ export class VoteAccount {
112
150
  static fromAccountData(
113
151
  buffer: Buffer | Uint8Array | Array<number>,
114
152
  ): VoteAccount {
115
- const va = VoteAccountLayout.decode(toBuffer(buffer), 0);
153
+ const versionOffset = 4;
154
+ const va = VoteAccountLayout.decode(toBuffer(buffer), versionOffset);
116
155
 
117
156
  let rootSlot: number | null = va.rootSlot;
118
157
  if (!va.rootSlotValid) {
@@ -121,15 +160,49 @@ export class VoteAccount {
121
160
 
122
161
  return new VoteAccount({
123
162
  nodePubkey: new PublicKey(va.nodePubkey),
124
- authorizedVoterPubkey: new PublicKey(va.authorizedVoterPubkey),
125
- authorizedWithdrawerPubkey: new PublicKey(va.authorizedWithdrawerPubkey),
163
+ authorizedWithdrawer: new PublicKey(va.authorizedWithdrawer),
126
164
  commission: va.commission,
127
165
  votes: va.votes,
128
166
  rootSlot,
129
- epoch: va.epoch,
130
- credits: va.credits,
131
- lastEpochCredits: va.lastEpochCredits,
167
+ authorizedVoters: va.authorizedVoters.map(parseAuthorizedVoter),
168
+ priorVoters: getPriorVoters(va.priorVoters),
132
169
  epochCredits: va.epochCredits,
170
+ lastTimestamp: va.lastTimestamp,
133
171
  });
134
172
  }
135
173
  }
174
+
175
+ function parseAuthorizedVoter({epoch, authorizedVoter}: AuthorizedVoter) {
176
+ return {
177
+ epoch,
178
+ authorizedVoter: new PublicKey(authorizedVoter),
179
+ };
180
+ }
181
+
182
+ function parsePriorVoters({
183
+ authorizedPubkey,
184
+ epochOfLastAuthorizedSwitch,
185
+ targetEpoch,
186
+ }: PriorVoter) {
187
+ return {
188
+ authorizedPubkey: new PublicKey(authorizedPubkey),
189
+ epochOfLastAuthorizedSwitch,
190
+ targetEpoch,
191
+ };
192
+ }
193
+
194
+ function getPriorVoters({
195
+ buf,
196
+ idx,
197
+ isEmpty,
198
+ }: {
199
+ buf: PriorVoter[];
200
+ idx: number;
201
+ isEmpty: boolean;
202
+ }): PriorVoter[] {
203
+ if (isEmpty) {
204
+ return [];
205
+ }
206
+
207
+ return [...buf.slice(idx + 1).map(parsePriorVoters), ...buf.slice(0, idx)];
208
+ }
@@ -0,0 +1,290 @@
1
+ import * as BufferLayout from '@solana/buffer-layout';
2
+
3
+ import {encodeData, decodeData, InstructionType} from './instruction';
4
+ import * as Layout from './layout';
5
+ import {PublicKey} from './publickey';
6
+ import {SystemProgram} from './system-program';
7
+ import {SYSVAR_CLOCK_PUBKEY, SYSVAR_RENT_PUBKEY} from './sysvar';
8
+ import {Transaction, TransactionInstruction} from './transaction';
9
+ import {toBuffer} from './util/to-buffer';
10
+
11
+ /**
12
+ * Vote account info
13
+ */
14
+ export class VoteInit {
15
+ nodePubkey: PublicKey;
16
+ authorizedVoter: PublicKey;
17
+ authorizedWithdrawer: PublicKey;
18
+ commission: number; /** [0, 100] */
19
+
20
+ constructor(
21
+ nodePubkey: PublicKey,
22
+ authorizedVoter: PublicKey,
23
+ authorizedWithdrawer: PublicKey,
24
+ commission: number,
25
+ ) {
26
+ this.nodePubkey = nodePubkey;
27
+ this.authorizedVoter = authorizedVoter;
28
+ this.authorizedWithdrawer = authorizedWithdrawer;
29
+ this.commission = commission;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Create vote account transaction params
35
+ */
36
+ export type CreateVoteAccountParams = {
37
+ fromPubkey: PublicKey;
38
+ votePubkey: PublicKey;
39
+ voteInit: VoteInit;
40
+ lamports: number;
41
+ };
42
+
43
+ /**
44
+ * InitializeAccount instruction params
45
+ */
46
+ export type InitializeAccountParams = {
47
+ votePubkey: PublicKey;
48
+ nodePubkey: PublicKey;
49
+ voteInit: VoteInit;
50
+ };
51
+
52
+ /**
53
+ * Withdraw from vote account transaction params
54
+ */
55
+ export type WithdrawFromVoteAccountParams = {
56
+ votePubkey: PublicKey;
57
+ authorizedWithdrawerPubkey: PublicKey;
58
+ lamports: number;
59
+ toPubkey: PublicKey;
60
+ };
61
+
62
+ /**
63
+ * Vote Instruction class
64
+ */
65
+ export class VoteInstruction {
66
+ /**
67
+ * @internal
68
+ */
69
+ constructor() {}
70
+
71
+ /**
72
+ * Decode a vote instruction and retrieve the instruction type.
73
+ */
74
+ static decodeInstructionType(
75
+ instruction: TransactionInstruction,
76
+ ): VoteInstructionType {
77
+ this.checkProgramId(instruction.programId);
78
+
79
+ const instructionTypeLayout = BufferLayout.u32('instruction');
80
+ const typeIndex = instructionTypeLayout.decode(instruction.data);
81
+
82
+ let type: VoteInstructionType | undefined;
83
+ for (const [ixType, layout] of Object.entries(VOTE_INSTRUCTION_LAYOUTS)) {
84
+ if (layout.index == typeIndex) {
85
+ type = ixType as VoteInstructionType;
86
+ break;
87
+ }
88
+ }
89
+
90
+ if (!type) {
91
+ throw new Error('Instruction type incorrect; not a VoteInstruction');
92
+ }
93
+
94
+ return type;
95
+ }
96
+
97
+ /**
98
+ * Decode an initialize vote instruction and retrieve the instruction params.
99
+ */
100
+ static decodeInitializeAccount(
101
+ instruction: TransactionInstruction,
102
+ ): InitializeAccountParams {
103
+ this.checkProgramId(instruction.programId);
104
+ this.checkKeyLength(instruction.keys, 4);
105
+
106
+ const {voteInit} = decodeData(
107
+ VOTE_INSTRUCTION_LAYOUTS.InitializeAccount,
108
+ instruction.data,
109
+ );
110
+
111
+ return {
112
+ votePubkey: instruction.keys[0].pubkey,
113
+ nodePubkey: instruction.keys[3].pubkey,
114
+ voteInit: new VoteInit(
115
+ new PublicKey(voteInit.nodePubkey),
116
+ new PublicKey(voteInit.authorizedVoter),
117
+ new PublicKey(voteInit.authorizedWithdrawer),
118
+ voteInit.commission,
119
+ ),
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Decode a withdraw instruction and retrieve the instruction params.
125
+ */
126
+ static decodeWithdraw(
127
+ instruction: TransactionInstruction,
128
+ ): WithdrawFromVoteAccountParams {
129
+ this.checkProgramId(instruction.programId);
130
+ this.checkKeyLength(instruction.keys, 3);
131
+
132
+ const {lamports} = decodeData(
133
+ VOTE_INSTRUCTION_LAYOUTS.Withdraw,
134
+ instruction.data,
135
+ );
136
+
137
+ return {
138
+ votePubkey: instruction.keys[0].pubkey,
139
+ authorizedWithdrawerPubkey: instruction.keys[2].pubkey,
140
+ lamports,
141
+ toPubkey: instruction.keys[1].pubkey,
142
+ };
143
+ }
144
+
145
+ /**
146
+ * @internal
147
+ */
148
+ static checkProgramId(programId: PublicKey) {
149
+ if (!programId.equals(VoteProgram.programId)) {
150
+ throw new Error('invalid instruction; programId is not VoteProgram');
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @internal
156
+ */
157
+ static checkKeyLength(keys: Array<any>, expectedLength: number) {
158
+ if (keys.length < expectedLength) {
159
+ throw new Error(
160
+ `invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`,
161
+ );
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * An enumeration of valid VoteInstructionType's
168
+ */
169
+ export type VoteInstructionType = 'InitializeAccount' | 'Withdraw';
170
+
171
+ const VOTE_INSTRUCTION_LAYOUTS: {
172
+ [type in VoteInstructionType]: InstructionType;
173
+ } = Object.freeze({
174
+ InitializeAccount: {
175
+ index: 0,
176
+ layout: BufferLayout.struct([
177
+ BufferLayout.u32('instruction'),
178
+ Layout.voteInit(),
179
+ ]),
180
+ },
181
+ Withdraw: {
182
+ index: 3,
183
+ layout: BufferLayout.struct([
184
+ BufferLayout.u32('instruction'),
185
+ BufferLayout.ns64('lamports'),
186
+ ]),
187
+ },
188
+ });
189
+
190
+ /**
191
+ * Factory class for transactions to interact with the Vote program
192
+ */
193
+ export class VoteProgram {
194
+ /**
195
+ * @internal
196
+ */
197
+ constructor() {}
198
+
199
+ /**
200
+ * Public key that identifies the Vote program
201
+ */
202
+ static programId: PublicKey = new PublicKey(
203
+ 'Vote111111111111111111111111111111111111111',
204
+ );
205
+
206
+ /**
207
+ * Max space of a Vote account
208
+ *
209
+ * This is generated from the solana-vote-program VoteState struct as
210
+ * `VoteState::size_of()`:
211
+ * https://docs.rs/solana-vote-program/1.9.5/solana_vote_program/vote_state/struct.VoteState.html#method.size_of
212
+ */
213
+ static space: number = 3731;
214
+
215
+ /**
216
+ * Generate an Initialize instruction.
217
+ */
218
+ static initializeAccount(
219
+ params: InitializeAccountParams,
220
+ ): TransactionInstruction {
221
+ const {votePubkey, nodePubkey, voteInit} = params;
222
+ const type = VOTE_INSTRUCTION_LAYOUTS.InitializeAccount;
223
+ const data = encodeData(type, {
224
+ voteInit: {
225
+ nodePubkey: toBuffer(voteInit.nodePubkey.toBuffer()),
226
+ authorizedVoter: toBuffer(voteInit.authorizedVoter.toBuffer()),
227
+ authorizedWithdrawer: toBuffer(
228
+ voteInit.authorizedWithdrawer.toBuffer(),
229
+ ),
230
+ commission: voteInit.commission,
231
+ },
232
+ });
233
+ const instructionData = {
234
+ keys: [
235
+ {pubkey: votePubkey, isSigner: false, isWritable: true},
236
+ {pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
237
+ {pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
238
+ {pubkey: nodePubkey, isSigner: true, isWritable: false},
239
+ ],
240
+ programId: this.programId,
241
+ data,
242
+ };
243
+ return new TransactionInstruction(instructionData);
244
+ }
245
+
246
+ /**
247
+ * Generate a transaction that creates a new Vote account.
248
+ */
249
+ static createAccount(params: CreateVoteAccountParams): Transaction {
250
+ const transaction = new Transaction();
251
+ transaction.add(
252
+ SystemProgram.createAccount({
253
+ fromPubkey: params.fromPubkey,
254
+ newAccountPubkey: params.votePubkey,
255
+ lamports: params.lamports,
256
+ space: this.space,
257
+ programId: this.programId,
258
+ }),
259
+ );
260
+
261
+ return transaction.add(
262
+ this.initializeAccount({
263
+ votePubkey: params.votePubkey,
264
+ nodePubkey: params.voteInit.nodePubkey,
265
+ voteInit: params.voteInit,
266
+ }),
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Generate a transaction to withdraw from a Vote account.
272
+ */
273
+ static withdraw(params: WithdrawFromVoteAccountParams): Transaction {
274
+ const {votePubkey, authorizedWithdrawerPubkey, lamports, toPubkey} = params;
275
+ const type = VOTE_INSTRUCTION_LAYOUTS.Withdraw;
276
+ const data = encodeData(type, {lamports});
277
+
278
+ const keys = [
279
+ {pubkey: votePubkey, isSigner: false, isWritable: true},
280
+ {pubkey: toPubkey, isSigner: false, isWritable: true},
281
+ {pubkey: authorizedWithdrawerPubkey, isSigner: true, isWritable: false},
282
+ ];
283
+
284
+ return new Transaction().add({
285
+ keys,
286
+ programId: this.programId,
287
+ data,
288
+ });
289
+ }
290
+ }