@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.
- package/package.json +3 -2
- package/src/transaction.js +6 -6
- package/src/helpers/account.js +0 -38
- package/src/helpers/blockhash.js +0 -6
- package/src/helpers/fee-calculator.js +0 -17
- package/src/helpers/instruction.js +0 -46
- package/src/helpers/layout.js +0 -80
- package/src/helpers/message.js +0 -216
- package/src/helpers/nonce-account.js +0 -46
- package/src/helpers/publickey.js +0 -65
- package/src/helpers/shortvec-encoding.js +0 -30
- package/src/helpers/system-program.js +0 -782
- package/src/helpers/sysvar.js +0 -16
- package/src/helpers/transaction.js +0 -593
- package/src/helpers/util/to-buffer.js +0 -9
package/src/helpers/sysvar.js
DELETED
|
@@ -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
|
-
}
|