@solana/web3.js 0.0.0-next
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/LICENSE +20 -0
- package/README.md +158 -0
- package/lib/index.browser.cjs.js +10062 -0
- package/lib/index.browser.cjs.js.map +1 -0
- package/lib/index.browser.esm.js +9976 -0
- package/lib/index.browser.esm.js.map +1 -0
- package/lib/index.cjs.js +9568 -0
- package/lib/index.cjs.js.map +1 -0
- package/lib/index.d.ts +3311 -0
- package/lib/index.esm.js +9479 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.iife.js +30136 -0
- package/lib/index.iife.js.map +1 -0
- package/lib/index.iife.min.js +40 -0
- package/lib/index.iife.min.js.map +1 -0
- package/package.json +140 -0
- package/src/account.ts +46 -0
- package/src/agent-manager.ts +44 -0
- package/src/blockhash.ts +4 -0
- package/src/bpf-loader-deprecated.ts +5 -0
- package/src/bpf-loader.ts +45 -0
- package/src/connection.ts +4935 -0
- package/src/ed25519-program.ts +157 -0
- package/src/epoch-schedule.ts +102 -0
- package/src/errors.ts +9 -0
- package/src/fee-calculator.ts +16 -0
- package/src/index.ts +31 -0
- package/src/instruction.ts +58 -0
- package/src/keypair.ts +98 -0
- package/src/layout.ts +147 -0
- package/src/loader.ts +236 -0
- package/src/message.ts +271 -0
- package/src/nonce-account.ts +78 -0
- package/src/publickey.ts +296 -0
- package/src/secp256k1-program.ts +229 -0
- package/src/stake-program.ts +923 -0
- package/src/system-program.ts +1007 -0
- package/src/sysvar.ts +37 -0
- package/src/timing.ts +23 -0
- package/src/transaction.ts +808 -0
- package/src/util/assert.ts +8 -0
- package/src/util/borsh-schema.ts +38 -0
- package/src/util/cluster.ts +31 -0
- package/src/util/promise-timeout.ts +14 -0
- package/src/util/send-and-confirm-raw-transaction.ts +46 -0
- package/src/util/send-and-confirm-transaction.ts +50 -0
- package/src/util/shortvec-encoding.ts +28 -0
- package/src/util/sleep.ts +4 -0
- package/src/util/to-buffer.ts +11 -0
- package/src/util/url.ts +18 -0
- package/src/validator-info.ts +106 -0
- package/src/vote-account.ts +236 -0
- package/src/vote-program.ts +413 -0
package/src/loader.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {Buffer} from 'buffer';
|
|
2
|
+
import * as BufferLayout from '@solana/buffer-layout';
|
|
3
|
+
|
|
4
|
+
import {PublicKey} from './publickey';
|
|
5
|
+
import {Transaction, PACKET_DATA_SIZE} from './transaction';
|
|
6
|
+
import {SYSVAR_RENT_PUBKEY} from './sysvar';
|
|
7
|
+
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
|
8
|
+
import {sleep} from './util/sleep';
|
|
9
|
+
import type {Connection} from './connection';
|
|
10
|
+
import type {Signer} from './keypair';
|
|
11
|
+
import {SystemProgram} from './system-program';
|
|
12
|
+
import {IInstructionInputData} from './instruction';
|
|
13
|
+
|
|
14
|
+
// Keep program chunks under PACKET_DATA_SIZE, leaving enough room for the
|
|
15
|
+
// rest of the Transaction fields
|
|
16
|
+
//
|
|
17
|
+
// TODO: replace 300 with a proper constant for the size of the other
|
|
18
|
+
// Transaction fields
|
|
19
|
+
const CHUNK_SIZE = PACKET_DATA_SIZE - 300;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Program loader interface
|
|
23
|
+
*/
|
|
24
|
+
export class Loader {
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
constructor() {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Amount of program data placed in each load Transaction
|
|
32
|
+
*/
|
|
33
|
+
static chunkSize: number = CHUNK_SIZE;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Minimum number of signatures required to load a program not including
|
|
37
|
+
* retries
|
|
38
|
+
*
|
|
39
|
+
* Can be used to calculate transaction fees
|
|
40
|
+
*/
|
|
41
|
+
static getMinNumSignatures(dataLength: number): number {
|
|
42
|
+
return (
|
|
43
|
+
2 * // Every transaction requires two signatures (payer + program)
|
|
44
|
+
(Math.ceil(dataLength / Loader.chunkSize) +
|
|
45
|
+
1 + // Add one for Create transaction
|
|
46
|
+
1) // Add one for Finalize transaction
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Loads a generic program
|
|
52
|
+
*
|
|
53
|
+
* @param connection The connection to use
|
|
54
|
+
* @param payer System account that pays to load the program
|
|
55
|
+
* @param program Account to load the program into
|
|
56
|
+
* @param programId Public key that identifies the loader
|
|
57
|
+
* @param data Program octets
|
|
58
|
+
* @return true if program was loaded successfully, false if program was already loaded
|
|
59
|
+
*/
|
|
60
|
+
static async load(
|
|
61
|
+
connection: Connection,
|
|
62
|
+
payer: Signer,
|
|
63
|
+
program: Signer,
|
|
64
|
+
programId: PublicKey,
|
|
65
|
+
data: Buffer | Uint8Array | Array<number>,
|
|
66
|
+
): Promise<boolean> {
|
|
67
|
+
{
|
|
68
|
+
const balanceNeeded = await connection.getMinimumBalanceForRentExemption(
|
|
69
|
+
data.length,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Fetch program account info to check if it has already been created
|
|
73
|
+
const programInfo = await connection.getAccountInfo(
|
|
74
|
+
program.publicKey,
|
|
75
|
+
'confirmed',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
let transaction: Transaction | null = null;
|
|
79
|
+
if (programInfo !== null) {
|
|
80
|
+
if (programInfo.executable) {
|
|
81
|
+
console.error('Program load failed, account is already executable');
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (programInfo.data.length !== data.length) {
|
|
86
|
+
transaction = transaction || new Transaction();
|
|
87
|
+
transaction.add(
|
|
88
|
+
SystemProgram.allocate({
|
|
89
|
+
accountPubkey: program.publicKey,
|
|
90
|
+
space: data.length,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!programInfo.owner.equals(programId)) {
|
|
96
|
+
transaction = transaction || new Transaction();
|
|
97
|
+
transaction.add(
|
|
98
|
+
SystemProgram.assign({
|
|
99
|
+
accountPubkey: program.publicKey,
|
|
100
|
+
programId,
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (programInfo.lamports < balanceNeeded) {
|
|
106
|
+
transaction = transaction || new Transaction();
|
|
107
|
+
transaction.add(
|
|
108
|
+
SystemProgram.transfer({
|
|
109
|
+
fromPubkey: payer.publicKey,
|
|
110
|
+
toPubkey: program.publicKey,
|
|
111
|
+
lamports: balanceNeeded - programInfo.lamports,
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
transaction = new Transaction().add(
|
|
117
|
+
SystemProgram.createAccount({
|
|
118
|
+
fromPubkey: payer.publicKey,
|
|
119
|
+
newAccountPubkey: program.publicKey,
|
|
120
|
+
lamports: balanceNeeded > 0 ? balanceNeeded : 1,
|
|
121
|
+
space: data.length,
|
|
122
|
+
programId,
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If the account is already created correctly, skip this step
|
|
128
|
+
// and proceed directly to loading instructions
|
|
129
|
+
if (transaction !== null) {
|
|
130
|
+
await sendAndConfirmTransaction(
|
|
131
|
+
connection,
|
|
132
|
+
transaction,
|
|
133
|
+
[payer, program],
|
|
134
|
+
{
|
|
135
|
+
commitment: 'confirmed',
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const dataLayout = BufferLayout.struct<
|
|
142
|
+
Readonly<{
|
|
143
|
+
bytes: number[];
|
|
144
|
+
bytesLength: number;
|
|
145
|
+
bytesLengthPadding: number;
|
|
146
|
+
instruction: number;
|
|
147
|
+
offset: number;
|
|
148
|
+
}>
|
|
149
|
+
>([
|
|
150
|
+
BufferLayout.u32('instruction'),
|
|
151
|
+
BufferLayout.u32('offset'),
|
|
152
|
+
BufferLayout.u32('bytesLength'),
|
|
153
|
+
BufferLayout.u32('bytesLengthPadding'),
|
|
154
|
+
BufferLayout.seq(
|
|
155
|
+
BufferLayout.u8('byte'),
|
|
156
|
+
BufferLayout.offset(BufferLayout.u32(), -8),
|
|
157
|
+
'bytes',
|
|
158
|
+
),
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const chunkSize = Loader.chunkSize;
|
|
162
|
+
let offset = 0;
|
|
163
|
+
let array = data;
|
|
164
|
+
let transactions = [];
|
|
165
|
+
while (array.length > 0) {
|
|
166
|
+
const bytes = array.slice(0, chunkSize);
|
|
167
|
+
const data = Buffer.alloc(chunkSize + 16);
|
|
168
|
+
dataLayout.encode(
|
|
169
|
+
{
|
|
170
|
+
instruction: 0, // Load instruction
|
|
171
|
+
offset,
|
|
172
|
+
bytes: bytes as number[],
|
|
173
|
+
bytesLength: 0,
|
|
174
|
+
bytesLengthPadding: 0,
|
|
175
|
+
},
|
|
176
|
+
data,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const transaction = new Transaction().add({
|
|
180
|
+
keys: [{pubkey: program.publicKey, isSigner: true, isWritable: true}],
|
|
181
|
+
programId,
|
|
182
|
+
data,
|
|
183
|
+
});
|
|
184
|
+
transactions.push(
|
|
185
|
+
sendAndConfirmTransaction(connection, transaction, [payer, program], {
|
|
186
|
+
commitment: 'confirmed',
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Delay between sends in an attempt to reduce rate limit errors
|
|
191
|
+
if (connection._rpcEndpoint.includes('solana.com')) {
|
|
192
|
+
const REQUESTS_PER_SECOND = 4;
|
|
193
|
+
await sleep(1000 / REQUESTS_PER_SECOND);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
offset += chunkSize;
|
|
197
|
+
array = array.slice(chunkSize);
|
|
198
|
+
}
|
|
199
|
+
await Promise.all(transactions);
|
|
200
|
+
|
|
201
|
+
// Finalize the account loaded with program data for execution
|
|
202
|
+
{
|
|
203
|
+
const dataLayout = BufferLayout.struct<IInstructionInputData>([
|
|
204
|
+
BufferLayout.u32('instruction'),
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
const data = Buffer.alloc(dataLayout.span);
|
|
208
|
+
dataLayout.encode(
|
|
209
|
+
{
|
|
210
|
+
instruction: 1, // Finalize instruction
|
|
211
|
+
},
|
|
212
|
+
data,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const transaction = new Transaction().add({
|
|
216
|
+
keys: [
|
|
217
|
+
{pubkey: program.publicKey, isSigner: true, isWritable: true},
|
|
218
|
+
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
|
|
219
|
+
],
|
|
220
|
+
programId,
|
|
221
|
+
data,
|
|
222
|
+
});
|
|
223
|
+
await sendAndConfirmTransaction(
|
|
224
|
+
connection,
|
|
225
|
+
transaction,
|
|
226
|
+
[payer, program],
|
|
227
|
+
{
|
|
228
|
+
commitment: 'confirmed',
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// success
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
}
|
package/src/message.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import bs58 from 'bs58';
|
|
2
|
+
import {Buffer} from 'buffer';
|
|
3
|
+
import * as BufferLayout from '@solana/buffer-layout';
|
|
4
|
+
|
|
5
|
+
import {PublicKey} from './publickey';
|
|
6
|
+
import type {Blockhash} from './blockhash';
|
|
7
|
+
import * as Layout from './layout';
|
|
8
|
+
import {PACKET_DATA_SIZE} from './transaction';
|
|
9
|
+
import * as shortvec from './util/shortvec-encoding';
|
|
10
|
+
import {toBuffer} from './util/to-buffer';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The message header, identifying signed and read-only account
|
|
14
|
+
*/
|
|
15
|
+
export type MessageHeader = {
|
|
16
|
+
/**
|
|
17
|
+
* The number of signatures required for this message to be considered valid. The
|
|
18
|
+
* signatures must match the first `numRequiredSignatures` of `accountKeys`.
|
|
19
|
+
*/
|
|
20
|
+
numRequiredSignatures: number;
|
|
21
|
+
/** The last `numReadonlySignedAccounts` of the signed keys are read-only accounts */
|
|
22
|
+
numReadonlySignedAccounts: number;
|
|
23
|
+
/** The last `numReadonlySignedAccounts` of the unsigned keys are read-only accounts */
|
|
24
|
+
numReadonlyUnsignedAccounts: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* An instruction to execute by a program
|
|
29
|
+
*
|
|
30
|
+
* @property {number} programIdIndex
|
|
31
|
+
* @property {number[]} accounts
|
|
32
|
+
* @property {string} data
|
|
33
|
+
*/
|
|
34
|
+
export type CompiledInstruction = {
|
|
35
|
+
/** Index into the transaction keys array indicating the program account that executes this instruction */
|
|
36
|
+
programIdIndex: number;
|
|
37
|
+
/** Ordered indices into the transaction keys array indicating which accounts to pass to the program */
|
|
38
|
+
accounts: number[];
|
|
39
|
+
/** The program input data encoded as base 58 */
|
|
40
|
+
data: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Message constructor arguments
|
|
45
|
+
*/
|
|
46
|
+
export type MessageArgs = {
|
|
47
|
+
/** The message header, identifying signed and read-only `accountKeys` */
|
|
48
|
+
header: MessageHeader;
|
|
49
|
+
/** All the account keys used by this transaction */
|
|
50
|
+
accountKeys: string[];
|
|
51
|
+
/** The hash of a recent ledger block */
|
|
52
|
+
recentBlockhash: Blockhash;
|
|
53
|
+
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
|
|
54
|
+
instructions: CompiledInstruction[];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const PUBKEY_LENGTH = 32;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* List of instructions to be processed atomically
|
|
61
|
+
*/
|
|
62
|
+
export class Message {
|
|
63
|
+
header: MessageHeader;
|
|
64
|
+
accountKeys: PublicKey[];
|
|
65
|
+
recentBlockhash: Blockhash;
|
|
66
|
+
instructions: CompiledInstruction[];
|
|
67
|
+
|
|
68
|
+
private indexToProgramIds: Map<number, PublicKey> = new Map<
|
|
69
|
+
number,
|
|
70
|
+
PublicKey
|
|
71
|
+
>();
|
|
72
|
+
|
|
73
|
+
constructor(args: MessageArgs) {
|
|
74
|
+
this.header = args.header;
|
|
75
|
+
this.accountKeys = args.accountKeys.map(account => new PublicKey(account));
|
|
76
|
+
this.recentBlockhash = args.recentBlockhash;
|
|
77
|
+
this.instructions = args.instructions;
|
|
78
|
+
this.instructions.forEach(ix =>
|
|
79
|
+
this.indexToProgramIds.set(
|
|
80
|
+
ix.programIdIndex,
|
|
81
|
+
this.accountKeys[ix.programIdIndex],
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
isAccountSigner(index: number): boolean {
|
|
87
|
+
return index < this.header.numRequiredSignatures;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
isAccountWritable(index: number): boolean {
|
|
91
|
+
return (
|
|
92
|
+
index <
|
|
93
|
+
this.header.numRequiredSignatures -
|
|
94
|
+
this.header.numReadonlySignedAccounts ||
|
|
95
|
+
(index >= this.header.numRequiredSignatures &&
|
|
96
|
+
index <
|
|
97
|
+
this.accountKeys.length - this.header.numReadonlyUnsignedAccounts)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isProgramId(index: number): boolean {
|
|
102
|
+
return this.indexToProgramIds.has(index);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
programIds(): PublicKey[] {
|
|
106
|
+
return [...this.indexToProgramIds.values()];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
nonProgramIds(): PublicKey[] {
|
|
110
|
+
return this.accountKeys.filter((_, index) => !this.isProgramId(index));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
serialize(): Buffer {
|
|
114
|
+
const numKeys = this.accountKeys.length;
|
|
115
|
+
|
|
116
|
+
let keyCount: number[] = [];
|
|
117
|
+
shortvec.encodeLength(keyCount, numKeys);
|
|
118
|
+
|
|
119
|
+
const instructions = this.instructions.map(instruction => {
|
|
120
|
+
const {accounts, programIdIndex} = instruction;
|
|
121
|
+
const data = Array.from(bs58.decode(instruction.data));
|
|
122
|
+
|
|
123
|
+
let keyIndicesCount: number[] = [];
|
|
124
|
+
shortvec.encodeLength(keyIndicesCount, accounts.length);
|
|
125
|
+
|
|
126
|
+
let dataCount: number[] = [];
|
|
127
|
+
shortvec.encodeLength(dataCount, data.length);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
programIdIndex,
|
|
131
|
+
keyIndicesCount: Buffer.from(keyIndicesCount),
|
|
132
|
+
keyIndices: accounts,
|
|
133
|
+
dataLength: Buffer.from(dataCount),
|
|
134
|
+
data,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
let instructionCount: number[] = [];
|
|
139
|
+
shortvec.encodeLength(instructionCount, instructions.length);
|
|
140
|
+
let instructionBuffer = Buffer.alloc(PACKET_DATA_SIZE);
|
|
141
|
+
Buffer.from(instructionCount).copy(instructionBuffer);
|
|
142
|
+
let instructionBufferLength = instructionCount.length;
|
|
143
|
+
|
|
144
|
+
instructions.forEach(instruction => {
|
|
145
|
+
const instructionLayout = BufferLayout.struct<
|
|
146
|
+
Readonly<{
|
|
147
|
+
data: number[];
|
|
148
|
+
dataLength: Uint8Array;
|
|
149
|
+
keyIndices: number[];
|
|
150
|
+
keyIndicesCount: Uint8Array;
|
|
151
|
+
programIdIndex: number;
|
|
152
|
+
}>
|
|
153
|
+
>([
|
|
154
|
+
BufferLayout.u8('programIdIndex'),
|
|
155
|
+
|
|
156
|
+
BufferLayout.blob(
|
|
157
|
+
instruction.keyIndicesCount.length,
|
|
158
|
+
'keyIndicesCount',
|
|
159
|
+
),
|
|
160
|
+
BufferLayout.seq(
|
|
161
|
+
BufferLayout.u8('keyIndex'),
|
|
162
|
+
instruction.keyIndices.length,
|
|
163
|
+
'keyIndices',
|
|
164
|
+
),
|
|
165
|
+
BufferLayout.blob(instruction.dataLength.length, 'dataLength'),
|
|
166
|
+
BufferLayout.seq(
|
|
167
|
+
BufferLayout.u8('userdatum'),
|
|
168
|
+
instruction.data.length,
|
|
169
|
+
'data',
|
|
170
|
+
),
|
|
171
|
+
]);
|
|
172
|
+
const length = instructionLayout.encode(
|
|
173
|
+
instruction,
|
|
174
|
+
instructionBuffer,
|
|
175
|
+
instructionBufferLength,
|
|
176
|
+
);
|
|
177
|
+
instructionBufferLength += length;
|
|
178
|
+
});
|
|
179
|
+
instructionBuffer = instructionBuffer.slice(0, instructionBufferLength);
|
|
180
|
+
|
|
181
|
+
const signDataLayout = BufferLayout.struct<
|
|
182
|
+
Readonly<{
|
|
183
|
+
keyCount: Uint8Array;
|
|
184
|
+
keys: Uint8Array[];
|
|
185
|
+
numReadonlySignedAccounts: Uint8Array;
|
|
186
|
+
numReadonlyUnsignedAccounts: Uint8Array;
|
|
187
|
+
numRequiredSignatures: Uint8Array;
|
|
188
|
+
recentBlockhash: Uint8Array;
|
|
189
|
+
}>
|
|
190
|
+
>([
|
|
191
|
+
BufferLayout.blob(1, 'numRequiredSignatures'),
|
|
192
|
+
BufferLayout.blob(1, 'numReadonlySignedAccounts'),
|
|
193
|
+
BufferLayout.blob(1, 'numReadonlyUnsignedAccounts'),
|
|
194
|
+
BufferLayout.blob(keyCount.length, 'keyCount'),
|
|
195
|
+
BufferLayout.seq(Layout.publicKey('key'), numKeys, 'keys'),
|
|
196
|
+
Layout.publicKey('recentBlockhash'),
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
const transaction = {
|
|
200
|
+
numRequiredSignatures: Buffer.from([this.header.numRequiredSignatures]),
|
|
201
|
+
numReadonlySignedAccounts: Buffer.from([
|
|
202
|
+
this.header.numReadonlySignedAccounts,
|
|
203
|
+
]),
|
|
204
|
+
numReadonlyUnsignedAccounts: Buffer.from([
|
|
205
|
+
this.header.numReadonlyUnsignedAccounts,
|
|
206
|
+
]),
|
|
207
|
+
keyCount: Buffer.from(keyCount),
|
|
208
|
+
keys: this.accountKeys.map(key => toBuffer(key.toBytes())),
|
|
209
|
+
recentBlockhash: bs58.decode(this.recentBlockhash),
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
let signData = Buffer.alloc(2048);
|
|
213
|
+
const length = signDataLayout.encode(transaction, signData);
|
|
214
|
+
instructionBuffer.copy(signData, length);
|
|
215
|
+
return signData.slice(0, length + instructionBuffer.length);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Decode a compiled message into a Message object.
|
|
220
|
+
*/
|
|
221
|
+
static from(buffer: Buffer | Uint8Array | Array<number>): Message {
|
|
222
|
+
// Slice up wire data
|
|
223
|
+
let byteArray = [...buffer];
|
|
224
|
+
|
|
225
|
+
const numRequiredSignatures = byteArray.shift() as number;
|
|
226
|
+
const numReadonlySignedAccounts = byteArray.shift() as number;
|
|
227
|
+
const numReadonlyUnsignedAccounts = byteArray.shift() as number;
|
|
228
|
+
|
|
229
|
+
const accountCount = shortvec.decodeLength(byteArray);
|
|
230
|
+
let accountKeys = [];
|
|
231
|
+
for (let i = 0; i < accountCount; i++) {
|
|
232
|
+
const account = byteArray.slice(0, PUBKEY_LENGTH);
|
|
233
|
+
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
|
234
|
+
accountKeys.push(bs58.encode(Buffer.from(account)));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const recentBlockhash = byteArray.slice(0, PUBKEY_LENGTH);
|
|
238
|
+
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
|
239
|
+
|
|
240
|
+
const instructionCount = shortvec.decodeLength(byteArray);
|
|
241
|
+
let instructions: CompiledInstruction[] = [];
|
|
242
|
+
for (let i = 0; i < instructionCount; i++) {
|
|
243
|
+
const programIdIndex = byteArray.shift() as number;
|
|
244
|
+
const accountCount = shortvec.decodeLength(byteArray);
|
|
245
|
+
const accounts = byteArray.slice(0, accountCount);
|
|
246
|
+
byteArray = byteArray.slice(accountCount);
|
|
247
|
+
const dataLength = shortvec.decodeLength(byteArray);
|
|
248
|
+
const dataSlice = byteArray.slice(0, dataLength);
|
|
249
|
+
const data = bs58.encode(Buffer.from(dataSlice));
|
|
250
|
+
byteArray = byteArray.slice(dataLength);
|
|
251
|
+
instructions.push({
|
|
252
|
+
programIdIndex,
|
|
253
|
+
accounts,
|
|
254
|
+
data,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const messageArgs = {
|
|
259
|
+
header: {
|
|
260
|
+
numRequiredSignatures,
|
|
261
|
+
numReadonlySignedAccounts,
|
|
262
|
+
numReadonlyUnsignedAccounts,
|
|
263
|
+
},
|
|
264
|
+
recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)),
|
|
265
|
+
accountKeys,
|
|
266
|
+
instructions,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return new Message(messageArgs);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as BufferLayout from '@solana/buffer-layout';
|
|
2
|
+
import {Buffer} from 'buffer';
|
|
3
|
+
|
|
4
|
+
import type {Blockhash} from './blockhash';
|
|
5
|
+
import * as Layout from './layout';
|
|
6
|
+
import {PublicKey} from './publickey';
|
|
7
|
+
import type {FeeCalculator} from './fee-calculator';
|
|
8
|
+
import {FeeCalculatorLayout} from './fee-calculator';
|
|
9
|
+
import {toBuffer} from './util/to-buffer';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* See https://github.com/solana-labs/solana/blob/0ea2843ec9cdc517572b8e62c959f41b55cf4453/sdk/src/nonce_state.rs#L29-L32
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
const NonceAccountLayout = BufferLayout.struct<
|
|
17
|
+
Readonly<{
|
|
18
|
+
authorizedPubkey: Uint8Array;
|
|
19
|
+
feeCalculator: Readonly<{
|
|
20
|
+
lamportsPerSignature: number;
|
|
21
|
+
}>;
|
|
22
|
+
nonce: Uint8Array;
|
|
23
|
+
state: number;
|
|
24
|
+
version: number;
|
|
25
|
+
}>
|
|
26
|
+
>([
|
|
27
|
+
BufferLayout.u32('version'),
|
|
28
|
+
BufferLayout.u32('state'),
|
|
29
|
+
Layout.publicKey('authorizedPubkey'),
|
|
30
|
+
Layout.publicKey('nonce'),
|
|
31
|
+
BufferLayout.struct<Readonly<{lamportsPerSignature: number}>>(
|
|
32
|
+
[FeeCalculatorLayout],
|
|
33
|
+
'feeCalculator',
|
|
34
|
+
),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
export const NONCE_ACCOUNT_LENGTH = NonceAccountLayout.span;
|
|
38
|
+
|
|
39
|
+
type NonceAccountArgs = {
|
|
40
|
+
authorizedPubkey: PublicKey;
|
|
41
|
+
nonce: Blockhash;
|
|
42
|
+
feeCalculator: FeeCalculator;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* NonceAccount class
|
|
47
|
+
*/
|
|
48
|
+
export class NonceAccount {
|
|
49
|
+
authorizedPubkey: PublicKey;
|
|
50
|
+
nonce: Blockhash;
|
|
51
|
+
feeCalculator: FeeCalculator;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
constructor(args: NonceAccountArgs) {
|
|
57
|
+
this.authorizedPubkey = args.authorizedPubkey;
|
|
58
|
+
this.nonce = args.nonce;
|
|
59
|
+
this.feeCalculator = args.feeCalculator;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Deserialize NonceAccount from the account data.
|
|
64
|
+
*
|
|
65
|
+
* @param buffer account data
|
|
66
|
+
* @return NonceAccount
|
|
67
|
+
*/
|
|
68
|
+
static fromAccountData(
|
|
69
|
+
buffer: Buffer | Uint8Array | Array<number>,
|
|
70
|
+
): NonceAccount {
|
|
71
|
+
const nonceAccount = NonceAccountLayout.decode(toBuffer(buffer), 0);
|
|
72
|
+
return new NonceAccount({
|
|
73
|
+
authorizedPubkey: new PublicKey(nonceAccount.authorizedPubkey),
|
|
74
|
+
nonce: new PublicKey(nonceAccount.nonce).toString(),
|
|
75
|
+
feeCalculator: nonceAccount.feeCalculator,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|