@exodus/solana-lib 1.2.1 → 1.2.4

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