@solana/web3.js 1.56.2 → 1.59.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.
@@ -1,6 +1,6 @@
1
1
  import {Buffer} from 'buffer';
2
2
  import * as BufferLayout from '@solana/buffer-layout';
3
- import sha3 from 'js-sha3';
3
+ import {keccak_256} from '@noble/hashes/sha3';
4
4
 
5
5
  import {PublicKey} from '../publickey';
6
6
  import {TransactionInstruction} from '../transaction';
@@ -98,9 +98,9 @@ export class Secp256k1Program {
98
98
  );
99
99
 
100
100
  try {
101
- return Buffer.from(
102
- sha3.keccak_256.update(toBuffer(publicKey)).digest(),
103
- ).slice(-ETHEREUM_ADDRESS_BYTES);
101
+ return Buffer.from(keccak_256(toBuffer(publicKey))).slice(
102
+ -ETHEREUM_ADDRESS_BYTES,
103
+ );
104
104
  } catch (error) {
105
105
  throw new Error(`Error constructing Ethereum address: ${error}`);
106
106
  }
@@ -211,9 +211,7 @@ export class Secp256k1Program {
211
211
  privateKey,
212
212
  false /* isCompressed */,
213
213
  ).slice(1); // throw away leading byte
214
- const messageHash = Buffer.from(
215
- sha3.keccak_256.update(toBuffer(message)).digest(),
216
- );
214
+ const messageHash = Buffer.from(keccak_256(toBuffer(message)));
217
215
  const [signature, recoveryId] = ecdsaSign(messageHash, privateKey);
218
216
 
219
217
  return this.createInstructionWithPublicKey({
@@ -65,6 +65,18 @@ export type AuthorizeVoteParams = {
65
65
  voteAuthorizationType: VoteAuthorizationType;
66
66
  };
67
67
 
68
+ /**
69
+ * AuthorizeWithSeed instruction params
70
+ */
71
+ export type AuthorizeVoteWithSeedParams = {
72
+ currentAuthorityDerivedKeyBasePubkey: PublicKey;
73
+ currentAuthorityDerivedKeyOwnerPubkey: PublicKey;
74
+ currentAuthorityDerivedKeySeed: string;
75
+ newAuthorizedPubkey: PublicKey;
76
+ voteAuthorizationType: VoteAuthorizationType;
77
+ votePubkey: PublicKey;
78
+ };
79
+
68
80
  /**
69
81
  * Withdraw from vote account transaction params
70
82
  */
@@ -160,6 +172,41 @@ export class VoteInstruction {
160
172
  };
161
173
  }
162
174
 
175
+ /**
176
+ * Decode an authorize instruction and retrieve the instruction params.
177
+ */
178
+ static decodeAuthorizeWithSeed(
179
+ instruction: TransactionInstruction,
180
+ ): AuthorizeVoteWithSeedParams {
181
+ this.checkProgramId(instruction.programId);
182
+ this.checkKeyLength(instruction.keys, 3);
183
+
184
+ const {
185
+ voteAuthorizeWithSeedArgs: {
186
+ currentAuthorityDerivedKeyOwnerPubkey,
187
+ currentAuthorityDerivedKeySeed,
188
+ newAuthorized,
189
+ voteAuthorizationType,
190
+ },
191
+ } = decodeData(
192
+ VOTE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed,
193
+ instruction.data,
194
+ );
195
+
196
+ return {
197
+ currentAuthorityDerivedKeyBasePubkey: instruction.keys[2].pubkey,
198
+ currentAuthorityDerivedKeyOwnerPubkey: new PublicKey(
199
+ currentAuthorityDerivedKeyOwnerPubkey,
200
+ ),
201
+ currentAuthorityDerivedKeySeed: currentAuthorityDerivedKeySeed,
202
+ newAuthorizedPubkey: new PublicKey(newAuthorized),
203
+ voteAuthorizationType: {
204
+ index: voteAuthorizationType,
205
+ },
206
+ votePubkey: instruction.keys[0].pubkey,
207
+ };
208
+ }
209
+
163
210
  /**
164
211
  * Decode a withdraw instruction and retrieve the instruction params.
165
212
  */
@@ -211,13 +258,23 @@ export type VoteInstructionType =
211
258
  // It would be preferable for this type to be `keyof VoteInstructionInputData`
212
259
  // but Typedoc does not transpile `keyof` expressions.
213
260
  // See https://github.com/TypeStrong/typedoc/issues/1894
214
- 'Authorize' | 'InitializeAccount' | 'Withdraw';
215
-
261
+ 'Authorize' | 'AuthorizeWithSeed' | 'InitializeAccount' | 'Withdraw';
262
+
263
+ /** @internal */
264
+ export type VoteAuthorizeWithSeedArgs = Readonly<{
265
+ currentAuthorityDerivedKeyOwnerPubkey: Uint8Array;
266
+ currentAuthorityDerivedKeySeed: string;
267
+ newAuthorized: Uint8Array;
268
+ voteAuthorizationType: number;
269
+ }>;
216
270
  type VoteInstructionInputData = {
217
271
  Authorize: IInstructionInputData & {
218
272
  newAuthorized: Uint8Array;
219
273
  voteAuthorizationType: number;
220
274
  };
275
+ AuthorizeWithSeed: IInstructionInputData & {
276
+ voteAuthorizeWithSeedArgs: VoteAuthorizeWithSeedArgs;
277
+ };
221
278
  InitializeAccount: IInstructionInputData & {
222
279
  voteInit: Readonly<{
223
280
  authorizedVoter: Uint8Array;
@@ -258,6 +315,13 @@ const VOTE_INSTRUCTION_LAYOUTS = Object.freeze<{
258
315
  BufferLayout.ns64('lamports'),
259
316
  ]),
260
317
  },
318
+ AuthorizeWithSeed: {
319
+ index: 10,
320
+ layout: BufferLayout.struct<VoteInstructionInputData['AuthorizeWithSeed']>([
321
+ BufferLayout.u32('instruction'),
322
+ Layout.voteAuthorizeWithSeedArgs(),
323
+ ]),
324
+ },
261
325
  });
262
326
 
263
327
  /**
@@ -390,6 +454,49 @@ export class VoteProgram {
390
454
  });
391
455
  }
392
456
 
457
+ /**
458
+ * Generate a transaction that authorizes a new Voter or Withdrawer on the Vote account
459
+ * where the current Voter or Withdrawer authority is a derived key.
460
+ */
461
+ static authorizeWithSeed(params: AuthorizeVoteWithSeedParams): Transaction {
462
+ const {
463
+ currentAuthorityDerivedKeyBasePubkey,
464
+ currentAuthorityDerivedKeyOwnerPubkey,
465
+ currentAuthorityDerivedKeySeed,
466
+ newAuthorizedPubkey,
467
+ voteAuthorizationType,
468
+ votePubkey,
469
+ } = params;
470
+
471
+ const type = VOTE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed;
472
+ const data = encodeData(type, {
473
+ voteAuthorizeWithSeedArgs: {
474
+ currentAuthorityDerivedKeyOwnerPubkey: toBuffer(
475
+ currentAuthorityDerivedKeyOwnerPubkey.toBuffer(),
476
+ ),
477
+ currentAuthorityDerivedKeySeed: currentAuthorityDerivedKeySeed,
478
+ newAuthorized: toBuffer(newAuthorizedPubkey.toBuffer()),
479
+ voteAuthorizationType: voteAuthorizationType.index,
480
+ },
481
+ });
482
+
483
+ const keys = [
484
+ {pubkey: votePubkey, isSigner: false, isWritable: true},
485
+ {pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
486
+ {
487
+ pubkey: currentAuthorityDerivedKeyBasePubkey,
488
+ isSigner: true,
489
+ isWritable: false,
490
+ },
491
+ ];
492
+
493
+ return new Transaction().add({
494
+ keys,
495
+ programId: this.programId,
496
+ data,
497
+ });
498
+ }
499
+
393
500
  /**
394
501
  * Generate a transaction to withdraw from a Vote account.
395
502
  */
package/src/publickey.ts CHANGED
@@ -40,6 +40,9 @@ function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
40
40
  return (value as PublicKeyData)._bn !== undefined;
41
41
  }
42
42
 
43
+ // local counter used by PublicKey.unique()
44
+ let uniquePublicKeyCounter = 1;
45
+
43
46
  /**
44
47
  * A public key
45
48
  */
@@ -73,6 +76,15 @@ export class PublicKey extends Struct {
73
76
  }
74
77
  }
75
78
 
79
+ /**
80
+ * Returns a unique PublicKey for tests and benchmarks using acounter
81
+ */
82
+ static unique(): PublicKey {
83
+ const key = new PublicKey(uniquePublicKeyCounter);
84
+ uniquePublicKeyCounter += 1;
85
+ return new PublicKey(key.toBuffer());
86
+ }
87
+
76
88
  /**
77
89
  * Default public key value. (All zeros)
78
90
  */
@@ -1,4 +1,5 @@
1
1
  export * from './constants';
2
2
  export * from './expiry-custom-errors';
3
3
  export * from './legacy';
4
+ export * from './message';
4
5
  export * from './versioned';
@@ -0,0 +1,147 @@
1
+ import {
2
+ AccountKeysFromLookups,
3
+ MessageAccountKeys,
4
+ } from '../message/account-keys';
5
+ import assert from '../utils/assert';
6
+ import {toBuffer} from '../utils/to-buffer';
7
+ import {Blockhash} from '../blockhash';
8
+ import {Message, MessageV0, VersionedMessage} from '../message';
9
+ import {AddressLookupTableAccount} from '../programs';
10
+ import {AccountMeta, TransactionInstruction} from './legacy';
11
+
12
+ export type TransactionMessageArgs = {
13
+ accountKeys: MessageAccountKeys;
14
+ instructions: Array<TransactionInstruction>;
15
+ recentBlockhash: Blockhash;
16
+ };
17
+
18
+ export type DecompileArgs =
19
+ | {
20
+ accountKeysFromLookups: AccountKeysFromLookups;
21
+ }
22
+ | {
23
+ addressLookupTableAccounts: AddressLookupTableAccount[];
24
+ };
25
+
26
+ export class TransactionMessage {
27
+ accountKeys: MessageAccountKeys;
28
+ instructions: Array<TransactionInstruction>;
29
+ recentBlockhash: Blockhash;
30
+
31
+ constructor(args: TransactionMessageArgs) {
32
+ this.accountKeys = args.accountKeys;
33
+ this.instructions = args.instructions;
34
+ this.recentBlockhash = args.recentBlockhash;
35
+ }
36
+
37
+ static decompile(
38
+ message: VersionedMessage,
39
+ args?: DecompileArgs,
40
+ ): TransactionMessage {
41
+ const {header, compiledInstructions, recentBlockhash} = message;
42
+
43
+ const {
44
+ numRequiredSignatures,
45
+ numReadonlySignedAccounts,
46
+ numReadonlyUnsignedAccounts,
47
+ } = header;
48
+
49
+ const numWritableSignedAccounts =
50
+ numRequiredSignatures - numReadonlySignedAccounts;
51
+ assert(numWritableSignedAccounts > 0, 'Message header is invalid');
52
+
53
+ const numWritableUnsignedAccounts =
54
+ message.staticAccountKeys.length - numReadonlyUnsignedAccounts;
55
+ assert(numWritableUnsignedAccounts >= 0, 'Message header is invalid');
56
+
57
+ const accountKeys = message.getAccountKeys(args);
58
+ const instructions: TransactionInstruction[] = [];
59
+ for (const compiledIx of compiledInstructions) {
60
+ const keys: AccountMeta[] = [];
61
+
62
+ for (const keyIndex of compiledIx.accountKeyIndexes) {
63
+ const pubkey = accountKeys.get(keyIndex);
64
+ if (pubkey === undefined) {
65
+ throw new Error(
66
+ `Failed to find key for account key index ${keyIndex}`,
67
+ );
68
+ }
69
+
70
+ const isSigner = keyIndex < numRequiredSignatures;
71
+
72
+ let isWritable;
73
+ if (isSigner) {
74
+ isWritable = keyIndex < numWritableSignedAccounts;
75
+ } else if (keyIndex < accountKeys.staticAccountKeys.length) {
76
+ isWritable =
77
+ keyIndex - numRequiredSignatures < numWritableUnsignedAccounts;
78
+ } else {
79
+ isWritable =
80
+ keyIndex - accountKeys.staticAccountKeys.length <
81
+ // accountKeysFromLookups cannot be undefined because we already found a pubkey for this index above
82
+ accountKeys.accountKeysFromLookups!.writable.length;
83
+ }
84
+
85
+ keys.push({
86
+ pubkey,
87
+ isSigner: keyIndex < header.numRequiredSignatures,
88
+ isWritable,
89
+ });
90
+ }
91
+
92
+ const programId = accountKeys.get(compiledIx.programIdIndex);
93
+ if (programId === undefined) {
94
+ throw new Error(
95
+ `Failed to find program id for program id index ${compiledIx.programIdIndex}`,
96
+ );
97
+ }
98
+
99
+ instructions.push(
100
+ new TransactionInstruction({
101
+ programId,
102
+ data: toBuffer(compiledIx.data),
103
+ keys,
104
+ }),
105
+ );
106
+ }
107
+
108
+ return new TransactionMessage({
109
+ accountKeys,
110
+ instructions,
111
+ recentBlockhash,
112
+ });
113
+ }
114
+
115
+ compileToLegacyMessage(): Message {
116
+ const payerKey = this.accountKeys.get(0);
117
+ if (payerKey === undefined) {
118
+ throw new Error(
119
+ 'Failed to compile message because no account keys were found',
120
+ );
121
+ }
122
+
123
+ return Message.compile({
124
+ payerKey,
125
+ recentBlockhash: this.recentBlockhash,
126
+ instructions: this.instructions,
127
+ });
128
+ }
129
+
130
+ compileToV0Message(
131
+ addressLookupTableAccounts?: AddressLookupTableAccount[],
132
+ ): MessageV0 {
133
+ const payerKey = this.accountKeys.get(0);
134
+ if (payerKey === undefined) {
135
+ throw new Error(
136
+ 'Failed to compile message because no account keys were found',
137
+ );
138
+ }
139
+
140
+ return MessageV0.compile({
141
+ payerKey,
142
+ recentBlockhash: this.recentBlockhash,
143
+ instructions: this.instructions,
144
+ addressLookupTableAccounts,
145
+ });
146
+ }
147
+ }