@exodus/solana-lib 1.0.2 → 1.1.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,16 +0,0 @@
1
- // @flow
2
- import { PublicKey } from './publickey'
3
-
4
- export const SYSVAR_CLOCK_PUBKEY = new PublicKey('SysvarC1ock11111111111111111111111111111111')
5
-
6
- export const SYSVAR_RECENT_BLOCKHASHES_PUBKEY = new PublicKey(
7
- 'SysvarRecentB1ockHashes11111111111111111111'
8
- )
9
-
10
- export const SYSVAR_RENT_PUBKEY = new PublicKey('SysvarRent111111111111111111111111111111111')
11
-
12
- export const SYSVAR_REWARDS_PUBKEY = new PublicKey('SysvarRewards111111111111111111111111111111')
13
-
14
- export const SYSVAR_STAKE_HISTORY_PUBKEY = new PublicKey(
15
- 'SysvarStakeHistory1111111111111111111111111'
16
- )
@@ -1,593 +0,0 @@
1
- // @flow
2
- // https://github.com/solana-labs/solana-web3.js/blob/master/src/transaction.js
3
-
4
- import invariant from 'assert'
5
- import nacl from 'tweetnacl'
6
- import bs58 from 'bs58'
7
-
8
- import type { Blockhash } from './blockhash'
9
- import type { CompiledInstruction } from './message'
10
- import { Message } from './message'
11
- import { PublicKey } from './publickey'
12
- import { Account } from './account'
13
- import * as shortvec from './shortvec-encoding'
14
-
15
- /**
16
- * @typedef {string} TransactionSignature
17
- */
18
- export type TransactionSignature = string
19
-
20
- /**
21
- * Default (empty) signature
22
- *
23
- * Signatures are 64 bytes in length
24
- */
25
- const DEFAULT_SIGNATURE = Buffer.alloc(64).fill(0)
26
-
27
- /**
28
- * Maximum over-the-wire size of a Transaction
29
- *
30
- * 1280 is IPv6 minimum MTU
31
- * 40 bytes is the size of the IPv6 header
32
- * 8 bytes is the size of the fragment header
33
- */
34
- export const PACKET_DATA_SIZE = 1280 - 40 - 8
35
-
36
- const SIGNATURE_LENGTH = 64
37
-
38
- /**
39
- * Account metadata used to define instructions
40
- *
41
- * @typedef {Object} AccountMeta
42
- * @property {PublicKey} pubkey An account's public key
43
- * @property {boolean} isSigner True if an instruction requires a transaction signature matching `pubkey`
44
- * @property {boolean} isWritable True if the `pubkey` can be loaded as a read-write account.
45
- */
46
- export type AccountMeta = {
47
- pubkey: PublicKey,
48
- isSigner: boolean,
49
- isWritable: boolean,
50
- }
51
-
52
- /**
53
- * List of TransactionInstruction object fields that may be initialized at construction
54
- *
55
- * @typedef {Object} TransactionInstructionCtorFields
56
- * @property {?Array<PublicKey>} keys
57
- * @property {?PublicKey} programId
58
- * @property {?Buffer} data
59
- */
60
- export type TransactionInstructionCtorFields = {|
61
- keys?: Array<AccountMeta>,
62
- programId?: PublicKey,
63
- data?: Buffer,
64
- |}
65
-
66
- /**
67
- * Configuration object for Transaction.serialize()
68
- *
69
- * @typedef {Object} SerializeConfig
70
- * @property {boolean|undefined} requireAllSignatures Require all transaction signatures be present (default: true)
71
- * @property {boolean|undefined} verifySignatures Verify provided signatures (default: true)
72
- */
73
- export type SerializeConfig = {
74
- requireAllSignatures?: boolean,
75
- verifySignatures?: boolean,
76
- }
77
-
78
- /**
79
- * Transaction Instruction class
80
- */
81
- export class TransactionInstruction {
82
- /**
83
- * Public keys to include in this transaction
84
- * Boolean represents whether this pubkey needs to sign the transaction
85
- */
86
- keys: Array<AccountMeta> = []
87
-
88
- /**
89
- * Program Id to execute
90
- */
91
- programId: PublicKey
92
-
93
- /**
94
- * Program input
95
- */
96
- data: Buffer = Buffer.alloc(0)
97
-
98
- constructor(opts?: TransactionInstructionCtorFields) {
99
- opts && Object.assign(this, opts)
100
- }
101
- }
102
-
103
- /**
104
- * @private
105
- */
106
- type SignaturePubkeyPair = {|
107
- signature: Buffer | null,
108
- publicKey: PublicKey,
109
- |}
110
-
111
- /**
112
- * NonceInformation to be used to build a Transaction.
113
- *
114
- * @typedef {Object} NonceInformation
115
- * @property {Blockhash} nonce The current Nonce blockhash
116
- * @property {TransactionInstruction} nonceInstruction AdvanceNonceAccount Instruction
117
- */
118
- type NonceInformation = {|
119
- nonce: Blockhash,
120
- nonceInstruction: TransactionInstruction,
121
- |}
122
-
123
- /**
124
- * List of Transaction object fields that may be initialized at construction
125
- *
126
- * @typedef {Object} TransactionCtorFields
127
- * @property {?Blockhash} recentBlockhash A recent blockhash
128
- * @property {?Array<SignaturePubkeyPair>} signatures One or more signatures
129
- *
130
- */
131
- type TransactionCtorFields = {|
132
- recentBlockhash?: Blockhash | null,
133
- nonceInfo?: NonceInformation | null,
134
- signatures?: Array<SignaturePubkeyPair>,
135
- |}
136
-
137
- /**
138
- * Transaction class
139
- */
140
- export class Transaction {
141
- /**
142
- * Signatures for the transaction. Typically created by invoking the
143
- * `sign()` method
144
- */
145
- signatures: Array<SignaturePubkeyPair> = []
146
-
147
- /**
148
- * The first (payer) Transaction signature
149
- */
150
- get signature(): Buffer | null {
151
- if (this.signatures.length > 0) {
152
- return this.signatures[0].signature
153
- }
154
- return null
155
- }
156
- /**
157
- * The transaction fee payer (first signer)
158
- */
159
- get feePayer(): PublicKey | null {
160
- if (this.signatures.length > 0) {
161
- return this.signatures[0].publicKey
162
- }
163
- return null
164
- }
165
- /**
166
- * The instructions to atomically execute
167
- */
168
- instructions: Array<TransactionInstruction> = []
169
-
170
- /**
171
- * A recent transaction id. Must be populated by the caller
172
- */
173
- recentBlockhash: Blockhash | null
174
-
175
- /**
176
- * Optional Nonce information. If populated, transaction will use a durable
177
- * Nonce hash instead of a recentBlockhash. Must be populated by the caller
178
- */
179
- nonceInfo: NonceInformation | null
180
-
181
- /**
182
- * Construct an empty Transaction
183
- */
184
- constructor(opts?: TransactionCtorFields) {
185
- opts && Object.assign(this, opts)
186
- }
187
- /**
188
- * Add one or more instructions to this Transaction
189
- */
190
- add(
191
- ...items: Array<Transaction | TransactionInstruction | TransactionInstructionCtorFields>
192
- ): Transaction {
193
- if (items.length === 0) {
194
- throw new Error('No instructions')
195
- }
196
-
197
- items.forEach((item: any) => {
198
- if ('instructions' in item) {
199
- this.instructions = this.instructions.concat(item.instructions)
200
- } else if ('data' in item && 'programId' in item && 'keys' in item) {
201
- this.instructions.push(item)
202
- } else {
203
- this.instructions.push(new TransactionInstruction(item))
204
- }
205
- })
206
- return this
207
- }
208
- /**
209
- * Compile transaction data
210
- */
211
- compileMessage(): Message {
212
- const { nonceInfo } = this
213
- if (nonceInfo && this.instructions[0] !== nonceInfo.nonceInstruction) {
214
- this.recentBlockhash = nonceInfo.nonce
215
- this.instructions.unshift(nonceInfo.nonceInstruction)
216
- }
217
- const { recentBlockhash } = this
218
- if (!recentBlockhash) {
219
- throw new Error('Transaction recentBlockhash required')
220
- }
221
-
222
- if (this.instructions.length < 1) {
223
- throw new Error('No instructions provided')
224
- }
225
-
226
- if (this.feePayer === null) {
227
- throw new Error('Transaction feePayer required')
228
- }
229
-
230
- const programIds: string[] = []
231
- const accountMetas: AccountMeta[] = []
232
- this.instructions.forEach((instruction) => {
233
- instruction.keys.forEach((accountMeta) => {
234
- accountMetas.push({ ...accountMeta })
235
- })
236
-
237
- const programId = instruction.programId.toString()
238
- if (!programIds.includes(programId)) {
239
- programIds.push(programId)
240
- }
241
- })
242
- // Append programID account metas
243
- programIds.forEach((programId) => {
244
- accountMetas.push({
245
- pubkey: new PublicKey(programId),
246
- isSigner: false,
247
- isWritable: false,
248
- })
249
- })
250
-
251
- // Sort. Prioritizing first by signer, then by writable
252
- accountMetas.sort(function(x, y) {
253
- const checkSigner = x.isSigner === y.isSigner ? 0 : x.isSigner ? -1 : 1
254
- const checkWritable = x.isWritable === y.isWritable ? 0 : x.isWritable ? -1 : 1
255
- return checkSigner || checkWritable
256
- })
257
-
258
- // Cull duplicate account metas
259
- const uniqueMetas: AccountMeta[] = []
260
- accountMetas.forEach((accountMeta) => {
261
- const pubkeyString = accountMeta.pubkey.toString()
262
- const uniqueIndex = uniqueMetas.findIndex((x) => {
263
- return x.pubkey.toString() === pubkeyString
264
- })
265
- if (uniqueIndex > -1) {
266
- uniqueMetas[uniqueIndex].isWritable =
267
- uniqueMetas[uniqueIndex].isWritable || accountMeta.isWritable
268
- } else {
269
- uniqueMetas.push(accountMeta)
270
- }
271
- })
272
- // Move payer to the front and disallow unknown signers
273
- this.signatures.forEach((signature, signatureIndex) => {
274
- const isPayer = signatureIndex === 0
275
- const uniqueIndex = uniqueMetas.findIndex((x) => {
276
- return x.pubkey.equals(signature.publicKey)
277
- })
278
- if (uniqueIndex > -1) {
279
- if (isPayer) {
280
- const [payerMeta] = uniqueMetas.splice(uniqueIndex, 1)
281
- payerMeta.isSigner = true
282
- payerMeta.isWritable = true
283
- uniqueMetas.unshift(payerMeta)
284
- } else {
285
- uniqueMetas[uniqueIndex].isSigner = true
286
- }
287
- } else if (isPayer) {
288
- uniqueMetas.unshift({
289
- pubkey: signature.publicKey,
290
- isSigner: true,
291
- isWritable: true,
292
- })
293
- } else {
294
- throw new Error(`unknown signer: ${signature.publicKey.toString()}`)
295
- }
296
- })
297
-
298
- let numRequiredSignatures = 0
299
- let numReadonlySignedAccounts = 0
300
- let numReadonlyUnsignedAccounts = 0
301
- // Split out signing from non-signing keys and count header values
302
- const signedKeys: string[] = []
303
- const unsignedKeys: string[] = []
304
- uniqueMetas.forEach(({ pubkey, isSigner, isWritable }) => {
305
- if (isSigner) {
306
- signedKeys.push(pubkey.toString())
307
- numRequiredSignatures += 1
308
- if (!isWritable) {
309
- numReadonlySignedAccounts += 1
310
- }
311
- } else {
312
- unsignedKeys.push(pubkey.toString())
313
- if (!isWritable) {
314
- numReadonlyUnsignedAccounts += 1
315
- }
316
- }
317
- })
318
-
319
- if (numRequiredSignatures !== this.signatures.length) {
320
- throw new Error('missing signer(s)')
321
- }
322
-
323
- const accountKeys = signedKeys.concat(unsignedKeys)
324
- const instructions: CompiledInstruction[] = this.instructions.map((instruction) => {
325
- const { data, programId } = instruction
326
- return {
327
- programIdIndex: accountKeys.indexOf(programId.toString()),
328
- accounts: instruction.keys.map((keyObj) => accountKeys.indexOf(keyObj.pubkey.toString())),
329
- data: bs58.encode(data),
330
- }
331
- })
332
-
333
- instructions.forEach((instruction) => {
334
- invariant(instruction.programIdIndex >= 0)
335
- instruction.accounts.forEach((keyIndex) => invariant(keyIndex >= 0))
336
- })
337
-
338
- return new Message({
339
- header: {
340
- numRequiredSignatures,
341
- numReadonlySignedAccounts,
342
- numReadonlyUnsignedAccounts,
343
- },
344
- accountKeys,
345
- recentBlockhash,
346
- instructions,
347
- })
348
- }
349
- /**
350
- * Get a buffer of the Transaction data that need to be covered by signatures
351
- */
352
- serializeMessage(): Buffer {
353
- return this.compileMessage().serialize()
354
- }
355
- /**
356
- * Specify the public keys which will be used to sign the Transaction.
357
- * The first signer will be used as the transaction fee payer account.
358
- *
359
- * Signatures can be added with either `partialSign` or `addSignature`
360
- */
361
- setSigners(...signers: Array<PublicKey>) {
362
- if (signers.length === 0) {
363
- throw new Error('No signers')
364
- }
365
-
366
- const seen = new Set()
367
- this.signatures = signers
368
- .filter((publicKey) => {
369
- const key = publicKey.toString()
370
- if (seen.has(key)) {
371
- return false
372
- } else {
373
- seen.add(key)
374
- return true
375
- }
376
- })
377
- .map((publicKey) => ({ signature: null, publicKey }))
378
- }
379
- /**
380
- * Sign the Transaction with the specified accounts. Multiple signatures may
381
- * be applied to a Transaction. The first signature is considered "primary"
382
- * and is used when testing for Transaction confirmation. The first signer
383
- * will be used as the transaction fee payer account.
384
- *
385
- * Transaction fields should not be modified after the first call to `sign`,
386
- * as doing so may invalidate the signature and cause the Transaction to be
387
- * rejected.
388
- *
389
- * The Transaction must be assigned a valid `recentBlockhash` before invoking this method
390
- */
391
- sign(...signers: Array<Account>) {
392
- if (signers.length === 0) {
393
- throw new Error('No signers')
394
- }
395
-
396
- const seen = new Set()
397
- this.signatures = signers
398
- .filter((signer) => {
399
- const key = signer.publicKey.toString()
400
- if (seen.has(key)) {
401
- return false
402
- } else {
403
- seen.add(key)
404
- return true
405
- }
406
- })
407
- .map((signer) => ({
408
- signature: null,
409
- publicKey: signer.publicKey,
410
- }))
411
-
412
- this.partialSign(...signers)
413
- }
414
- /**
415
- * Partially sign a transaction with the specified accounts. All accounts must
416
- * correspond to a public key that was previously provided to `setSigners`.
417
- *
418
- * All the caveats from the `sign` method apply to `partialSign`
419
- */
420
- partialSign(...signers: Array<Account>) {
421
- if (signers.length === 0) {
422
- throw new Error('No signers')
423
- }
424
-
425
- const message = this.compileMessage()
426
- this.signatures.sort(function(x, y) {
427
- const xIndex = message.findSignerIndex(x.publicKey)
428
- const yIndex = message.findSignerIndex(y.publicKey)
429
- return xIndex < yIndex ? -1 : 1
430
- })
431
-
432
- const signData = message.serialize()
433
- signers.forEach((signer) => {
434
- const signature = nacl.sign.detached(signData, signer.secretKey)
435
- this.addSignature(signer.publicKey, signature)
436
- })
437
- }
438
- /**
439
- * Add an externally created signature to a transaction. The public key
440
- * must correspond to a public key that was previously provided to `setSigners`.
441
- */
442
- addSignature(pubkey: PublicKey, signature: Buffer) {
443
- invariant(signature.length === 64)
444
-
445
- const index = this.signatures.findIndex((sigpair) => pubkey.equals(sigpair.publicKey))
446
- if (index < 0) {
447
- throw new Error(`unknown signer: ${pubkey.toString()}`)
448
- }
449
-
450
- this.signatures[index].signature = Buffer.from(signature)
451
- }
452
- /**
453
- * Verify signatures of a complete, signed Transaction
454
- */
455
- verifySignatures(): boolean {
456
- return this._verifySignatures(this.serializeMessage(), true)
457
- }
458
- /**
459
- * @private
460
- */
461
- _verifySignatures(signData: Buffer, requireAllSignatures: boolean): boolean {
462
- for (const { signature, publicKey } of this.signatures) {
463
- if (signature === null) {
464
- if (requireAllSignatures) {
465
- return false
466
- }
467
- } else {
468
- if (!nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())) {
469
- return false
470
- }
471
- }
472
- }
473
- return true
474
- }
475
- /**
476
- * Serialize the Transaction in the wire format.
477
- */
478
- serialize(config?: SerializeConfig): Buffer {
479
- const { requireAllSignatures, verifySignatures } = Object.assign(
480
- { requireAllSignatures: true, verifySignatures: true },
481
- config
482
- )
483
-
484
- const signData = this.serializeMessage()
485
- if (verifySignatures && !this._verifySignatures(signData, requireAllSignatures)) {
486
- throw new Error('Signature verification failed')
487
- }
488
-
489
- return this._serialize(signData)
490
- }
491
- /**
492
- * @private
493
- */
494
- _serialize(signData: Buffer): Buffer {
495
- const { signatures } = this
496
- const signatureCount = []
497
- shortvec.encodeLength(signatureCount, signatures.length)
498
- const transactionLength = signatureCount.length + signatures.length * 64 + signData.length
499
- const wireTransaction = Buffer.alloc(transactionLength)
500
- invariant(signatures.length < 256)
501
- Buffer.from(signatureCount).copy(wireTransaction, 0)
502
- signatures.forEach(({ signature }, index) => {
503
- if (signature !== null) {
504
- invariant(signature.length === 64, `signature has invalid length`)
505
- Buffer.from(signature).copy(wireTransaction, signatureCount.length + index * 64)
506
- }
507
- })
508
- signData.copy(wireTransaction, signatureCount.length + signatures.length * 64)
509
- invariant(
510
- wireTransaction.length <= PACKET_DATA_SIZE,
511
- `Transaction too large: ${wireTransaction.length} > ${PACKET_DATA_SIZE}`
512
- )
513
- return wireTransaction
514
- }
515
- /**
516
- * Deprecated method
517
- * @private
518
- */
519
- get keys(): Array<PublicKey> {
520
- invariant(this.instructions.length === 1)
521
- return this.instructions[0].keys.map((keyObj) => keyObj.pubkey)
522
- }
523
- /**
524
- * Deprecated method
525
- * @private
526
- */
527
- get programId(): PublicKey {
528
- invariant(this.instructions.length === 1)
529
- return this.instructions[0].programId
530
- }
531
- /**
532
- * Deprecated method
533
- * @private
534
- */
535
- get data(): Buffer {
536
- invariant(this.instructions.length === 1)
537
- return this.instructions[0].data
538
- }
539
- /**
540
- * Parse a wire transaction into a Transaction object.
541
- */
542
- static from(buffer: Buffer | Uint8Array | Array<number>): Transaction {
543
- // Slice up wire data
544
- let byteArray = [...buffer]
545
-
546
- const signatureCount = shortvec.decodeLength(byteArray)
547
- let signatures = []
548
- for (let i = 0; i < signatureCount; i++) {
549
- const signature = byteArray.slice(0, SIGNATURE_LENGTH)
550
- byteArray = byteArray.slice(SIGNATURE_LENGTH)
551
- signatures.push(bs58.encode(Buffer.from(signature)))
552
- }
553
-
554
- return Transaction.populate(Message.from(byteArray), signatures)
555
- }
556
- /**
557
- * Populate Transaction object from message and signatures
558
- */
559
- static populate(message: Message, signatures: Array<string>): Transaction {
560
- const transaction = new Transaction()
561
- transaction.recentBlockhash = message.recentBlockhash
562
- signatures.forEach((signature, index) => {
563
- const sigPubkeyPair = {
564
- signature: signature === bs58.encode(DEFAULT_SIGNATURE) ? null : bs58.decode(signature),
565
- publicKey: message.accountKeys[index],
566
- }
567
- transaction.signatures.push(sigPubkeyPair)
568
- })
569
-
570
- message.instructions.forEach((instruction) => {
571
- const keys = instruction.accounts.map((account) => {
572
- const pubkey = message.accountKeys[account]
573
- return {
574
- pubkey,
575
- isSigner: transaction.signatures.some(
576
- (keyObj) => keyObj.publicKey.toString() === pubkey.toString()
577
- ),
578
- isWritable: message.isAccountWritable(account),
579
- }
580
- })
581
-
582
- transaction.instructions.push(
583
- new TransactionInstruction({
584
- keys,
585
- programId: message.accountKeys[instruction.programIdIndex],
586
- data: bs58.decode(instruction.data),
587
- })
588
- )
589
- })
590
-
591
- return transaction
592
- }
593
- }
@@ -1,9 +0,0 @@
1
- export const toBuffer = (arr: Buffer | Uint8Array | Array<number>): Buffer => {
2
- if (arr instanceof Buffer) {
3
- return arr
4
- } else if (arr instanceof Uint8Array) {
5
- return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength)
6
- } else {
7
- return Buffer.from(arr)
8
- }
9
- }