@rialo/spl-token 0.3.0-alpha.0 → 0.3.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/dist/index.d.mts +1374 -164
- package/dist/index.d.ts +1374 -164
- package/dist/index.js +2235 -682
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2201 -684
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -4
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PublicKey, SYSTEM_PROGRAM_ID, TransactionBuilder, isOnCurve } from '@rialo/ts-cdk';
|
|
1
|
+
import { PublicKey, SYSTEM_PROGRAM_ID, createAccount, TransactionBuilder, isOnCurve } from '@rialo/ts-cdk';
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/builders/mint-builder.ts
|
|
4
4
|
|
|
5
5
|
// src/constants.ts
|
|
6
6
|
var TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
|
@@ -83,157 +83,501 @@ var TokenInstruction = /* @__PURE__ */ ((TokenInstruction2) => {
|
|
|
83
83
|
TokenInstruction2[TokenInstruction2["InitializeAccount3"] = 18] = "InitializeAccount3";
|
|
84
84
|
TokenInstruction2[TokenInstruction2["InitializeMultisig2"] = 19] = "InitializeMultisig2";
|
|
85
85
|
TokenInstruction2[TokenInstruction2["InitializeMint2"] = 20] = "InitializeMint2";
|
|
86
|
+
TokenInstruction2[TokenInstruction2["TransferFeeExtension"] = 26] = "TransferFeeExtension";
|
|
87
|
+
TokenInstruction2[TokenInstruction2["InitializeNonTransferableMint"] = 32] = "InitializeNonTransferableMint";
|
|
88
|
+
TokenInstruction2[TokenInstruction2["InitializePermanentDelegate"] = 35] = "InitializePermanentDelegate";
|
|
89
|
+
TokenInstruction2[TokenInstruction2["TransferHookExtension"] = 36] = "TransferHookExtension";
|
|
90
|
+
TokenInstruction2[TokenInstruction2["MetadataPointerExtension"] = 39] = "MetadataPointerExtension";
|
|
86
91
|
return TokenInstruction2;
|
|
87
92
|
})(TokenInstruction || {});
|
|
88
93
|
var TLV_TYPE_SIZE = 2;
|
|
89
94
|
var TLV_LENGTH_SIZE = 2;
|
|
90
|
-
var MINT_EXTENSIONS_OFFSET =
|
|
95
|
+
var MINT_EXTENSIONS_OFFSET = TOKEN_ACCOUNT_SIZE + 1;
|
|
96
|
+
var TOKEN_ACCOUNT_EXTENSIONS_OFFSET = TOKEN_ACCOUNT_SIZE + 1;
|
|
91
97
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
})(SplTokenErrorCode || {});
|
|
106
|
-
var SplTokenError = class _SplTokenError extends Error {
|
|
107
|
-
code;
|
|
108
|
-
details;
|
|
109
|
-
constructor({ code, message, details }) {
|
|
110
|
-
super(message);
|
|
111
|
-
this.name = "SplTokenError";
|
|
112
|
-
this.code = code;
|
|
113
|
-
this.details = details;
|
|
114
|
-
if (Error.captureStackTrace) {
|
|
115
|
-
Error.captureStackTrace(this, _SplTokenError);
|
|
116
|
-
}
|
|
98
|
+
// node_modules/@noble/hashes/utils.js
|
|
99
|
+
function isBytes(a) {
|
|
100
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
101
|
+
}
|
|
102
|
+
function abytes(value, length, title = "") {
|
|
103
|
+
const bytes = isBytes(value);
|
|
104
|
+
const len = value?.length;
|
|
105
|
+
const needsLen = length !== void 0;
|
|
106
|
+
if (!bytes || needsLen) {
|
|
107
|
+
const prefix = title && `"${title}" `;
|
|
108
|
+
const ofLen = "";
|
|
109
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
110
|
+
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|
117
111
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function aexists(instance, checkFinished = true) {
|
|
115
|
+
if (instance.destroyed)
|
|
116
|
+
throw new Error("Hash instance has been destroyed");
|
|
117
|
+
if (checkFinished && instance.finished)
|
|
118
|
+
throw new Error("Hash#digest() has already been called");
|
|
119
|
+
}
|
|
120
|
+
function aoutput(out, instance) {
|
|
121
|
+
abytes(out, void 0, "digestInto() output");
|
|
122
|
+
const min = instance.outputLen;
|
|
123
|
+
if (out.length < min) {
|
|
124
|
+
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|
128
125
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
*/
|
|
134
|
-
static invalidTokenAccount({ reason }) {
|
|
135
|
-
return new _SplTokenError({
|
|
136
|
-
code: "INVALID_TOKEN_ACCOUNT" /* INVALID_TOKEN_ACCOUNT */,
|
|
137
|
-
message: `Invalid token account: ${reason}`
|
|
138
|
-
});
|
|
126
|
+
}
|
|
127
|
+
function clean(...arrays) {
|
|
128
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
129
|
+
arrays[i].fill(0);
|
|
139
130
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
131
|
+
}
|
|
132
|
+
function createView(arr) {
|
|
133
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
134
|
+
}
|
|
135
|
+
function rotr(word, shift) {
|
|
136
|
+
return word << 32 - shift | word >>> shift;
|
|
137
|
+
}
|
|
138
|
+
function createHasher(hashCons, info = {}) {
|
|
139
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
140
|
+
const tmp = hashCons(void 0);
|
|
141
|
+
hashC.outputLen = tmp.outputLen;
|
|
142
|
+
hashC.blockLen = tmp.blockLen;
|
|
143
|
+
hashC.create = (opts) => hashCons(opts);
|
|
144
|
+
Object.assign(hashC, info);
|
|
145
|
+
return Object.freeze(hashC);
|
|
146
|
+
}
|
|
147
|
+
var oidNist = (suffix) => ({
|
|
148
|
+
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// node_modules/@noble/hashes/_md.js
|
|
152
|
+
function Chi(a, b, c) {
|
|
153
|
+
return a & b ^ ~a & c;
|
|
154
|
+
}
|
|
155
|
+
function Maj(a, b, c) {
|
|
156
|
+
return a & b ^ a & c ^ b & c;
|
|
157
|
+
}
|
|
158
|
+
var HashMD = class {
|
|
159
|
+
blockLen;
|
|
160
|
+
outputLen;
|
|
161
|
+
padOffset;
|
|
162
|
+
isLE;
|
|
163
|
+
// For partial updates less than block size
|
|
164
|
+
buffer;
|
|
165
|
+
view;
|
|
166
|
+
finished = false;
|
|
167
|
+
length = 0;
|
|
168
|
+
pos = 0;
|
|
169
|
+
destroyed = false;
|
|
170
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
171
|
+
this.blockLen = blockLen;
|
|
172
|
+
this.outputLen = outputLen;
|
|
173
|
+
this.padOffset = padOffset;
|
|
174
|
+
this.isLE = isLE;
|
|
175
|
+
this.buffer = new Uint8Array(blockLen);
|
|
176
|
+
this.view = createView(this.buffer);
|
|
151
177
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
update(data) {
|
|
179
|
+
aexists(this);
|
|
180
|
+
abytes(data);
|
|
181
|
+
const { view, buffer, blockLen } = this;
|
|
182
|
+
const len = data.length;
|
|
183
|
+
for (let pos = 0; pos < len; ) {
|
|
184
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
185
|
+
if (take === blockLen) {
|
|
186
|
+
const dataView = createView(data);
|
|
187
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
188
|
+
this.process(dataView, pos);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
192
|
+
this.pos += take;
|
|
193
|
+
pos += take;
|
|
194
|
+
if (this.pos === blockLen) {
|
|
195
|
+
this.process(view, 0);
|
|
196
|
+
this.pos = 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.length += data.length;
|
|
200
|
+
this.roundClean();
|
|
201
|
+
return this;
|
|
163
202
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
digestInto(out) {
|
|
204
|
+
aexists(this);
|
|
205
|
+
aoutput(out, this);
|
|
206
|
+
this.finished = true;
|
|
207
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
208
|
+
let { pos } = this;
|
|
209
|
+
buffer[pos++] = 128;
|
|
210
|
+
clean(this.buffer.subarray(pos));
|
|
211
|
+
if (this.padOffset > blockLen - pos) {
|
|
212
|
+
this.process(view, 0);
|
|
213
|
+
pos = 0;
|
|
214
|
+
}
|
|
215
|
+
for (let i = pos; i < blockLen; i++)
|
|
216
|
+
buffer[i] = 0;
|
|
217
|
+
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
218
|
+
this.process(view, 0);
|
|
219
|
+
const oview = createView(out);
|
|
220
|
+
const len = this.outputLen;
|
|
221
|
+
if (len % 4)
|
|
222
|
+
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|
223
|
+
const outLen = len / 4;
|
|
224
|
+
const state = this.get();
|
|
225
|
+
if (outLen > state.length)
|
|
226
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
227
|
+
for (let i = 0; i < outLen; i++)
|
|
228
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
175
229
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
required,
|
|
183
|
-
available
|
|
184
|
-
}) {
|
|
185
|
-
return new _SplTokenError({
|
|
186
|
-
code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */,
|
|
187
|
-
message: `Insufficient balance: required ${required}, available ${available}`,
|
|
188
|
-
details: { required: required.toString(), available: available.toString() }
|
|
189
|
-
});
|
|
230
|
+
digest() {
|
|
231
|
+
const { buffer, outputLen } = this;
|
|
232
|
+
this.digestInto(buffer);
|
|
233
|
+
const res = buffer.slice(0, outputLen);
|
|
234
|
+
this.destroy();
|
|
235
|
+
return res;
|
|
190
236
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
237
|
+
_cloneInto(to) {
|
|
238
|
+
to ||= new this.constructor();
|
|
239
|
+
to.set(...this.get());
|
|
240
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
241
|
+
to.destroyed = destroyed;
|
|
242
|
+
to.finished = finished;
|
|
243
|
+
to.length = length;
|
|
244
|
+
to.pos = pos;
|
|
245
|
+
if (length % blockLen)
|
|
246
|
+
to.buffer.set(buffer);
|
|
247
|
+
return to;
|
|
201
248
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
*
|
|
205
|
-
* @param options - Error options containing the reason
|
|
206
|
-
*/
|
|
207
|
-
static pdaDerivationFailed({ reason }) {
|
|
208
|
-
return new _SplTokenError({
|
|
209
|
-
code: "PDA_DERIVATION_FAILED" /* PDA_DERIVATION_FAILED */,
|
|
210
|
-
message: `PDA derivation failed: ${reason}`
|
|
211
|
-
});
|
|
249
|
+
clone() {
|
|
250
|
+
return this._cloneInto();
|
|
212
251
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
252
|
+
};
|
|
253
|
+
var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
254
|
+
1779033703,
|
|
255
|
+
3144134277,
|
|
256
|
+
1013904242,
|
|
257
|
+
2773480762,
|
|
258
|
+
1359893119,
|
|
259
|
+
2600822924,
|
|
260
|
+
528734635,
|
|
261
|
+
1541459225
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
// node_modules/@noble/hashes/sha2.js
|
|
265
|
+
var SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
266
|
+
1116352408,
|
|
267
|
+
1899447441,
|
|
268
|
+
3049323471,
|
|
269
|
+
3921009573,
|
|
270
|
+
961987163,
|
|
271
|
+
1508970993,
|
|
272
|
+
2453635748,
|
|
273
|
+
2870763221,
|
|
274
|
+
3624381080,
|
|
275
|
+
310598401,
|
|
276
|
+
607225278,
|
|
277
|
+
1426881987,
|
|
278
|
+
1925078388,
|
|
279
|
+
2162078206,
|
|
280
|
+
2614888103,
|
|
281
|
+
3248222580,
|
|
282
|
+
3835390401,
|
|
283
|
+
4022224774,
|
|
284
|
+
264347078,
|
|
285
|
+
604807628,
|
|
286
|
+
770255983,
|
|
287
|
+
1249150122,
|
|
288
|
+
1555081692,
|
|
289
|
+
1996064986,
|
|
290
|
+
2554220882,
|
|
291
|
+
2821834349,
|
|
292
|
+
2952996808,
|
|
293
|
+
3210313671,
|
|
294
|
+
3336571891,
|
|
295
|
+
3584528711,
|
|
296
|
+
113926993,
|
|
297
|
+
338241895,
|
|
298
|
+
666307205,
|
|
299
|
+
773529912,
|
|
300
|
+
1294757372,
|
|
301
|
+
1396182291,
|
|
302
|
+
1695183700,
|
|
303
|
+
1986661051,
|
|
304
|
+
2177026350,
|
|
305
|
+
2456956037,
|
|
306
|
+
2730485921,
|
|
307
|
+
2820302411,
|
|
308
|
+
3259730800,
|
|
309
|
+
3345764771,
|
|
310
|
+
3516065817,
|
|
311
|
+
3600352804,
|
|
312
|
+
4094571909,
|
|
313
|
+
275423344,
|
|
314
|
+
430227734,
|
|
315
|
+
506948616,
|
|
316
|
+
659060556,
|
|
317
|
+
883997877,
|
|
318
|
+
958139571,
|
|
319
|
+
1322822218,
|
|
320
|
+
1537002063,
|
|
321
|
+
1747873779,
|
|
322
|
+
1955562222,
|
|
323
|
+
2024104815,
|
|
324
|
+
2227730452,
|
|
325
|
+
2361852424,
|
|
326
|
+
2428436474,
|
|
327
|
+
2756734187,
|
|
328
|
+
3204031479,
|
|
329
|
+
3329325298
|
|
330
|
+
]);
|
|
331
|
+
var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
332
|
+
var SHA2_32B = class extends HashMD {
|
|
333
|
+
constructor(outputLen) {
|
|
334
|
+
super(64, outputLen, 8, false);
|
|
223
335
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
336
|
+
get() {
|
|
337
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
338
|
+
return [A, B, C, D, E, F, G, H];
|
|
339
|
+
}
|
|
340
|
+
// prettier-ignore
|
|
341
|
+
set(A, B, C, D, E, F, G, H) {
|
|
342
|
+
this.A = A | 0;
|
|
343
|
+
this.B = B | 0;
|
|
344
|
+
this.C = C | 0;
|
|
345
|
+
this.D = D | 0;
|
|
346
|
+
this.E = E | 0;
|
|
347
|
+
this.F = F | 0;
|
|
348
|
+
this.G = G | 0;
|
|
349
|
+
this.H = H | 0;
|
|
350
|
+
}
|
|
351
|
+
process(view, offset) {
|
|
352
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
353
|
+
SHA256_W[i] = view.getUint32(offset, false);
|
|
354
|
+
for (let i = 16; i < 64; i++) {
|
|
355
|
+
const W15 = SHA256_W[i - 15];
|
|
356
|
+
const W2 = SHA256_W[i - 2];
|
|
357
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
358
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
359
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
360
|
+
}
|
|
361
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
362
|
+
for (let i = 0; i < 64; i++) {
|
|
363
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
364
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
365
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
366
|
+
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
367
|
+
H = G;
|
|
368
|
+
G = F;
|
|
369
|
+
F = E;
|
|
370
|
+
E = D + T1 | 0;
|
|
371
|
+
D = C;
|
|
372
|
+
C = B;
|
|
373
|
+
B = A;
|
|
374
|
+
A = T1 + T2 | 0;
|
|
375
|
+
}
|
|
376
|
+
A = A + this.A | 0;
|
|
377
|
+
B = B + this.B | 0;
|
|
378
|
+
C = C + this.C | 0;
|
|
379
|
+
D = D + this.D | 0;
|
|
380
|
+
E = E + this.E | 0;
|
|
381
|
+
F = F + this.F | 0;
|
|
382
|
+
G = G + this.G | 0;
|
|
383
|
+
H = H + this.H | 0;
|
|
384
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
385
|
+
}
|
|
386
|
+
roundClean() {
|
|
387
|
+
clean(SHA256_W);
|
|
388
|
+
}
|
|
389
|
+
destroy() {
|
|
390
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
391
|
+
clean(this.buffer);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
var _SHA256 = class extends SHA2_32B {
|
|
395
|
+
// We cannot use array here since array allows indexing by variable
|
|
396
|
+
// which means optimizer/compiler cannot use registers.
|
|
397
|
+
A = SHA256_IV[0] | 0;
|
|
398
|
+
B = SHA256_IV[1] | 0;
|
|
399
|
+
C = SHA256_IV[2] | 0;
|
|
400
|
+
D = SHA256_IV[3] | 0;
|
|
401
|
+
E = SHA256_IV[4] | 0;
|
|
402
|
+
F = SHA256_IV[5] | 0;
|
|
403
|
+
G = SHA256_IV[6] | 0;
|
|
404
|
+
H = SHA256_IV[7] | 0;
|
|
405
|
+
constructor() {
|
|
406
|
+
super(32);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var sha256 = /* @__PURE__ */ createHasher(
|
|
410
|
+
() => new _SHA256(),
|
|
411
|
+
/* @__PURE__ */ oidNist(1)
|
|
412
|
+
);
|
|
413
|
+
var TOKEN_METADATA_NAMESPACE = "rialo_s_spl_token_metadata_interface";
|
|
414
|
+
var TOKEN_METADATA_DISCRIMINATOR_LENGTH = 8;
|
|
415
|
+
function metadataDiscriminator(label) {
|
|
416
|
+
const input = new TextEncoder().encode(`${TOKEN_METADATA_NAMESPACE}:${label}`);
|
|
417
|
+
return sha256(input).slice(0, TOKEN_METADATA_DISCRIMINATOR_LENGTH);
|
|
418
|
+
}
|
|
419
|
+
function writeU32LE({
|
|
420
|
+
data,
|
|
421
|
+
offset,
|
|
422
|
+
value
|
|
423
|
+
}) {
|
|
424
|
+
data[offset] = value & 255;
|
|
425
|
+
data[offset + 1] = value >>> 8 & 255;
|
|
426
|
+
data[offset + 2] = value >>> 16 & 255;
|
|
427
|
+
data[offset + 3] = value >>> 24 & 255;
|
|
428
|
+
}
|
|
429
|
+
function encodeLengthPrefixedString(value) {
|
|
430
|
+
const bytes = new TextEncoder().encode(value);
|
|
431
|
+
const buffer = new Uint8Array(4 + bytes.length);
|
|
432
|
+
writeU32LE({ data: buffer, offset: 0, value: bytes.length });
|
|
433
|
+
buffer.set(bytes, 4);
|
|
434
|
+
return buffer;
|
|
435
|
+
}
|
|
436
|
+
function encodeAdditionalMetadata(additionalMetadata) {
|
|
437
|
+
const entries = additionalMetadata.flatMap(([key, value]) => [
|
|
438
|
+
encodeLengthPrefixedString(key),
|
|
439
|
+
encodeLengthPrefixedString(value)
|
|
440
|
+
]);
|
|
441
|
+
const totalLength = 4 + entries.reduce((sum, entry) => sum + entry.length, 0);
|
|
442
|
+
const buffer = new Uint8Array(totalLength);
|
|
443
|
+
writeU32LE({ data: buffer, offset: 0, value: additionalMetadata.length });
|
|
444
|
+
let offset = 4;
|
|
445
|
+
for (const entry of entries) {
|
|
446
|
+
buffer.set(entry, offset);
|
|
447
|
+
offset += entry.length;
|
|
448
|
+
}
|
|
449
|
+
return buffer;
|
|
450
|
+
}
|
|
451
|
+
function getTokenMetadataDataSize({
|
|
452
|
+
name,
|
|
453
|
+
symbol,
|
|
454
|
+
uri,
|
|
455
|
+
additionalMetadata = []
|
|
456
|
+
}) {
|
|
457
|
+
return 32 + 32 + encodeLengthPrefixedString(name).length + encodeLengthPrefixedString(symbol).length + encodeLengthPrefixedString(uri).length + encodeAdditionalMetadata(additionalMetadata).length;
|
|
458
|
+
}
|
|
459
|
+
function initializeTokenMetadataInstruction({
|
|
460
|
+
mint,
|
|
461
|
+
updateAuthority,
|
|
462
|
+
mintAuthority,
|
|
463
|
+
name,
|
|
464
|
+
symbol,
|
|
465
|
+
uri,
|
|
466
|
+
additionalMetadata = [],
|
|
467
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
468
|
+
}) {
|
|
469
|
+
const nameBytes = encodeLengthPrefixedString(name);
|
|
470
|
+
const symbolBytes = encodeLengthPrefixedString(symbol);
|
|
471
|
+
const uriBytes = encodeLengthPrefixedString(uri);
|
|
472
|
+
const hasAdditionalMetadata = additionalMetadata.length > 0;
|
|
473
|
+
const additionalBytes = hasAdditionalMetadata ? encodeAdditionalMetadata(additionalMetadata) : null;
|
|
474
|
+
const dataLength = TOKEN_METADATA_DISCRIMINATOR_LENGTH + nameBytes.length + symbolBytes.length + uriBytes.length + (additionalBytes?.length ?? 0);
|
|
475
|
+
const data = new Uint8Array(dataLength);
|
|
476
|
+
data.set(metadataDiscriminator("initialize_account"), 0);
|
|
477
|
+
let offset = TOKEN_METADATA_DISCRIMINATOR_LENGTH;
|
|
478
|
+
data.set(nameBytes, offset);
|
|
479
|
+
offset += nameBytes.length;
|
|
480
|
+
data.set(symbolBytes, offset);
|
|
481
|
+
offset += symbolBytes.length;
|
|
482
|
+
data.set(uriBytes, offset);
|
|
483
|
+
if (additionalBytes) {
|
|
484
|
+
offset += uriBytes.length;
|
|
485
|
+
data.set(additionalBytes, offset);
|
|
235
486
|
}
|
|
487
|
+
return {
|
|
488
|
+
programId,
|
|
489
|
+
accounts: [
|
|
490
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
491
|
+
{ pubkey: updateAuthority, isSigner: false, isWritable: false },
|
|
492
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
493
|
+
{ pubkey: mintAuthority, isSigner: true, isWritable: false }
|
|
494
|
+
],
|
|
495
|
+
data
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/helpers/size.ts
|
|
500
|
+
var TLV_HEADER_SIZE = TLV_TYPE_SIZE + TLV_LENGTH_SIZE;
|
|
501
|
+
var ACCOUNT_TYPE_SIZE = 1;
|
|
502
|
+
var EXTENSION_SIZES = {
|
|
503
|
+
[1 /* TransferFeeConfig */]: 108,
|
|
504
|
+
[2 /* TransferFeeAmount */]: 8,
|
|
505
|
+
[3 /* MintCloseAuthority */]: 32,
|
|
506
|
+
[4 /* ConfidentialTransferMint */]: 97,
|
|
507
|
+
[5 /* ConfidentialTransferAccount */]: 286,
|
|
508
|
+
[6 /* DefaultAccountState */]: 1,
|
|
509
|
+
[7 /* ImmutableOwner */]: 0,
|
|
510
|
+
[8 /* MemoTransfer */]: 1,
|
|
511
|
+
[9 /* NonTransferable */]: 0,
|
|
512
|
+
[10 /* InterestBearingConfig */]: 52,
|
|
513
|
+
[11 /* CpiGuard */]: 1,
|
|
514
|
+
[12 /* PermanentDelegate */]: 32,
|
|
515
|
+
[13 /* NonTransferableAccount */]: 0,
|
|
516
|
+
[14 /* TransferHook */]: 64,
|
|
517
|
+
[15 /* TransferHookAccount */]: 1,
|
|
518
|
+
[16 /* ConfidentialTransferFeeConfig */]: 64,
|
|
519
|
+
[17 /* ConfidentialTransferFeeAmount */]: 32,
|
|
520
|
+
[18 /* MetadataPointer */]: 64,
|
|
521
|
+
// TokenMetadata is variable-sized, must use getMetadataExtensionSize
|
|
522
|
+
[20 /* GroupPointer */]: 64,
|
|
523
|
+
[21 /* TokenGroup */]: 72,
|
|
524
|
+
[22 /* GroupMemberPointer */]: 64,
|
|
525
|
+
[23 /* TokenGroupMember */]: 68
|
|
236
526
|
};
|
|
527
|
+
function getMintSizeWithExtensions({
|
|
528
|
+
extensions,
|
|
529
|
+
metadataSize
|
|
530
|
+
}) {
|
|
531
|
+
if (extensions.length === 0) {
|
|
532
|
+
return MINT_SIZE;
|
|
533
|
+
}
|
|
534
|
+
let totalSize = TOKEN_ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE;
|
|
535
|
+
for (const extension of extensions) {
|
|
536
|
+
if (extension === 19 /* TokenMetadata */) {
|
|
537
|
+
if (metadataSize === void 0) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
"metadataSize is required when ExtensionType.TokenMetadata is included. Use getMetadataExtensionSize() to calculate it."
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
totalSize += TLV_HEADER_SIZE + metadataSize;
|
|
543
|
+
} else {
|
|
544
|
+
const extSize = EXTENSION_SIZES[extension];
|
|
545
|
+
if (extSize === void 0) {
|
|
546
|
+
throw new Error(`Unknown extension type: ${extension}`);
|
|
547
|
+
}
|
|
548
|
+
totalSize += TLV_HEADER_SIZE + extSize;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return totalSize;
|
|
552
|
+
}
|
|
553
|
+
function getMetadataExtensionSize({
|
|
554
|
+
name,
|
|
555
|
+
symbol,
|
|
556
|
+
uri,
|
|
557
|
+
additionalMetadata = []
|
|
558
|
+
}) {
|
|
559
|
+
return getTokenMetadataDataSize({ name, symbol, uri, additionalMetadata });
|
|
560
|
+
}
|
|
561
|
+
async function getMinRentForMint(client, options) {
|
|
562
|
+
const size = getMintSizeWithExtensions(options);
|
|
563
|
+
return await client.getMinimumBalanceForRentExemption(BigInt(size));
|
|
564
|
+
}
|
|
565
|
+
function getMintSizeWithMetadata({
|
|
566
|
+
name,
|
|
567
|
+
symbol,
|
|
568
|
+
uri,
|
|
569
|
+
additionalMetadata = []
|
|
570
|
+
}) {
|
|
571
|
+
const metadataSize = getMetadataExtensionSize({ name, symbol, uri, additionalMetadata });
|
|
572
|
+
return getMintSizeWithExtensions({
|
|
573
|
+
extensions: [18 /* MetadataPointer */, 19 /* TokenMetadata */],
|
|
574
|
+
metadataSize
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async function getMinRentForMintWithMetadata(client, metadata) {
|
|
578
|
+
const size = getMintSizeWithMetadata(metadata);
|
|
579
|
+
return await client.getMinimumBalanceForRentExemption(BigInt(size));
|
|
580
|
+
}
|
|
237
581
|
function createAssociatedTokenAccountInstruction({
|
|
238
582
|
payer,
|
|
239
583
|
associatedToken,
|
|
@@ -281,7 +625,7 @@ function createAssociatedTokenAccountIdempotentInstruction({
|
|
|
281
625
|
};
|
|
282
626
|
}
|
|
283
627
|
|
|
284
|
-
// src/
|
|
628
|
+
// src/utils/bytes.ts
|
|
285
629
|
function writeU64LE({
|
|
286
630
|
data,
|
|
287
631
|
offset,
|
|
@@ -291,8 +635,96 @@ function writeU64LE({
|
|
|
291
635
|
data[offset + i] = Number(value >> BigInt(i * 8) & 0xffn);
|
|
292
636
|
}
|
|
293
637
|
}
|
|
638
|
+
function writeU16LE({
|
|
639
|
+
data,
|
|
640
|
+
offset,
|
|
641
|
+
value
|
|
642
|
+
}) {
|
|
643
|
+
data[offset] = value & 255;
|
|
644
|
+
data[offset + 1] = value >> 8 & 255;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/instructions/utils.ts
|
|
648
|
+
function isAuthoritySigner({ signers }) {
|
|
649
|
+
return (signers?.length ?? 0) === 0;
|
|
650
|
+
}
|
|
651
|
+
function writeOptionalNonZeroPubkey({
|
|
652
|
+
data,
|
|
653
|
+
offset,
|
|
654
|
+
value
|
|
655
|
+
}) {
|
|
656
|
+
if (value) {
|
|
657
|
+
data.set(value.toBytes(), offset);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function writeCOptionPubkey({
|
|
661
|
+
data,
|
|
662
|
+
offset,
|
|
663
|
+
value
|
|
664
|
+
}) {
|
|
665
|
+
if (value) {
|
|
666
|
+
data[offset] = 1;
|
|
667
|
+
data.set(value.toBytes(), offset + 1);
|
|
668
|
+
return 33;
|
|
669
|
+
}
|
|
670
|
+
data[offset] = 0;
|
|
671
|
+
return 1;
|
|
672
|
+
}
|
|
673
|
+
function appendSigners({
|
|
674
|
+
accounts,
|
|
675
|
+
signers
|
|
676
|
+
}) {
|
|
677
|
+
for (const signer of signers ?? []) {
|
|
678
|
+
accounts.push({ pubkey: signer, isSigner: true, isWritable: false });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
294
681
|
|
|
295
|
-
// src/instructions/
|
|
682
|
+
// src/instructions/metadata-pointer.ts
|
|
683
|
+
var METADATA_POINTER_INSTRUCTION_INITIALIZE = 0;
|
|
684
|
+
var METADATA_POINTER_INSTRUCTION_UPDATE = 1;
|
|
685
|
+
function initializeMetadataPointerInstruction({
|
|
686
|
+
mint,
|
|
687
|
+
authority,
|
|
688
|
+
metadataAddress,
|
|
689
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
690
|
+
}) {
|
|
691
|
+
const data = new Uint8Array(66);
|
|
692
|
+
data[0] = 39 /* MetadataPointerExtension */;
|
|
693
|
+
data[1] = METADATA_POINTER_INSTRUCTION_INITIALIZE;
|
|
694
|
+
writeOptionalNonZeroPubkey({ data, offset: 2, value: authority ?? null });
|
|
695
|
+
writeOptionalNonZeroPubkey({
|
|
696
|
+
data,
|
|
697
|
+
offset: 34,
|
|
698
|
+
value: metadataAddress ?? null
|
|
699
|
+
});
|
|
700
|
+
return {
|
|
701
|
+
programId,
|
|
702
|
+
accounts: [{ pubkey: mint, isSigner: false, isWritable: true }],
|
|
703
|
+
data
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function updateMetadataPointerInstruction({
|
|
707
|
+
mint,
|
|
708
|
+
authority,
|
|
709
|
+
metadataAddress,
|
|
710
|
+
signers,
|
|
711
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
712
|
+
}) {
|
|
713
|
+
const data = new Uint8Array(34);
|
|
714
|
+
data[0] = 39 /* MetadataPointerExtension */;
|
|
715
|
+
data[1] = METADATA_POINTER_INSTRUCTION_UPDATE;
|
|
716
|
+
writeOptionalNonZeroPubkey({
|
|
717
|
+
data,
|
|
718
|
+
offset: 2,
|
|
719
|
+
value: metadataAddress ?? null
|
|
720
|
+
});
|
|
721
|
+
const accounts = [
|
|
722
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
723
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
724
|
+
];
|
|
725
|
+
appendSigners({ accounts, signers });
|
|
726
|
+
return { programId, accounts, data };
|
|
727
|
+
}
|
|
296
728
|
function initializeMintInstruction({
|
|
297
729
|
mint,
|
|
298
730
|
decimals,
|
|
@@ -328,29 +760,43 @@ function mintToInstruction({
|
|
|
328
760
|
destination,
|
|
329
761
|
authority,
|
|
330
762
|
amount,
|
|
763
|
+
signers,
|
|
331
764
|
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
332
765
|
}) {
|
|
333
766
|
const data = new Uint8Array(9);
|
|
334
767
|
data[0] = 7 /* MintTo */;
|
|
335
768
|
writeU64LE({ data, offset: 1, value: amount });
|
|
769
|
+
const accounts = [
|
|
770
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
771
|
+
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
772
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
773
|
+
];
|
|
774
|
+
appendSigners({ accounts, signers });
|
|
775
|
+
return { programId, accounts, data };
|
|
776
|
+
}
|
|
777
|
+
function initializeNonTransferableMintInstruction({
|
|
778
|
+
mint,
|
|
779
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
780
|
+
}) {
|
|
336
781
|
return {
|
|
337
782
|
programId,
|
|
338
|
-
accounts: [
|
|
339
|
-
|
|
340
|
-
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
341
|
-
{ pubkey: authority, isSigner: true, isWritable: false }
|
|
342
|
-
],
|
|
343
|
-
data
|
|
783
|
+
accounts: [{ pubkey: mint, isSigner: false, isWritable: true }],
|
|
784
|
+
data: new Uint8Array([32 /* InitializeNonTransferableMint */])
|
|
344
785
|
};
|
|
345
786
|
}
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
787
|
+
function initializePermanentDelegateInstruction({
|
|
788
|
+
mint,
|
|
789
|
+
delegate,
|
|
790
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
350
791
|
}) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
792
|
+
const data = new Uint8Array(33);
|
|
793
|
+
data[0] = 35 /* InitializePermanentDelegate */;
|
|
794
|
+
data.set(delegate.toBytes(), 1);
|
|
795
|
+
return {
|
|
796
|
+
programId,
|
|
797
|
+
accounts: [{ pubkey: mint, isSigner: false, isWritable: true }],
|
|
798
|
+
data
|
|
799
|
+
};
|
|
354
800
|
}
|
|
355
801
|
function transferCheckedInstruction({
|
|
356
802
|
source,
|
|
@@ -359,359 +805,289 @@ function transferCheckedInstruction({
|
|
|
359
805
|
authority,
|
|
360
806
|
amount,
|
|
361
807
|
decimals,
|
|
808
|
+
signers,
|
|
362
809
|
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
363
810
|
}) {
|
|
364
811
|
const data = new Uint8Array(10);
|
|
365
812
|
data[0] = 12 /* TransferChecked */;
|
|
366
|
-
|
|
813
|
+
writeU64LE({ data, offset: 1, value: amount });
|
|
367
814
|
data[9] = decimals;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
data
|
|
377
|
-
};
|
|
815
|
+
const accounts = [
|
|
816
|
+
{ pubkey: source, isSigner: false, isWritable: true },
|
|
817
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
818
|
+
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
819
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
820
|
+
];
|
|
821
|
+
appendSigners({ accounts, signers });
|
|
822
|
+
return { programId, accounts, data };
|
|
378
823
|
}
|
|
379
824
|
function transferInstruction({
|
|
380
825
|
source,
|
|
381
826
|
destination,
|
|
382
827
|
authority,
|
|
383
828
|
amount,
|
|
829
|
+
signers,
|
|
384
830
|
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
385
831
|
}) {
|
|
386
832
|
const data = new Uint8Array(9);
|
|
387
833
|
data[0] = 3 /* Transfer */;
|
|
388
|
-
|
|
834
|
+
writeU64LE({ data, offset: 1, value: amount });
|
|
835
|
+
const accounts = [
|
|
836
|
+
{ pubkey: source, isSigner: false, isWritable: true },
|
|
837
|
+
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
838
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
839
|
+
];
|
|
840
|
+
appendSigners({ accounts, signers });
|
|
841
|
+
return { programId, accounts, data };
|
|
842
|
+
}
|
|
843
|
+
var TRANSFER_FEE_INSTRUCTION_INITIALIZE_CONFIG = 0;
|
|
844
|
+
var TRANSFER_FEE_INSTRUCTION_TRANSFER_CHECKED_WITH_FEE = 1;
|
|
845
|
+
var TRANSFER_FEE_INSTRUCTION_SET_TRANSFER_FEE = 5;
|
|
846
|
+
function initializeTransferFeeConfigInstruction({
|
|
847
|
+
mint,
|
|
848
|
+
transferFeeConfigAuthority,
|
|
849
|
+
withdrawWithheldAuthority,
|
|
850
|
+
transferFeeBasisPoints,
|
|
851
|
+
maximumFee,
|
|
852
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
853
|
+
}) {
|
|
854
|
+
const authorityLength = transferFeeConfigAuthority ? 33 : 1;
|
|
855
|
+
const withdrawLength = withdrawWithheldAuthority ? 33 : 1;
|
|
856
|
+
const data = new Uint8Array(2 + authorityLength + withdrawLength + 2 + 8);
|
|
857
|
+
data[0] = 26 /* TransferFeeExtension */;
|
|
858
|
+
data[1] = TRANSFER_FEE_INSTRUCTION_INITIALIZE_CONFIG;
|
|
859
|
+
let offset = 2;
|
|
860
|
+
offset += writeCOptionPubkey({
|
|
861
|
+
data,
|
|
862
|
+
offset,
|
|
863
|
+
value: transferFeeConfigAuthority ?? null
|
|
864
|
+
});
|
|
865
|
+
offset += writeCOptionPubkey({
|
|
866
|
+
data,
|
|
867
|
+
offset,
|
|
868
|
+
value: withdrawWithheldAuthority ?? null
|
|
869
|
+
});
|
|
870
|
+
writeU16LE({ data, offset, value: transferFeeBasisPoints });
|
|
871
|
+
offset += 2;
|
|
872
|
+
writeU64LE({ data, offset, value: maximumFee });
|
|
389
873
|
return {
|
|
390
874
|
programId,
|
|
391
|
-
accounts: [
|
|
392
|
-
{ pubkey: source, isSigner: false, isWritable: true },
|
|
393
|
-
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
394
|
-
{ pubkey: authority, isSigner: true, isWritable: false }
|
|
395
|
-
],
|
|
875
|
+
accounts: [{ pubkey: mint, isSigner: false, isWritable: true }],
|
|
396
876
|
data
|
|
397
877
|
};
|
|
398
878
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const min = instance.outputLen;
|
|
425
|
-
if (out.length < min) {
|
|
426
|
-
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
function clean(...arrays) {
|
|
430
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
431
|
-
arrays[i].fill(0);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
function createView(arr) {
|
|
435
|
-
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
879
|
+
function transferCheckedWithFeeInstruction({
|
|
880
|
+
source,
|
|
881
|
+
mint,
|
|
882
|
+
destination,
|
|
883
|
+
authority,
|
|
884
|
+
amount,
|
|
885
|
+
decimals,
|
|
886
|
+
fee,
|
|
887
|
+
signers,
|
|
888
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
889
|
+
}) {
|
|
890
|
+
const data = new Uint8Array(2 + 8 + 1 + 8);
|
|
891
|
+
data[0] = 26 /* TransferFeeExtension */;
|
|
892
|
+
data[1] = TRANSFER_FEE_INSTRUCTION_TRANSFER_CHECKED_WITH_FEE;
|
|
893
|
+
writeU64LE({ data, offset: 2, value: amount });
|
|
894
|
+
data[10] = decimals;
|
|
895
|
+
writeU64LE({ data, offset: 11, value: fee });
|
|
896
|
+
const accounts = [
|
|
897
|
+
{ pubkey: source, isSigner: false, isWritable: true },
|
|
898
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
899
|
+
{ pubkey: destination, isSigner: false, isWritable: true },
|
|
900
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
901
|
+
];
|
|
902
|
+
appendSigners({ accounts, signers });
|
|
903
|
+
return { programId, accounts, data };
|
|
436
904
|
}
|
|
437
|
-
function
|
|
438
|
-
|
|
905
|
+
function setTransferFeeInstruction({
|
|
906
|
+
mint,
|
|
907
|
+
authority,
|
|
908
|
+
transferFeeBasisPoints,
|
|
909
|
+
maximumFee,
|
|
910
|
+
signers,
|
|
911
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
912
|
+
}) {
|
|
913
|
+
const data = new Uint8Array(2 + 2 + 8);
|
|
914
|
+
data[0] = 26 /* TransferFeeExtension */;
|
|
915
|
+
data[1] = TRANSFER_FEE_INSTRUCTION_SET_TRANSFER_FEE;
|
|
916
|
+
writeU16LE({ data, offset: 2, value: transferFeeBasisPoints });
|
|
917
|
+
writeU64LE({ data, offset: 4, value: maximumFee });
|
|
918
|
+
const accounts = [
|
|
919
|
+
{ pubkey: mint, isSigner: false, isWritable: true },
|
|
920
|
+
{ pubkey: authority, isSigner: isAuthoritySigner({ signers }), isWritable: false }
|
|
921
|
+
];
|
|
922
|
+
appendSigners({ accounts, signers });
|
|
923
|
+
return { programId, accounts, data };
|
|
439
924
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
925
|
+
var TRANSFER_HOOK_INSTRUCTION_INITIALIZE = 0;
|
|
926
|
+
function initializeTransferHookInstruction({
|
|
927
|
+
mint,
|
|
928
|
+
authority,
|
|
929
|
+
hookProgramId,
|
|
930
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
931
|
+
}) {
|
|
932
|
+
const data = new Uint8Array(66);
|
|
933
|
+
data[0] = 36 /* TransferHookExtension */;
|
|
934
|
+
data[1] = TRANSFER_HOOK_INSTRUCTION_INITIALIZE;
|
|
935
|
+
data.set(authority.toBytes(), 2);
|
|
936
|
+
data.set(hookProgramId.toBytes(), 34);
|
|
937
|
+
return {
|
|
938
|
+
programId,
|
|
939
|
+
accounts: [{ pubkey: mint, isSigner: false, isWritable: true }],
|
|
940
|
+
data
|
|
941
|
+
};
|
|
448
942
|
}
|
|
449
|
-
var oidNist = (suffix) => ({
|
|
450
|
-
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
451
|
-
});
|
|
452
943
|
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
this.
|
|
474
|
-
this.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
this.buffer = new Uint8Array(blockLen);
|
|
478
|
-
this.view = createView(this.buffer);
|
|
479
|
-
}
|
|
480
|
-
update(data) {
|
|
481
|
-
aexists(this);
|
|
482
|
-
abytes(data);
|
|
483
|
-
const { view, buffer, blockLen } = this;
|
|
484
|
-
const len = data.length;
|
|
485
|
-
for (let pos = 0; pos < len; ) {
|
|
486
|
-
const take = Math.min(blockLen - this.pos, len - pos);
|
|
487
|
-
if (take === blockLen) {
|
|
488
|
-
const dataView = createView(data);
|
|
489
|
-
for (; blockLen <= len - pos; pos += blockLen)
|
|
490
|
-
this.process(dataView, pos);
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
494
|
-
this.pos += take;
|
|
495
|
-
pos += take;
|
|
496
|
-
if (this.pos === blockLen) {
|
|
497
|
-
this.process(view, 0);
|
|
498
|
-
this.pos = 0;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
this.length += data.length;
|
|
502
|
-
this.roundClean();
|
|
503
|
-
return this;
|
|
504
|
-
}
|
|
505
|
-
digestInto(out) {
|
|
506
|
-
aexists(this);
|
|
507
|
-
aoutput(out, this);
|
|
508
|
-
this.finished = true;
|
|
509
|
-
const { buffer, view, blockLen, isLE } = this;
|
|
510
|
-
let { pos } = this;
|
|
511
|
-
buffer[pos++] = 128;
|
|
512
|
-
clean(this.buffer.subarray(pos));
|
|
513
|
-
if (this.padOffset > blockLen - pos) {
|
|
514
|
-
this.process(view, 0);
|
|
515
|
-
pos = 0;
|
|
944
|
+
// src/errors.ts
|
|
945
|
+
var SplTokenErrorCode = /* @__PURE__ */ ((SplTokenErrorCode2) => {
|
|
946
|
+
SplTokenErrorCode2["INVALID_MINT"] = "INVALID_MINT";
|
|
947
|
+
SplTokenErrorCode2["INVALID_TOKEN_ACCOUNT"] = "INVALID_TOKEN_ACCOUNT";
|
|
948
|
+
SplTokenErrorCode2["TOKEN_ACCOUNT_NOT_FOUND"] = "TOKEN_ACCOUNT_NOT_FOUND";
|
|
949
|
+
SplTokenErrorCode2["MINT_NOT_FOUND"] = "MINT_NOT_FOUND";
|
|
950
|
+
SplTokenErrorCode2["ACCOUNT_FROZEN"] = "ACCOUNT_FROZEN";
|
|
951
|
+
SplTokenErrorCode2["INSUFFICIENT_BALANCE"] = "INSUFFICIENT_BALANCE";
|
|
952
|
+
SplTokenErrorCode2["INVALID_EXTENSION"] = "INVALID_EXTENSION";
|
|
953
|
+
SplTokenErrorCode2["PDA_DERIVATION_FAILED"] = "PDA_DERIVATION_FAILED";
|
|
954
|
+
SplTokenErrorCode2["INVALID_METADATA"] = "INVALID_METADATA";
|
|
955
|
+
SplTokenErrorCode2["ACCOUNT_NOT_INITIALIZED"] = "ACCOUNT_NOT_INITIALIZED";
|
|
956
|
+
return SplTokenErrorCode2;
|
|
957
|
+
})(SplTokenErrorCode || {});
|
|
958
|
+
var SplTokenError = class _SplTokenError extends Error {
|
|
959
|
+
code;
|
|
960
|
+
details;
|
|
961
|
+
constructor({ code, message, details }) {
|
|
962
|
+
super(message);
|
|
963
|
+
this.name = "SplTokenError";
|
|
964
|
+
this.code = code;
|
|
965
|
+
this.details = details;
|
|
966
|
+
if (Error.captureStackTrace) {
|
|
967
|
+
Error.captureStackTrace(this, _SplTokenError);
|
|
516
968
|
}
|
|
517
|
-
for (let i = pos; i < blockLen; i++)
|
|
518
|
-
buffer[i] = 0;
|
|
519
|
-
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
520
|
-
this.process(view, 0);
|
|
521
|
-
const oview = createView(out);
|
|
522
|
-
const len = this.outputLen;
|
|
523
|
-
if (len % 4)
|
|
524
|
-
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|
525
|
-
const outLen = len / 4;
|
|
526
|
-
const state = this.get();
|
|
527
|
-
if (outLen > state.length)
|
|
528
|
-
throw new Error("_sha2: outputLen bigger than state");
|
|
529
|
-
for (let i = 0; i < outLen; i++)
|
|
530
|
-
oview.setUint32(4 * i, state[i], isLE);
|
|
531
969
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
970
|
+
/**
|
|
971
|
+
* Creates an invalid mint error.
|
|
972
|
+
*
|
|
973
|
+
* @param options - Error options containing the reason
|
|
974
|
+
*/
|
|
975
|
+
static invalidMint({ reason }) {
|
|
976
|
+
return new _SplTokenError({
|
|
977
|
+
code: "INVALID_MINT" /* INVALID_MINT */,
|
|
978
|
+
message: `Invalid mint: ${reason}`
|
|
979
|
+
});
|
|
538
980
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return to;
|
|
981
|
+
/**
|
|
982
|
+
* Creates an invalid token account error.
|
|
983
|
+
*
|
|
984
|
+
* @param options - Error options containing the reason
|
|
985
|
+
*/
|
|
986
|
+
static invalidTokenAccount({ reason }) {
|
|
987
|
+
return new _SplTokenError({
|
|
988
|
+
code: "INVALID_TOKEN_ACCOUNT" /* INVALID_TOKEN_ACCOUNT */,
|
|
989
|
+
message: `Invalid token account: ${reason}`
|
|
990
|
+
});
|
|
550
991
|
}
|
|
551
|
-
|
|
552
|
-
|
|
992
|
+
/**
|
|
993
|
+
* Creates a token account not found error.
|
|
994
|
+
*
|
|
995
|
+
* @param options - Error options containing the address
|
|
996
|
+
*/
|
|
997
|
+
static tokenAccountNotFound({ address }) {
|
|
998
|
+
return new _SplTokenError({
|
|
999
|
+
code: "TOKEN_ACCOUNT_NOT_FOUND" /* TOKEN_ACCOUNT_NOT_FOUND */,
|
|
1000
|
+
message: `Token account not found: ${address}`,
|
|
1001
|
+
details: { address }
|
|
1002
|
+
});
|
|
553
1003
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// node_modules/@noble/hashes/sha2.js
|
|
567
|
-
var SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
568
|
-
1116352408,
|
|
569
|
-
1899447441,
|
|
570
|
-
3049323471,
|
|
571
|
-
3921009573,
|
|
572
|
-
961987163,
|
|
573
|
-
1508970993,
|
|
574
|
-
2453635748,
|
|
575
|
-
2870763221,
|
|
576
|
-
3624381080,
|
|
577
|
-
310598401,
|
|
578
|
-
607225278,
|
|
579
|
-
1426881987,
|
|
580
|
-
1925078388,
|
|
581
|
-
2162078206,
|
|
582
|
-
2614888103,
|
|
583
|
-
3248222580,
|
|
584
|
-
3835390401,
|
|
585
|
-
4022224774,
|
|
586
|
-
264347078,
|
|
587
|
-
604807628,
|
|
588
|
-
770255983,
|
|
589
|
-
1249150122,
|
|
590
|
-
1555081692,
|
|
591
|
-
1996064986,
|
|
592
|
-
2554220882,
|
|
593
|
-
2821834349,
|
|
594
|
-
2952996808,
|
|
595
|
-
3210313671,
|
|
596
|
-
3336571891,
|
|
597
|
-
3584528711,
|
|
598
|
-
113926993,
|
|
599
|
-
338241895,
|
|
600
|
-
666307205,
|
|
601
|
-
773529912,
|
|
602
|
-
1294757372,
|
|
603
|
-
1396182291,
|
|
604
|
-
1695183700,
|
|
605
|
-
1986661051,
|
|
606
|
-
2177026350,
|
|
607
|
-
2456956037,
|
|
608
|
-
2730485921,
|
|
609
|
-
2820302411,
|
|
610
|
-
3259730800,
|
|
611
|
-
3345764771,
|
|
612
|
-
3516065817,
|
|
613
|
-
3600352804,
|
|
614
|
-
4094571909,
|
|
615
|
-
275423344,
|
|
616
|
-
430227734,
|
|
617
|
-
506948616,
|
|
618
|
-
659060556,
|
|
619
|
-
883997877,
|
|
620
|
-
958139571,
|
|
621
|
-
1322822218,
|
|
622
|
-
1537002063,
|
|
623
|
-
1747873779,
|
|
624
|
-
1955562222,
|
|
625
|
-
2024104815,
|
|
626
|
-
2227730452,
|
|
627
|
-
2361852424,
|
|
628
|
-
2428436474,
|
|
629
|
-
2756734187,
|
|
630
|
-
3204031479,
|
|
631
|
-
3329325298
|
|
632
|
-
]);
|
|
633
|
-
var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
634
|
-
var SHA2_32B = class extends HashMD {
|
|
635
|
-
constructor(outputLen) {
|
|
636
|
-
super(64, outputLen, 8, false);
|
|
1004
|
+
/**
|
|
1005
|
+
* Creates a mint not found error.
|
|
1006
|
+
*
|
|
1007
|
+
* @param options - Error options containing the address
|
|
1008
|
+
*/
|
|
1009
|
+
static mintNotFound({ address }) {
|
|
1010
|
+
return new _SplTokenError({
|
|
1011
|
+
code: "MINT_NOT_FOUND" /* MINT_NOT_FOUND */,
|
|
1012
|
+
message: `Mint not found: ${address}`,
|
|
1013
|
+
details: { address }
|
|
1014
|
+
});
|
|
637
1015
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* Creates an account frozen error.
|
|
1018
|
+
*
|
|
1019
|
+
* @param options - Error options containing the address
|
|
1020
|
+
*/
|
|
1021
|
+
static accountFrozen({ address }) {
|
|
1022
|
+
return new _SplTokenError({
|
|
1023
|
+
code: "ACCOUNT_FROZEN" /* ACCOUNT_FROZEN */,
|
|
1024
|
+
message: `Token account is frozen: ${address}`,
|
|
1025
|
+
details: { address }
|
|
1026
|
+
});
|
|
641
1027
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
1028
|
+
/**
|
|
1029
|
+
* Creates an insufficient balance error.
|
|
1030
|
+
*
|
|
1031
|
+
* @param options - Error options containing required and available amounts
|
|
1032
|
+
*/
|
|
1033
|
+
static insufficientBalance({
|
|
1034
|
+
required,
|
|
1035
|
+
available
|
|
1036
|
+
}) {
|
|
1037
|
+
return new _SplTokenError({
|
|
1038
|
+
code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */,
|
|
1039
|
+
message: `Insufficient balance: required ${required}, available ${available}`,
|
|
1040
|
+
details: { required: required.toString(), available: available.toString() }
|
|
1041
|
+
});
|
|
652
1042
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
let { A, B, C, D, E, F, G, H } = this;
|
|
664
|
-
for (let i = 0; i < 64; i++) {
|
|
665
|
-
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
666
|
-
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
667
|
-
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
668
|
-
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
669
|
-
H = G;
|
|
670
|
-
G = F;
|
|
671
|
-
F = E;
|
|
672
|
-
E = D + T1 | 0;
|
|
673
|
-
D = C;
|
|
674
|
-
C = B;
|
|
675
|
-
B = A;
|
|
676
|
-
A = T1 + T2 | 0;
|
|
677
|
-
}
|
|
678
|
-
A = A + this.A | 0;
|
|
679
|
-
B = B + this.B | 0;
|
|
680
|
-
C = C + this.C | 0;
|
|
681
|
-
D = D + this.D | 0;
|
|
682
|
-
E = E + this.E | 0;
|
|
683
|
-
F = F + this.F | 0;
|
|
684
|
-
G = G + this.G | 0;
|
|
685
|
-
H = H + this.H | 0;
|
|
686
|
-
this.set(A, B, C, D, E, F, G, H);
|
|
1043
|
+
/**
|
|
1044
|
+
* Creates an invalid extension error.
|
|
1045
|
+
*
|
|
1046
|
+
* @param options - Error options containing the reason
|
|
1047
|
+
*/
|
|
1048
|
+
static invalidExtension({ reason }) {
|
|
1049
|
+
return new _SplTokenError({
|
|
1050
|
+
code: "INVALID_EXTENSION" /* INVALID_EXTENSION */,
|
|
1051
|
+
message: `Invalid extension: ${reason}`
|
|
1052
|
+
});
|
|
687
1053
|
}
|
|
688
|
-
|
|
689
|
-
|
|
1054
|
+
/**
|
|
1055
|
+
* Creates a PDA derivation failed error.
|
|
1056
|
+
*
|
|
1057
|
+
* @param options - Error options containing the reason
|
|
1058
|
+
*/
|
|
1059
|
+
static pdaDerivationFailed({ reason }) {
|
|
1060
|
+
return new _SplTokenError({
|
|
1061
|
+
code: "PDA_DERIVATION_FAILED" /* PDA_DERIVATION_FAILED */,
|
|
1062
|
+
message: `PDA derivation failed: ${reason}`
|
|
1063
|
+
});
|
|
690
1064
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1065
|
+
/**
|
|
1066
|
+
* Creates an invalid metadata error.
|
|
1067
|
+
*
|
|
1068
|
+
* @param options - Error options containing the reason
|
|
1069
|
+
*/
|
|
1070
|
+
static invalidMetadata({ reason }) {
|
|
1071
|
+
return new _SplTokenError({
|
|
1072
|
+
code: "INVALID_METADATA" /* INVALID_METADATA */,
|
|
1073
|
+
message: `Invalid metadata: ${reason}`
|
|
1074
|
+
});
|
|
694
1075
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
H = SHA256_IV[7] | 0;
|
|
707
|
-
constructor() {
|
|
708
|
-
super(32);
|
|
1076
|
+
/**
|
|
1077
|
+
* Creates an account not initialized error.
|
|
1078
|
+
*
|
|
1079
|
+
* @param options - Error options containing the address
|
|
1080
|
+
*/
|
|
1081
|
+
static accountNotInitialized({ address }) {
|
|
1082
|
+
return new _SplTokenError({
|
|
1083
|
+
code: "ACCOUNT_NOT_INITIALIZED" /* ACCOUNT_NOT_INITIALIZED */,
|
|
1084
|
+
message: `Account not initialized: ${address}`,
|
|
1085
|
+
details: { address }
|
|
1086
|
+
});
|
|
709
1087
|
}
|
|
710
1088
|
};
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
/* @__PURE__ */ oidNist(1)
|
|
714
|
-
);
|
|
1089
|
+
|
|
1090
|
+
// src/pda/ata.ts
|
|
715
1091
|
function createProgramAddress({
|
|
716
1092
|
seeds,
|
|
717
1093
|
programId
|
|
@@ -743,29 +1119,418 @@ function createProgramAddress({
|
|
|
743
1119
|
};
|
|
744
1120
|
}
|
|
745
1121
|
}
|
|
746
|
-
return null;
|
|
747
|
-
}
|
|
748
|
-
function findAssociatedTokenAddress({
|
|
749
|
-
wallet,
|
|
750
|
-
mint,
|
|
751
|
-
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
752
|
-
}) {
|
|
753
|
-
const associatedTokenProgramId = PublicKey.fromString(ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
754
|
-
const seeds = [wallet.toBytes(), programId.toBytes(), mint.toBytes()];
|
|
755
|
-
const result = createProgramAddress({ seeds, programId: associatedTokenProgramId });
|
|
756
|
-
if (!result) {
|
|
757
|
-
throw SplTokenError.pdaDerivationFailed({
|
|
758
|
-
reason: `Could not find valid PDA for wallet=${wallet.toString()}, mint=${mint.toString()}`
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
return result;
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
function findAssociatedTokenAddress({
|
|
1125
|
+
wallet,
|
|
1126
|
+
mint,
|
|
1127
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
1128
|
+
}) {
|
|
1129
|
+
const associatedTokenProgramId = PublicKey.fromString(ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
1130
|
+
const seeds = [wallet.toBytes(), programId.toBytes(), mint.toBytes()];
|
|
1131
|
+
const result = createProgramAddress({ seeds, programId: associatedTokenProgramId });
|
|
1132
|
+
if (!result) {
|
|
1133
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
1134
|
+
reason: `Could not find valid PDA for wallet=${wallet.toString()}, mint=${mint.toString()}`
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
return result;
|
|
1138
|
+
}
|
|
1139
|
+
function getAssociatedTokenAddressSync({
|
|
1140
|
+
wallet,
|
|
1141
|
+
mint,
|
|
1142
|
+
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
1143
|
+
}) {
|
|
1144
|
+
return findAssociatedTokenAddress({ wallet, mint, programId }).address;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// src/builders/mint-builder.ts
|
|
1148
|
+
var MintBuilder = class _MintBuilder {
|
|
1149
|
+
client;
|
|
1150
|
+
programId;
|
|
1151
|
+
// Default of 9 matches SOL and most major Solana tokens (e.g. USDC uses 6, native SOL uses 9).
|
|
1152
|
+
// SPL Token-2022 stores decimals as a u8, so the valid range is 0–255.
|
|
1153
|
+
decimals = 9;
|
|
1154
|
+
mintAuthority = null;
|
|
1155
|
+
freezeAuthority = null;
|
|
1156
|
+
metadata = null;
|
|
1157
|
+
transferFee = null;
|
|
1158
|
+
nonTransferable = false;
|
|
1159
|
+
permanentDelegate = null;
|
|
1160
|
+
transferHook = null;
|
|
1161
|
+
initialSupply = null;
|
|
1162
|
+
constructor(tokenClient, client) {
|
|
1163
|
+
this.client = client;
|
|
1164
|
+
this.programId = tokenClient.getProgramId();
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Creates a new MintBuilder instance.
|
|
1168
|
+
*
|
|
1169
|
+
* @param tokenClient - SplTokenClient instance
|
|
1170
|
+
* @returns A new MintBuilder
|
|
1171
|
+
*
|
|
1172
|
+
* @example
|
|
1173
|
+
* ```typescript
|
|
1174
|
+
* const builder = MintBuilder.create(tokenClient);
|
|
1175
|
+
* ```
|
|
1176
|
+
*/
|
|
1177
|
+
static create(tokenClient) {
|
|
1178
|
+
return new _MintBuilder(tokenClient, tokenClient.getRialoClient());
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Sets the number of decimals for the token.
|
|
1182
|
+
*
|
|
1183
|
+
* SPL Token-2022 stores decimals as a u8, so values from 0 to 255 are
|
|
1184
|
+
* valid at the protocol level. Common choices are 6 (USDC-style) or 9
|
|
1185
|
+
* (SOL-style). Values above 9 are uncommon on Solana but fully supported.
|
|
1186
|
+
*
|
|
1187
|
+
* @param decimals - Number of decimals (0-255)
|
|
1188
|
+
* @returns this for chaining
|
|
1189
|
+
*/
|
|
1190
|
+
withDecimals(decimals) {
|
|
1191
|
+
if (decimals < 0 || decimals > 255) {
|
|
1192
|
+
throw new Error("Decimals must be between 0 and 255");
|
|
1193
|
+
}
|
|
1194
|
+
this.decimals = decimals;
|
|
1195
|
+
return this;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Sets the mint authority.
|
|
1199
|
+
*
|
|
1200
|
+
* @param authority - Public key of the mint authority
|
|
1201
|
+
* @returns this for chaining
|
|
1202
|
+
*/
|
|
1203
|
+
withMintAuthority(authority) {
|
|
1204
|
+
this.mintAuthority = authority;
|
|
1205
|
+
return this;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Sets the freeze authority.
|
|
1209
|
+
*
|
|
1210
|
+
* @param authority - Public key of the freeze authority
|
|
1211
|
+
* @returns this for chaining
|
|
1212
|
+
*/
|
|
1213
|
+
withFreezeAuthority(authority) {
|
|
1214
|
+
this.freezeAuthority = authority;
|
|
1215
|
+
return this;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Configures token metadata extension.
|
|
1219
|
+
*
|
|
1220
|
+
* This enables both MetadataPointer and TokenMetadata extensions,
|
|
1221
|
+
* with the metadata stored directly on the mint account.
|
|
1222
|
+
*
|
|
1223
|
+
* @param metadata - Token metadata configuration
|
|
1224
|
+
* @returns this for chaining
|
|
1225
|
+
*/
|
|
1226
|
+
withMetadata(metadata) {
|
|
1227
|
+
this.metadata = metadata;
|
|
1228
|
+
return this;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Configures transfer fee extension.
|
|
1232
|
+
*
|
|
1233
|
+
* @param config - Transfer fee configuration
|
|
1234
|
+
* @returns this for chaining
|
|
1235
|
+
*/
|
|
1236
|
+
withTransferFee(config) {
|
|
1237
|
+
if (this.nonTransferable) {
|
|
1238
|
+
throw new Error("Cannot add transfer fee to non-transferable mint");
|
|
1239
|
+
}
|
|
1240
|
+
this.transferFee = config;
|
|
1241
|
+
return this;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Makes the mint non-transferable.
|
|
1245
|
+
*
|
|
1246
|
+
* Non-transferable tokens (soulbound tokens) cannot be transferred
|
|
1247
|
+
* after minting.
|
|
1248
|
+
*
|
|
1249
|
+
* @returns this for chaining
|
|
1250
|
+
*/
|
|
1251
|
+
withNonTransferable() {
|
|
1252
|
+
if (this.transferFee) {
|
|
1253
|
+
throw new Error(
|
|
1254
|
+
"Cannot make mint non-transferable when transfer fee is configured"
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
this.nonTransferable = true;
|
|
1258
|
+
return this;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Configures permanent delegate extension.
|
|
1262
|
+
*
|
|
1263
|
+
* A permanent delegate has unrestricted transfer and burn authority
|
|
1264
|
+
* over all token accounts for this mint.
|
|
1265
|
+
*
|
|
1266
|
+
* @param delegate - Public key of the permanent delegate
|
|
1267
|
+
* @returns this for chaining
|
|
1268
|
+
*/
|
|
1269
|
+
withPermanentDelegate(delegate) {
|
|
1270
|
+
this.permanentDelegate = delegate;
|
|
1271
|
+
return this;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Configures transfer hook extension.
|
|
1275
|
+
*
|
|
1276
|
+
* A transfer hook invokes a specified program on every token transfer,
|
|
1277
|
+
* enabling custom transfer logic (e.g., compliance checks, royalties).
|
|
1278
|
+
*
|
|
1279
|
+
* @param config - Transfer hook configuration
|
|
1280
|
+
* @returns this for chaining
|
|
1281
|
+
*/
|
|
1282
|
+
withTransferHook(config) {
|
|
1283
|
+
this.transferHook = config;
|
|
1284
|
+
return this;
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Configures initial token supply to mint after creation.
|
|
1288
|
+
*
|
|
1289
|
+
* The tokens will be minted to an Associated Token Account (ATA)
|
|
1290
|
+
* derived from the destination wallet.
|
|
1291
|
+
*
|
|
1292
|
+
* @param config - Initial supply configuration
|
|
1293
|
+
* @returns this for chaining
|
|
1294
|
+
*/
|
|
1295
|
+
withInitialSupply(config) {
|
|
1296
|
+
this.initialSupply = config;
|
|
1297
|
+
return this;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Builds the mint transaction without sending.
|
|
1301
|
+
*
|
|
1302
|
+
* @param options - Build options
|
|
1303
|
+
* @returns Build result with transaction and addresses
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* ```typescript
|
|
1307
|
+
* const { transaction, mintAddress } = await builder.build({
|
|
1308
|
+
* payer: authority,
|
|
1309
|
+
* mintKeypair,
|
|
1310
|
+
* validFrom: BigInt(Date.now()),
|
|
1311
|
+
* });
|
|
1312
|
+
*
|
|
1313
|
+
* // Sign manually
|
|
1314
|
+
* const signed = transaction.sign(authorityKeypair).sign(mintKeypair);
|
|
1315
|
+
* await client.sendAndConfirmTransaction(signed.serialize());
|
|
1316
|
+
* ```
|
|
1317
|
+
*/
|
|
1318
|
+
async build(options) {
|
|
1319
|
+
const { payer, mintKeypair, validFrom } = options;
|
|
1320
|
+
const mintAddress = mintKeypair.publicKey;
|
|
1321
|
+
if (!this.mintAuthority) {
|
|
1322
|
+
throw new Error(
|
|
1323
|
+
"Mint authority is required. Call withMintAuthority() first."
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
const extensions = this.getExtensions();
|
|
1327
|
+
const createAccountExtensions = extensions.filter(
|
|
1328
|
+
(ext) => ext !== 19 /* TokenMetadata */
|
|
1329
|
+
);
|
|
1330
|
+
const createAccountSize = getMintSizeWithExtensions({
|
|
1331
|
+
extensions: createAccountExtensions,
|
|
1332
|
+
metadataSize: void 0
|
|
1333
|
+
});
|
|
1334
|
+
let rentSize = createAccountSize;
|
|
1335
|
+
if (this.metadata) {
|
|
1336
|
+
const metadataSize = getMetadataExtensionSize({
|
|
1337
|
+
name: this.metadata.name,
|
|
1338
|
+
symbol: this.metadata.symbol,
|
|
1339
|
+
uri: this.metadata.uri,
|
|
1340
|
+
additionalMetadata: this.metadata.additionalMetadata
|
|
1341
|
+
});
|
|
1342
|
+
rentSize = getMintSizeWithExtensions({
|
|
1343
|
+
extensions,
|
|
1344
|
+
metadataSize
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
const rent = await this.client.getMinimumBalanceForRentExemption(
|
|
1348
|
+
BigInt(rentSize)
|
|
1349
|
+
);
|
|
1350
|
+
const instructions = [];
|
|
1351
|
+
instructions.push(
|
|
1352
|
+
createAccount(
|
|
1353
|
+
payer,
|
|
1354
|
+
mintAddress,
|
|
1355
|
+
rent,
|
|
1356
|
+
BigInt(createAccountSize),
|
|
1357
|
+
this.programId
|
|
1358
|
+
)
|
|
1359
|
+
);
|
|
1360
|
+
if (this.metadata) {
|
|
1361
|
+
instructions.push(
|
|
1362
|
+
initializeMetadataPointerInstruction({
|
|
1363
|
+
mint: mintAddress,
|
|
1364
|
+
authority: this.mintAuthority,
|
|
1365
|
+
metadataAddress: mintAddress,
|
|
1366
|
+
// Self-referential for on-mint metadata
|
|
1367
|
+
programId: this.programId
|
|
1368
|
+
})
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
if (this.transferFee) {
|
|
1372
|
+
instructions.push(
|
|
1373
|
+
initializeTransferFeeConfigInstruction({
|
|
1374
|
+
mint: mintAddress,
|
|
1375
|
+
transferFeeConfigAuthority: this.transferFee.transferFeeConfigAuthority,
|
|
1376
|
+
withdrawWithheldAuthority: this.transferFee.withdrawWithheldAuthority,
|
|
1377
|
+
transferFeeBasisPoints: this.transferFee.transferFeeBasisPoints,
|
|
1378
|
+
maximumFee: this.transferFee.maximumFee,
|
|
1379
|
+
programId: this.programId
|
|
1380
|
+
})
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
if (this.nonTransferable) {
|
|
1384
|
+
instructions.push(
|
|
1385
|
+
initializeNonTransferableMintInstruction({
|
|
1386
|
+
mint: mintAddress,
|
|
1387
|
+
programId: this.programId
|
|
1388
|
+
})
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
if (this.permanentDelegate) {
|
|
1392
|
+
instructions.push(
|
|
1393
|
+
initializePermanentDelegateInstruction({
|
|
1394
|
+
mint: mintAddress,
|
|
1395
|
+
delegate: this.permanentDelegate,
|
|
1396
|
+
programId: this.programId
|
|
1397
|
+
})
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
if (this.transferHook) {
|
|
1401
|
+
instructions.push(
|
|
1402
|
+
initializeTransferHookInstruction({
|
|
1403
|
+
mint: mintAddress,
|
|
1404
|
+
authority: this.transferHook.authority,
|
|
1405
|
+
hookProgramId: this.transferHook.programId,
|
|
1406
|
+
programId: this.programId
|
|
1407
|
+
})
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
instructions.push(
|
|
1411
|
+
initializeMintInstruction({
|
|
1412
|
+
mint: mintAddress,
|
|
1413
|
+
decimals: this.decimals,
|
|
1414
|
+
mintAuthority: this.mintAuthority,
|
|
1415
|
+
freezeAuthority: this.freezeAuthority ?? void 0,
|
|
1416
|
+
programId: this.programId
|
|
1417
|
+
})
|
|
1418
|
+
);
|
|
1419
|
+
if (this.metadata) {
|
|
1420
|
+
instructions.push(
|
|
1421
|
+
initializeTokenMetadataInstruction({
|
|
1422
|
+
mint: mintAddress,
|
|
1423
|
+
updateAuthority: this.mintAuthority,
|
|
1424
|
+
mintAuthority: this.mintAuthority,
|
|
1425
|
+
name: this.metadata.name,
|
|
1426
|
+
symbol: this.metadata.symbol,
|
|
1427
|
+
uri: this.metadata.uri,
|
|
1428
|
+
additionalMetadata: this.metadata.additionalMetadata,
|
|
1429
|
+
programId: this.programId
|
|
1430
|
+
})
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
let ataAddress;
|
|
1434
|
+
if (this.initialSupply) {
|
|
1435
|
+
ataAddress = getAssociatedTokenAddressSync({
|
|
1436
|
+
wallet: this.initialSupply.destination,
|
|
1437
|
+
mint: mintAddress,
|
|
1438
|
+
programId: this.programId
|
|
1439
|
+
});
|
|
1440
|
+
instructions.push(
|
|
1441
|
+
createAssociatedTokenAccountIdempotentInstruction({
|
|
1442
|
+
payer,
|
|
1443
|
+
associatedToken: ataAddress,
|
|
1444
|
+
owner: this.initialSupply.destination,
|
|
1445
|
+
mint: mintAddress,
|
|
1446
|
+
programId: this.programId
|
|
1447
|
+
})
|
|
1448
|
+
);
|
|
1449
|
+
instructions.push(
|
|
1450
|
+
mintToInstruction({
|
|
1451
|
+
mint: mintAddress,
|
|
1452
|
+
destination: ataAddress,
|
|
1453
|
+
authority: this.mintAuthority,
|
|
1454
|
+
amount: this.initialSupply.amount,
|
|
1455
|
+
programId: this.programId
|
|
1456
|
+
})
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
const configHashPrefix = await this.client.getConfigHashPrefix();
|
|
1460
|
+
const builder = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).setConfigHashPrefix(configHashPrefix);
|
|
1461
|
+
for (const instruction of instructions) {
|
|
1462
|
+
builder.addInstruction(instruction);
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
transaction: builder.build(),
|
|
1466
|
+
mintAddress,
|
|
1467
|
+
ataAddress
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Builds, signs, and sends the mint transaction.
|
|
1472
|
+
*
|
|
1473
|
+
* @param options - Send options including signers
|
|
1474
|
+
* @returns Send result with signature and addresses
|
|
1475
|
+
*
|
|
1476
|
+
* @example
|
|
1477
|
+
* ```typescript
|
|
1478
|
+
* const { signature, mintAddress } = await builder.send({
|
|
1479
|
+
* payer: authority,
|
|
1480
|
+
* mintKeypair,
|
|
1481
|
+
* validFrom: BigInt(Date.now()),
|
|
1482
|
+
* signers: [authorityKeypair, mintKeypair],
|
|
1483
|
+
* });
|
|
1484
|
+
* ```
|
|
1485
|
+
*/
|
|
1486
|
+
async send(options) {
|
|
1487
|
+
const { signers, ...buildOptions } = options;
|
|
1488
|
+
if (signers.length === 0) {
|
|
1489
|
+
throw new Error("At least one signer is required");
|
|
1490
|
+
}
|
|
1491
|
+
const { transaction, mintAddress, ataAddress } = await this.build(buildOptions);
|
|
1492
|
+
let signedTx = transaction;
|
|
1493
|
+
for (const signer of signers) {
|
|
1494
|
+
signedTx = signedTx.sign(signer);
|
|
1495
|
+
}
|
|
1496
|
+
const result = await this.client.sendAndConfirmTransaction(
|
|
1497
|
+
signedTx.serialize()
|
|
1498
|
+
);
|
|
1499
|
+
return {
|
|
1500
|
+
signature: result.signature,
|
|
1501
|
+
mintAddress,
|
|
1502
|
+
ataAddress
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Returns the list of extensions that will be enabled.
|
|
1507
|
+
*/
|
|
1508
|
+
getExtensions() {
|
|
1509
|
+
const extensions = [];
|
|
1510
|
+
if (this.metadata) {
|
|
1511
|
+
extensions.push(18 /* MetadataPointer */);
|
|
1512
|
+
extensions.push(19 /* TokenMetadata */);
|
|
1513
|
+
}
|
|
1514
|
+
if (this.transferFee) {
|
|
1515
|
+
extensions.push(1 /* TransferFeeConfig */);
|
|
1516
|
+
}
|
|
1517
|
+
if (this.nonTransferable) {
|
|
1518
|
+
extensions.push(9 /* NonTransferable */);
|
|
1519
|
+
}
|
|
1520
|
+
if (this.permanentDelegate) {
|
|
1521
|
+
extensions.push(12 /* PermanentDelegate */);
|
|
1522
|
+
}
|
|
1523
|
+
if (this.transferHook) {
|
|
1524
|
+
extensions.push(14 /* TransferHook */);
|
|
1525
|
+
}
|
|
1526
|
+
return extensions;
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
function readU16LE({ data, offset }) {
|
|
1530
|
+
return data[offset] | data[offset + 1] << 8;
|
|
762
1531
|
}
|
|
763
|
-
function
|
|
764
|
-
|
|
765
|
-
mint,
|
|
766
|
-
programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID)
|
|
767
|
-
}) {
|
|
768
|
-
return findAssociatedTokenAddress({ wallet, mint, programId }).address;
|
|
1532
|
+
function readU32LE({ data, offset }) {
|
|
1533
|
+
return (data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24) >>> 0;
|
|
769
1534
|
}
|
|
770
1535
|
function readU64LE({ data, offset }) {
|
|
771
1536
|
let value = 0n;
|
|
@@ -774,13 +1539,15 @@ function readU64LE({ data, offset }) {
|
|
|
774
1539
|
}
|
|
775
1540
|
return value;
|
|
776
1541
|
}
|
|
777
|
-
function readU32LE({ data, offset }) {
|
|
778
|
-
return (data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24) >>> 0;
|
|
779
|
-
}
|
|
780
1542
|
function readCOptionPubkey({
|
|
781
1543
|
data,
|
|
782
1544
|
offset
|
|
783
1545
|
}) {
|
|
1546
|
+
if (data.length < offset + 4 + PUBKEY_SIZE) {
|
|
1547
|
+
throw SplTokenError.invalidExtension({
|
|
1548
|
+
reason: `COption<Pubkey> out of bounds: need ${offset + 4 + PUBKEY_SIZE} bytes, got ${data.length}`
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
784
1551
|
const tag = readU32LE({ data, offset });
|
|
785
1552
|
if (tag === COPTION_SOME) {
|
|
786
1553
|
const pubkeyBytes = data.slice(offset + 4, offset + 4 + PUBKEY_SIZE);
|
|
@@ -788,122 +1555,192 @@ function readCOptionPubkey({
|
|
|
788
1555
|
}
|
|
789
1556
|
return null;
|
|
790
1557
|
}
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1558
|
+
function readOptionalNonZeroPubkey({
|
|
1559
|
+
data,
|
|
1560
|
+
offset
|
|
1561
|
+
}) {
|
|
1562
|
+
const pubkeyBytes = data.slice(offset, offset + PUBKEY_SIZE);
|
|
1563
|
+
const isZero = pubkeyBytes.every((byte) => byte === 0);
|
|
1564
|
+
return isZero ? null : PublicKey.fromBytes(pubkeyBytes);
|
|
1565
|
+
}
|
|
1566
|
+
function readPubkey({ data, offset }) {
|
|
1567
|
+
return PublicKey.fromBytes(data.slice(offset, offset + PUBKEY_SIZE));
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/state/extensions.ts
|
|
1571
|
+
function parseTlvExtensions({
|
|
1572
|
+
data,
|
|
1573
|
+
extensionsOffset
|
|
1574
|
+
}) {
|
|
1575
|
+
const extensions = [];
|
|
1576
|
+
let offset = extensionsOffset;
|
|
1577
|
+
while (offset + TLV_TYPE_SIZE + TLV_LENGTH_SIZE <= data.length) {
|
|
1578
|
+
const type = readU16LE({ data, offset });
|
|
1579
|
+
const length = readU16LE({ data, offset: offset + TLV_TYPE_SIZE });
|
|
1580
|
+
if (type === 0 /* Uninitialized */ && length === 0) {
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
const dataStart = offset + TLV_TYPE_SIZE + TLV_LENGTH_SIZE;
|
|
1584
|
+
const dataEnd = dataStart + length;
|
|
1585
|
+
if (dataEnd > data.length) {
|
|
1586
|
+
throw SplTokenError.invalidExtension({
|
|
1587
|
+
reason: `Extension data extends beyond buffer: type=${type}, length=${length}`
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
extensions.push({
|
|
1591
|
+
type,
|
|
1592
|
+
data: data.slice(dataStart, dataEnd)
|
|
795
1593
|
});
|
|
1594
|
+
offset = dataEnd;
|
|
796
1595
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1596
|
+
return extensions;
|
|
1597
|
+
}
|
|
1598
|
+
function getMintExtensions({ data }) {
|
|
1599
|
+
if (data.length <= TOKEN_ACCOUNT_SIZE) {
|
|
1600
|
+
return [];
|
|
1601
|
+
}
|
|
1602
|
+
const accountType = data[TOKEN_ACCOUNT_SIZE];
|
|
1603
|
+
if (accountType !== 1 /* Mint */) {
|
|
1604
|
+
return [];
|
|
804
1605
|
}
|
|
1606
|
+
return parseTlvExtensions({ data, extensionsOffset: MINT_EXTENSIONS_OFFSET });
|
|
1607
|
+
}
|
|
1608
|
+
function getTokenAccountExtensions({ data }) {
|
|
1609
|
+
if (data.length <= TOKEN_ACCOUNT_SIZE) {
|
|
1610
|
+
return [];
|
|
1611
|
+
}
|
|
1612
|
+
const accountType = data[TOKEN_ACCOUNT_SIZE];
|
|
1613
|
+
if (accountType !== 2 /* Account */) {
|
|
1614
|
+
return [];
|
|
1615
|
+
}
|
|
1616
|
+
return parseTlvExtensions({
|
|
1617
|
+
data,
|
|
1618
|
+
extensionsOffset: TOKEN_ACCOUNT_EXTENSIONS_OFFSET
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// src/state/extension-parsers.ts
|
|
1623
|
+
var TRANSFER_FEE_CONFIG_SIZE = 108;
|
|
1624
|
+
var TRANSFER_FEE_AMOUNT_SIZE = 8;
|
|
1625
|
+
var METADATA_POINTER_SIZE = 64;
|
|
1626
|
+
var PUBKEY_SIZE2 = 32;
|
|
1627
|
+
var PERMANENT_DELEGATE_SIZE = 32;
|
|
1628
|
+
var TRANSFER_HOOK_SIZE = 64;
|
|
1629
|
+
var TOKEN_METADATA_MAX_SIZE = 65535;
|
|
1630
|
+
function parseTransferFee({ data, offset }) {
|
|
1631
|
+
const epoch = readU64LE({ data, offset });
|
|
1632
|
+
const maximumFee = readU64LE({ data, offset: offset + 8 });
|
|
1633
|
+
const transferFeeBasisPoints = readU16LE({ data, offset: offset + 16 });
|
|
805
1634
|
return {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
isInitialized,
|
|
810
|
-
mintAuthority,
|
|
811
|
-
freezeAuthority
|
|
1635
|
+
epoch,
|
|
1636
|
+
maximumFee,
|
|
1637
|
+
transferFeeBasisPoints
|
|
812
1638
|
};
|
|
813
1639
|
}
|
|
814
|
-
function
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1640
|
+
function parseTransferFeeConfigExtension({
|
|
1641
|
+
data
|
|
1642
|
+
}) {
|
|
1643
|
+
if (data.length < TRANSFER_FEE_CONFIG_SIZE) {
|
|
1644
|
+
throw SplTokenError.invalidExtension({
|
|
1645
|
+
reason: `TransferFeeConfig too short: expected ${TRANSFER_FEE_CONFIG_SIZE} bytes, got ${data.length}`
|
|
1646
|
+
});
|
|
818
1647
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1648
|
+
const transferFeeConfigAuthority = readOptionalNonZeroPubkey({
|
|
1649
|
+
data,
|
|
1650
|
+
offset: 0
|
|
1651
|
+
});
|
|
1652
|
+
const withdrawWithheldAuthority = readOptionalNonZeroPubkey({
|
|
1653
|
+
data,
|
|
1654
|
+
offset: 32
|
|
1655
|
+
});
|
|
1656
|
+
const withheldAmount = readU64LE({ data, offset: 64 });
|
|
1657
|
+
const olderTransferFee = parseTransferFee({ data, offset: 72 });
|
|
1658
|
+
const newerTransferFee = parseTransferFee({ data, offset: 90 });
|
|
1659
|
+
return {
|
|
1660
|
+
transferFeeConfigAuthority,
|
|
1661
|
+
withdrawWithheldAuthority,
|
|
1662
|
+
withheldAmount,
|
|
1663
|
+
olderTransferFee,
|
|
1664
|
+
newerTransferFee
|
|
1665
|
+
};
|
|
823
1666
|
}
|
|
824
|
-
function
|
|
825
|
-
data
|
|
826
|
-
offset
|
|
1667
|
+
function parseTransferFeeAmountExtension({
|
|
1668
|
+
data
|
|
827
1669
|
}) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1670
|
+
if (data.length < TRANSFER_FEE_AMOUNT_SIZE) {
|
|
1671
|
+
throw SplTokenError.invalidExtension({
|
|
1672
|
+
reason: `TransferFeeAmount too short: expected ${TRANSFER_FEE_AMOUNT_SIZE} bytes, got ${data.length}`
|
|
1673
|
+
});
|
|
832
1674
|
}
|
|
833
|
-
return
|
|
1675
|
+
return {
|
|
1676
|
+
withheldAmount: readU64LE({ data, offset: 0 })
|
|
1677
|
+
};
|
|
834
1678
|
}
|
|
835
|
-
function
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1679
|
+
function parseNonTransferableExtension({
|
|
1680
|
+
data
|
|
1681
|
+
}) {
|
|
1682
|
+
if (data.length !== 0) {
|
|
1683
|
+
throw SplTokenError.invalidExtension({
|
|
1684
|
+
reason: `NonTransferable has unexpected length: expected 0 bytes, got ${data.length}`
|
|
1685
|
+
});
|
|
839
1686
|
}
|
|
840
|
-
return
|
|
1687
|
+
return { isNonTransferable: true };
|
|
841
1688
|
}
|
|
842
|
-
function
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1689
|
+
function parseNonTransferableAccountExtension({
|
|
1690
|
+
data
|
|
1691
|
+
}) {
|
|
1692
|
+
if (data.length !== 0) {
|
|
1693
|
+
throw SplTokenError.invalidExtension({
|
|
1694
|
+
reason: `NonTransferableAccount has unexpected length: expected 0 bytes, got ${data.length}`
|
|
846
1695
|
});
|
|
847
1696
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
)
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const stateValue = data[TOKEN_ACCOUNT_STATE_OFFSET];
|
|
858
|
-
if (stateValue > 2) {
|
|
859
|
-
throw SplTokenError.invalidTokenAccount({ reason: `Invalid account state: ${stateValue}` });
|
|
1697
|
+
return { isNonTransferableAccount: true };
|
|
1698
|
+
}
|
|
1699
|
+
function parsePermanentDelegateExtension({
|
|
1700
|
+
data
|
|
1701
|
+
}) {
|
|
1702
|
+
if (data.length < PERMANENT_DELEGATE_SIZE) {
|
|
1703
|
+
throw SplTokenError.invalidExtension({
|
|
1704
|
+
reason: `PermanentDelegate too short: expected ${PERMANENT_DELEGATE_SIZE} bytes, got ${data.length}`
|
|
1705
|
+
});
|
|
860
1706
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1707
|
+
return { delegate: readOptionalNonZeroPubkey({ data, offset: 0 }) };
|
|
1708
|
+
}
|
|
1709
|
+
function parseTransferHookExtension({ data }) {
|
|
1710
|
+
if (data.length < TRANSFER_HOOK_SIZE) {
|
|
1711
|
+
throw SplTokenError.invalidExtension({
|
|
1712
|
+
reason: `TransferHook too short: expected ${TRANSFER_HOOK_SIZE} bytes, got ${data.length}`
|
|
1713
|
+
});
|
|
864
1714
|
}
|
|
865
|
-
const isNative = readCOptionU64({ data, offset: TOKEN_ACCOUNT_IS_NATIVE_OFFSET });
|
|
866
|
-
const delegatedAmount = readU64LE2({ data, offset: TOKEN_ACCOUNT_DELEGATED_AMOUNT_OFFSET });
|
|
867
|
-
const closeAuthority = readCOptionPubkey2({ data, offset: TOKEN_ACCOUNT_CLOSE_AUTHORITY_OFFSET });
|
|
868
1715
|
return {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
owner,
|
|
872
|
-
amount,
|
|
873
|
-
delegate,
|
|
874
|
-
state,
|
|
875
|
-
isNative,
|
|
876
|
-
delegatedAmount,
|
|
877
|
-
closeAuthority
|
|
1716
|
+
authority: readOptionalNonZeroPubkey({ data, offset: 0 }),
|
|
1717
|
+
programId: readOptionalNonZeroPubkey({ data, offset: 32 })
|
|
878
1718
|
};
|
|
879
1719
|
}
|
|
880
|
-
function
|
|
881
|
-
|
|
882
|
-
}
|
|
883
|
-
function readU32LE3({ data, offset }) {
|
|
884
|
-
return (data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24) >>> 0;
|
|
885
|
-
}
|
|
886
|
-
function readCOptionPubkey3({
|
|
887
|
-
data,
|
|
888
|
-
offset
|
|
1720
|
+
function parseMetadataPointerExtensionData({
|
|
1721
|
+
data
|
|
889
1722
|
}) {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1723
|
+
if (data.length < METADATA_POINTER_SIZE) {
|
|
1724
|
+
throw SplTokenError.invalidExtension({
|
|
1725
|
+
reason: `MetadataPointer too short: expected ${METADATA_POINTER_SIZE} bytes, got ${data.length}`
|
|
1726
|
+
});
|
|
894
1727
|
}
|
|
895
|
-
|
|
1728
|
+
const authority = readOptionalNonZeroPubkey({ data, offset: 0 });
|
|
1729
|
+
const metadataAddress = readOptionalNonZeroPubkey({ data, offset: 32 });
|
|
1730
|
+
return { authority, metadataAddress };
|
|
896
1731
|
}
|
|
897
1732
|
function readLengthPrefixedString({
|
|
898
1733
|
data,
|
|
899
1734
|
offset
|
|
900
1735
|
}) {
|
|
901
1736
|
if (offset + 4 > data.length) {
|
|
902
|
-
throw SplTokenError.
|
|
1737
|
+
throw SplTokenError.invalidExtension({
|
|
1738
|
+
reason: "String length prefix extends beyond data"
|
|
1739
|
+
});
|
|
903
1740
|
}
|
|
904
|
-
const length =
|
|
1741
|
+
const length = readU32LE({ data, offset });
|
|
905
1742
|
if (offset + 4 + length > data.length) {
|
|
906
|
-
throw SplTokenError.
|
|
1743
|
+
throw SplTokenError.invalidExtension({
|
|
907
1744
|
reason: `String data extends beyond buffer: need ${length} bytes at offset ${offset + 4}`
|
|
908
1745
|
});
|
|
909
1746
|
}
|
|
@@ -911,47 +1748,140 @@ function readLengthPrefixedString({
|
|
|
911
1748
|
try {
|
|
912
1749
|
const str = new TextDecoder().decode(stringBytes);
|
|
913
1750
|
return [str, 4 + length];
|
|
914
|
-
} catch
|
|
915
|
-
throw SplTokenError.
|
|
1751
|
+
} catch {
|
|
1752
|
+
throw SplTokenError.invalidExtension({
|
|
916
1753
|
reason: `Invalid UTF-8 string at offset ${offset}`
|
|
917
1754
|
});
|
|
918
1755
|
}
|
|
919
1756
|
}
|
|
920
|
-
function
|
|
1757
|
+
function parseTokenMetadataExtensionData({ data }) {
|
|
1758
|
+
if (data.length < 64) {
|
|
1759
|
+
throw SplTokenError.invalidExtension({
|
|
1760
|
+
reason: `TokenMetadata too short: expected at least 64 bytes, got ${data.length}`
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
if (data.length > TOKEN_METADATA_MAX_SIZE) {
|
|
1764
|
+
throw SplTokenError.invalidExtension({
|
|
1765
|
+
reason: `TokenMetadata too large: maximum ${TOKEN_METADATA_MAX_SIZE} bytes, got ${data.length}`
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
let offset = 0;
|
|
1769
|
+
const updateAuthority = readOptionalNonZeroPubkey({ data, offset });
|
|
1770
|
+
offset += PUBKEY_SIZE2;
|
|
1771
|
+
const mint = readPubkey({ data, offset });
|
|
1772
|
+
offset += PUBKEY_SIZE2;
|
|
1773
|
+
const [name, nameLen] = readLengthPrefixedString({ data, offset });
|
|
1774
|
+
offset += nameLen;
|
|
1775
|
+
const [symbol, symbolLen] = readLengthPrefixedString({ data, offset });
|
|
1776
|
+
offset += symbolLen;
|
|
1777
|
+
const [uri, uriLen] = readLengthPrefixedString({ data, offset });
|
|
1778
|
+
offset += uriLen;
|
|
1779
|
+
const additionalMetadata = [];
|
|
1780
|
+
if (offset + 4 <= data.length) {
|
|
1781
|
+
const numEntries = readU32LE({ data, offset });
|
|
1782
|
+
offset += 4;
|
|
1783
|
+
for (let i = 0; i < numEntries && offset < data.length; i++) {
|
|
1784
|
+
const [key, keyLen] = readLengthPrefixedString({ data, offset });
|
|
1785
|
+
offset += keyLen;
|
|
1786
|
+
const [value, valueLen] = readLengthPrefixedString({ data, offset });
|
|
1787
|
+
offset += valueLen;
|
|
1788
|
+
additionalMetadata.push([key, value]);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
return {
|
|
1792
|
+
updateAuthority,
|
|
1793
|
+
mint,
|
|
1794
|
+
name,
|
|
1795
|
+
symbol,
|
|
1796
|
+
uri,
|
|
1797
|
+
additionalMetadata
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
function parseExtension({ extension }) {
|
|
1801
|
+
switch (extension.type) {
|
|
1802
|
+
case 1 /* TransferFeeConfig */:
|
|
1803
|
+
return {
|
|
1804
|
+
type: 1 /* TransferFeeConfig */,
|
|
1805
|
+
value: parseTransferFeeConfigExtension({ data: extension.data })
|
|
1806
|
+
};
|
|
1807
|
+
case 2 /* TransferFeeAmount */:
|
|
1808
|
+
return {
|
|
1809
|
+
type: 2 /* TransferFeeAmount */,
|
|
1810
|
+
value: parseTransferFeeAmountExtension({ data: extension.data })
|
|
1811
|
+
};
|
|
1812
|
+
case 9 /* NonTransferable */:
|
|
1813
|
+
return {
|
|
1814
|
+
type: 9 /* NonTransferable */,
|
|
1815
|
+
value: parseNonTransferableExtension({ data: extension.data })
|
|
1816
|
+
};
|
|
1817
|
+
case 13 /* NonTransferableAccount */:
|
|
1818
|
+
return {
|
|
1819
|
+
type: 13 /* NonTransferableAccount */,
|
|
1820
|
+
value: parseNonTransferableAccountExtension({ data: extension.data })
|
|
1821
|
+
};
|
|
1822
|
+
case 18 /* MetadataPointer */:
|
|
1823
|
+
return {
|
|
1824
|
+
type: 18 /* MetadataPointer */,
|
|
1825
|
+
value: parseMetadataPointerExtensionData({ data: extension.data })
|
|
1826
|
+
};
|
|
1827
|
+
case 19 /* TokenMetadata */:
|
|
1828
|
+
return {
|
|
1829
|
+
type: 19 /* TokenMetadata */,
|
|
1830
|
+
value: parseTokenMetadataExtensionData({ data: extension.data })
|
|
1831
|
+
};
|
|
1832
|
+
case 12 /* PermanentDelegate */:
|
|
1833
|
+
return {
|
|
1834
|
+
type: 12 /* PermanentDelegate */,
|
|
1835
|
+
value: parsePermanentDelegateExtension({ data: extension.data })
|
|
1836
|
+
};
|
|
1837
|
+
case 14 /* TransferHook */:
|
|
1838
|
+
return {
|
|
1839
|
+
type: 14 /* TransferHook */,
|
|
1840
|
+
value: parseTransferHookExtension({ data: extension.data })
|
|
1841
|
+
};
|
|
1842
|
+
default:
|
|
1843
|
+
return null;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
function getMintExtensionStates({ data }) {
|
|
1847
|
+
return getMintExtensions({ data }).map((extension) => parseExtension({ extension })).filter((extension) => extension !== null);
|
|
1848
|
+
}
|
|
1849
|
+
function getTokenAccountExtensionStates({ data }) {
|
|
1850
|
+
return getTokenAccountExtensions({ data }).map((extension) => parseExtension({ extension })).filter((extension) => extension !== null);
|
|
1851
|
+
}
|
|
1852
|
+
function readLengthPrefixedString2({
|
|
921
1853
|
data,
|
|
922
|
-
|
|
1854
|
+
offset
|
|
923
1855
|
}) {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1856
|
+
if (offset + 4 > data.length) {
|
|
1857
|
+
throw SplTokenError.invalidMetadata({
|
|
1858
|
+
reason: "String length prefix extends beyond data"
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
const length = readU32LE({ data, offset });
|
|
1862
|
+
if (offset + 4 + length > data.length) {
|
|
1863
|
+
throw SplTokenError.invalidMetadata({
|
|
1864
|
+
reason: `String data extends beyond buffer: need ${length} bytes at offset ${offset + 4}`
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
const stringBytes = data.slice(offset + 4, offset + 4 + length);
|
|
1868
|
+
try {
|
|
1869
|
+
const str = new TextDecoder().decode(stringBytes);
|
|
1870
|
+
return [str, 4 + length];
|
|
1871
|
+
} catch {
|
|
1872
|
+
throw SplTokenError.invalidMetadata({
|
|
1873
|
+
reason: `Invalid UTF-8 string at offset ${offset}`
|
|
942
1874
|
});
|
|
943
|
-
offset = dataEnd;
|
|
944
1875
|
}
|
|
945
|
-
return extensions;
|
|
946
1876
|
}
|
|
947
1877
|
function parseMetadataPointer({ data }) {
|
|
948
|
-
if (data.length <
|
|
1878
|
+
if (data.length < 64) {
|
|
949
1879
|
throw SplTokenError.invalidExtension({
|
|
950
|
-
reason: `MetadataPointer too short: expected
|
|
1880
|
+
reason: `MetadataPointer too short: expected 64 bytes, got ${data.length}`
|
|
951
1881
|
});
|
|
952
1882
|
}
|
|
953
|
-
const authority =
|
|
954
|
-
const metadataAddress =
|
|
1883
|
+
const authority = readOptionalNonZeroPubkey({ data, offset: 0 });
|
|
1884
|
+
const metadataAddress = readOptionalNonZeroPubkey({ data, offset: 32 });
|
|
955
1885
|
return { authority, metadataAddress };
|
|
956
1886
|
}
|
|
957
1887
|
function parseTokenMetadataExtension({
|
|
@@ -969,20 +1899,20 @@ function parseTokenMetadataExtension({
|
|
|
969
1899
|
const updateAuthority = isZeroAuthority ? null : PublicKey.fromBytes(updateAuthorityBytes);
|
|
970
1900
|
offset += PUBKEY_SIZE;
|
|
971
1901
|
offset += PUBKEY_SIZE;
|
|
972
|
-
const [name, nameLen] =
|
|
1902
|
+
const [name, nameLen] = readLengthPrefixedString2({ data, offset });
|
|
973
1903
|
offset += nameLen;
|
|
974
|
-
const [symbol, symbolLen] =
|
|
1904
|
+
const [symbol, symbolLen] = readLengthPrefixedString2({ data, offset });
|
|
975
1905
|
offset += symbolLen;
|
|
976
|
-
const [uri, uriLen] =
|
|
1906
|
+
const [uri, uriLen] = readLengthPrefixedString2({ data, offset });
|
|
977
1907
|
offset += uriLen;
|
|
978
1908
|
const additionalMetadata = [];
|
|
979
1909
|
if (offset + 4 <= data.length) {
|
|
980
|
-
const numEntries =
|
|
1910
|
+
const numEntries = readU32LE({ data, offset });
|
|
981
1911
|
offset += 4;
|
|
982
1912
|
for (let i = 0; i < numEntries && offset < data.length; i++) {
|
|
983
|
-
const [key, keyLen] =
|
|
1913
|
+
const [key, keyLen] = readLengthPrefixedString2({ data, offset });
|
|
984
1914
|
offset += keyLen;
|
|
985
|
-
const [value, valueLen] =
|
|
1915
|
+
const [value, valueLen] = readLengthPrefixedString2({ data, offset });
|
|
986
1916
|
offset += valueLen;
|
|
987
1917
|
additionalMetadata.push([key, value]);
|
|
988
1918
|
}
|
|
@@ -1000,14 +1930,17 @@ function parseTokenMetadata({
|
|
|
1000
1930
|
mint,
|
|
1001
1931
|
data
|
|
1002
1932
|
}) {
|
|
1003
|
-
if (data.length <=
|
|
1933
|
+
if (data.length <= TOKEN_ACCOUNT_SIZE) {
|
|
1004
1934
|
return null;
|
|
1005
1935
|
}
|
|
1006
|
-
const accountType = data[
|
|
1936
|
+
const accountType = data[TOKEN_ACCOUNT_SIZE];
|
|
1007
1937
|
if (accountType !== 1 /* Mint */) {
|
|
1008
1938
|
return null;
|
|
1009
1939
|
}
|
|
1010
|
-
const extensions =
|
|
1940
|
+
const extensions = parseTlvExtensions({
|
|
1941
|
+
data,
|
|
1942
|
+
extensionsOffset: MINT_EXTENSIONS_OFFSET
|
|
1943
|
+
});
|
|
1011
1944
|
const metadataExt = extensions.find((ext) => ext.type === 19 /* TokenMetadata */);
|
|
1012
1945
|
if (!metadataExt) {
|
|
1013
1946
|
return null;
|
|
@@ -1017,29 +1950,321 @@ function parseTokenMetadata({
|
|
|
1017
1950
|
function parseMetadataPointerExtension({
|
|
1018
1951
|
data
|
|
1019
1952
|
}) {
|
|
1020
|
-
if (data.length <=
|
|
1953
|
+
if (data.length <= TOKEN_ACCOUNT_SIZE) {
|
|
1021
1954
|
return null;
|
|
1022
1955
|
}
|
|
1023
|
-
const accountType = data[
|
|
1956
|
+
const accountType = data[TOKEN_ACCOUNT_SIZE];
|
|
1024
1957
|
if (accountType !== 1 /* Mint */) {
|
|
1025
1958
|
return null;
|
|
1026
1959
|
}
|
|
1027
|
-
const extensions =
|
|
1960
|
+
const extensions = parseTlvExtensions({
|
|
1961
|
+
data,
|
|
1962
|
+
extensionsOffset: MINT_EXTENSIONS_OFFSET
|
|
1963
|
+
});
|
|
1028
1964
|
const pointerExt = extensions.find((ext) => ext.type === 18 /* MetadataPointer */);
|
|
1029
1965
|
if (!pointerExt) {
|
|
1030
1966
|
return null;
|
|
1031
1967
|
}
|
|
1032
1968
|
return parseMetadataPointer({ data: pointerExt.data });
|
|
1033
1969
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1970
|
+
|
|
1971
|
+
// src/state/mint.ts
|
|
1972
|
+
function parseMintAccount({ address, data }) {
|
|
1973
|
+
if (data.length < MINT_SIZE) {
|
|
1974
|
+
throw SplTokenError.invalidMint({
|
|
1975
|
+
reason: `Account data too short: expected at least ${MINT_SIZE} bytes, got ${data.length}`
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
const mintAuthority = readCOptionPubkey({ data, offset: 0 });
|
|
1979
|
+
const supply = readU64LE({ data, offset: MINT_SUPPLY_OFFSET });
|
|
1980
|
+
const decimals = data[MINT_DECIMALS_OFFSET];
|
|
1981
|
+
const isInitialized = data[MINT_IS_INITIALIZED_OFFSET] !== 0;
|
|
1982
|
+
const freezeAuthority = readCOptionPubkey({
|
|
1983
|
+
data,
|
|
1984
|
+
offset: MINT_FREEZE_AUTHORITY_OFFSET
|
|
1985
|
+
});
|
|
1986
|
+
if (!isInitialized) {
|
|
1987
|
+
throw SplTokenError.accountNotInitialized({ address: address.toString() });
|
|
1988
|
+
}
|
|
1989
|
+
return {
|
|
1990
|
+
address,
|
|
1991
|
+
supply,
|
|
1992
|
+
decimals,
|
|
1993
|
+
isInitialized,
|
|
1994
|
+
mintAuthority,
|
|
1995
|
+
freezeAuthority
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/state/token-account.ts
|
|
2000
|
+
function readCOptionU64({ data, offset }) {
|
|
2001
|
+
const tag = readU32LE({ data, offset });
|
|
2002
|
+
if (tag === COPTION_SOME) {
|
|
2003
|
+
return readU64LE({ data, offset: offset + 4 });
|
|
2004
|
+
}
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
function parseTokenAccount({ address, data }) {
|
|
2008
|
+
if (data.length < TOKEN_ACCOUNT_SIZE) {
|
|
2009
|
+
throw SplTokenError.invalidTokenAccount({
|
|
2010
|
+
reason: `Account data too short: expected at least ${TOKEN_ACCOUNT_SIZE} bytes, got ${data.length}`
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
const mint = readPubkey({ data, offset: TOKEN_ACCOUNT_MINT_OFFSET });
|
|
2014
|
+
const owner = readPubkey({ data, offset: TOKEN_ACCOUNT_OWNER_OFFSET });
|
|
2015
|
+
const amount = readU64LE({ data, offset: TOKEN_ACCOUNT_AMOUNT_OFFSET });
|
|
2016
|
+
const delegate = readCOptionPubkey({
|
|
2017
|
+
data,
|
|
2018
|
+
offset: TOKEN_ACCOUNT_DELEGATE_OFFSET
|
|
2019
|
+
});
|
|
2020
|
+
const stateValue = data[TOKEN_ACCOUNT_STATE_OFFSET];
|
|
2021
|
+
if (stateValue > 2) {
|
|
2022
|
+
throw SplTokenError.invalidTokenAccount({
|
|
2023
|
+
reason: `Invalid account state: ${stateValue}`
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
const state = stateValue;
|
|
2027
|
+
if (state === 0 /* Uninitialized */) {
|
|
2028
|
+
throw SplTokenError.accountNotInitialized({ address: address.toString() });
|
|
2029
|
+
}
|
|
2030
|
+
const isNative = readCOptionU64({
|
|
2031
|
+
data,
|
|
2032
|
+
offset: TOKEN_ACCOUNT_IS_NATIVE_OFFSET
|
|
2033
|
+
});
|
|
2034
|
+
const delegatedAmount = readU64LE({
|
|
2035
|
+
data,
|
|
2036
|
+
offset: TOKEN_ACCOUNT_DELEGATED_AMOUNT_OFFSET
|
|
2037
|
+
});
|
|
2038
|
+
const closeAuthority = readCOptionPubkey({
|
|
2039
|
+
data,
|
|
2040
|
+
offset: TOKEN_ACCOUNT_CLOSE_AUTHORITY_OFFSET
|
|
2041
|
+
});
|
|
2042
|
+
return {
|
|
2043
|
+
address,
|
|
2044
|
+
mint,
|
|
2045
|
+
owner,
|
|
2046
|
+
amount,
|
|
2047
|
+
delegate,
|
|
2048
|
+
state,
|
|
2049
|
+
isNative,
|
|
2050
|
+
delegatedAmount,
|
|
2051
|
+
closeAuthority
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// src/transfer-hook/extra-account-meta.ts
|
|
2056
|
+
var EXTRA_ACCOUNT_META_SIZE = 35;
|
|
2057
|
+
function parseExtraAccountMeta(data, offset) {
|
|
2058
|
+
return {
|
|
2059
|
+
discriminator: data[offset],
|
|
2060
|
+
addressConfig: data.slice(offset + 1, offset + 33),
|
|
2061
|
+
isSigner: data[offset + 33] !== 0,
|
|
2062
|
+
isWritable: data[offset + 34] !== 0
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
var EXECUTE_DISCRIMINATOR = sha256(
|
|
2066
|
+
new TextEncoder().encode("rialo-s-spl-transfer-hook-interface:execute")
|
|
2067
|
+
).slice(0, 8);
|
|
2068
|
+
var TLV_HEADER_SIZE2 = 16;
|
|
2069
|
+
function parseExtraAccountMetaList(data) {
|
|
2070
|
+
if (data.length < TLV_HEADER_SIZE2) {
|
|
1036
2071
|
return [];
|
|
1037
2072
|
}
|
|
1038
|
-
const
|
|
1039
|
-
|
|
2073
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
2074
|
+
const count = view.getUint32(12, true);
|
|
2075
|
+
if (count === 0) {
|
|
1040
2076
|
return [];
|
|
1041
2077
|
}
|
|
1042
|
-
|
|
2078
|
+
const entries = [];
|
|
2079
|
+
let cursor = TLV_HEADER_SIZE2;
|
|
2080
|
+
for (let i = 0; i < count; i++) {
|
|
2081
|
+
if (cursor + EXTRA_ACCOUNT_META_SIZE > data.length) {
|
|
2082
|
+
break;
|
|
2083
|
+
}
|
|
2084
|
+
entries.push(parseExtraAccountMeta(data, cursor));
|
|
2085
|
+
cursor += EXTRA_ACCOUNT_META_SIZE;
|
|
2086
|
+
}
|
|
2087
|
+
return entries;
|
|
2088
|
+
}
|
|
2089
|
+
function buildExecuteInstructionData(amount) {
|
|
2090
|
+
const data = new Uint8Array(16);
|
|
2091
|
+
data.set(EXECUTE_DISCRIMINATOR, 0);
|
|
2092
|
+
writeU64LE({ data, offset: 8, value: amount });
|
|
2093
|
+
return data;
|
|
2094
|
+
}
|
|
2095
|
+
function getExtraAccountMetaAddress(mint, hookProgramId) {
|
|
2096
|
+
const [pda] = PublicKey.findProgramAddress(
|
|
2097
|
+
["extra-account-metas", mint.toBytes()],
|
|
2098
|
+
hookProgramId
|
|
2099
|
+
);
|
|
2100
|
+
return pda;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
// src/transfer-hook/seeds.ts
|
|
2104
|
+
async function unpackSeeds(params) {
|
|
2105
|
+
const { addressConfig, previousMetas, instructionData, fetchAccountData } = params;
|
|
2106
|
+
const seeds = [];
|
|
2107
|
+
let cursor = 0;
|
|
2108
|
+
if (addressConfig.length < 32) {
|
|
2109
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
2110
|
+
reason: `addressConfig must be at least 32 bytes, got ${addressConfig.length}`
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
while (cursor < 32) {
|
|
2114
|
+
const discriminator = addressConfig[cursor];
|
|
2115
|
+
if (discriminator === 0) {
|
|
2116
|
+
break;
|
|
2117
|
+
}
|
|
2118
|
+
cursor++;
|
|
2119
|
+
switch (discriminator) {
|
|
2120
|
+
case 1: {
|
|
2121
|
+
const length = addressConfig[cursor++];
|
|
2122
|
+
seeds.push(addressConfig.slice(cursor, cursor + length));
|
|
2123
|
+
cursor += length;
|
|
2124
|
+
break;
|
|
2125
|
+
}
|
|
2126
|
+
case 2: {
|
|
2127
|
+
const offset = addressConfig[cursor++];
|
|
2128
|
+
const length = addressConfig[cursor++];
|
|
2129
|
+
seeds.push(instructionData.slice(offset, offset + length));
|
|
2130
|
+
break;
|
|
2131
|
+
}
|
|
2132
|
+
case 3: {
|
|
2133
|
+
const index = addressConfig[cursor++];
|
|
2134
|
+
seeds.push(previousMetas[index].pubkey.toBytes());
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
case 4: {
|
|
2138
|
+
const accountIndex = addressConfig[cursor++];
|
|
2139
|
+
const dataOffset = addressConfig[cursor++];
|
|
2140
|
+
const length = addressConfig[cursor++];
|
|
2141
|
+
const data = await fetchAccountData(previousMetas[accountIndex].pubkey);
|
|
2142
|
+
if (data) {
|
|
2143
|
+
seeds.push(data.slice(dataOffset, dataOffset + length));
|
|
2144
|
+
}
|
|
2145
|
+
break;
|
|
2146
|
+
}
|
|
2147
|
+
default: {
|
|
2148
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
2149
|
+
reason: `Unknown seed discriminator ${discriminator} at addressConfig offset ${cursor - 1}`
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return seeds;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/transfer-hook/resolve-extra-accounts.ts
|
|
2158
|
+
async function resolveExtraAccountMeta(params) {
|
|
2159
|
+
const { meta, previousMetas, instructionData, hookProgramId, fetchAccountData } = params;
|
|
2160
|
+
if (meta.discriminator === 0) {
|
|
2161
|
+
return {
|
|
2162
|
+
pubkey: PublicKey.fromBytes(meta.addressConfig),
|
|
2163
|
+
isSigner: meta.isSigner,
|
|
2164
|
+
isWritable: meta.isWritable
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
if (meta.discriminator === 1) {
|
|
2168
|
+
const seeds2 = await unpackSeeds({
|
|
2169
|
+
addressConfig: meta.addressConfig,
|
|
2170
|
+
previousMetas,
|
|
2171
|
+
instructionData,
|
|
2172
|
+
fetchAccountData
|
|
2173
|
+
});
|
|
2174
|
+
const [pda2] = PublicKey.findProgramAddress(seeds2, hookProgramId);
|
|
2175
|
+
return {
|
|
2176
|
+
pubkey: pda2,
|
|
2177
|
+
isSigner: meta.isSigner,
|
|
2178
|
+
isWritable: meta.isWritable
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
if (meta.discriminator === 2) {
|
|
2182
|
+
const accountIndex2 = meta.addressConfig[0];
|
|
2183
|
+
const dataOffset = meta.addressConfig[1];
|
|
2184
|
+
const data = await fetchAccountData(previousMetas[accountIndex2].pubkey);
|
|
2185
|
+
if (!data) {
|
|
2186
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
2187
|
+
reason: `Failed to fetch account data for discriminator 2 resolution: account at index ${accountIndex2} returned no data`
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
if (data.length < dataOffset + 32) {
|
|
2191
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
2192
|
+
reason: `Account data too short for discriminator 2 resolution: expected at least ${dataOffset + 32} bytes at offset ${dataOffset}, but data is only ${data.length} bytes`
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
const extracted = data.slice(dataOffset, dataOffset + 32);
|
|
2196
|
+
return {
|
|
2197
|
+
pubkey: PublicKey.fromBytes(extracted),
|
|
2198
|
+
isSigner: meta.isSigner,
|
|
2199
|
+
isWritable: meta.isWritable
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
const accountIndex = meta.discriminator - 128;
|
|
2203
|
+
if (accountIndex >= previousMetas.length) {
|
|
2204
|
+
throw SplTokenError.pdaDerivationFailed({
|
|
2205
|
+
reason: `External PDA resolution failed: accountIndex ${accountIndex} (discriminator ${meta.discriminator}) is out of bounds for previousMetas of length ${previousMetas.length}`
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
const programId = previousMetas[accountIndex].pubkey;
|
|
2209
|
+
const seeds = await unpackSeeds({
|
|
2210
|
+
addressConfig: meta.addressConfig,
|
|
2211
|
+
previousMetas,
|
|
2212
|
+
instructionData,
|
|
2213
|
+
fetchAccountData
|
|
2214
|
+
});
|
|
2215
|
+
const [pda] = PublicKey.findProgramAddress(seeds, programId);
|
|
2216
|
+
return {
|
|
2217
|
+
pubkey: pda,
|
|
2218
|
+
isSigner: meta.isSigner,
|
|
2219
|
+
isWritable: meta.isWritable
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
async function resolveTransferHookExtraAccounts(params) {
|
|
2223
|
+
const { mint, hookProgramId, sourceAta, destinationAta, authority, amount, fetchAccountData } = params;
|
|
2224
|
+
const validationPda = getExtraAccountMetaAddress(mint, hookProgramId);
|
|
2225
|
+
const validationData = await fetchAccountData(validationPda);
|
|
2226
|
+
if (!validationData) {
|
|
2227
|
+
return [
|
|
2228
|
+
{ pubkey: hookProgramId, isSigner: false, isWritable: false },
|
|
2229
|
+
{ pubkey: validationPda, isSigner: false, isWritable: false }
|
|
2230
|
+
];
|
|
2231
|
+
}
|
|
2232
|
+
const extraMetas = parseExtraAccountMetaList(validationData);
|
|
2233
|
+
if (extraMetas.length === 0) {
|
|
2234
|
+
return [
|
|
2235
|
+
{ pubkey: hookProgramId, isSigner: false, isWritable: false },
|
|
2236
|
+
{ pubkey: validationPda, isSigner: false, isWritable: false }
|
|
2237
|
+
];
|
|
2238
|
+
}
|
|
2239
|
+
const instructionData = buildExecuteInstructionData(amount);
|
|
2240
|
+
const baseMetas = [
|
|
2241
|
+
{ pubkey: sourceAta, isSigner: false, isWritable: true },
|
|
2242
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
2243
|
+
{ pubkey: destinationAta, isSigner: false, isWritable: true },
|
|
2244
|
+
{ pubkey: authority, isSigner: false, isWritable: false },
|
|
2245
|
+
{ pubkey: validationPda, isSigner: false, isWritable: false }
|
|
2246
|
+
];
|
|
2247
|
+
const resolvedExtras = [];
|
|
2248
|
+
for (const meta of extraMetas) {
|
|
2249
|
+
const previousMetas = [...baseMetas, ...resolvedExtras];
|
|
2250
|
+
const resolved = await resolveExtraAccountMeta({
|
|
2251
|
+
meta,
|
|
2252
|
+
previousMetas,
|
|
2253
|
+
instructionData,
|
|
2254
|
+
hookProgramId,
|
|
2255
|
+
fetchAccountData
|
|
2256
|
+
});
|
|
2257
|
+
resolvedExtras.push(resolved);
|
|
2258
|
+
}
|
|
2259
|
+
const deEscalated = resolvedExtras.map((account) => ({
|
|
2260
|
+
...account,
|
|
2261
|
+
isSigner: false
|
|
2262
|
+
}));
|
|
2263
|
+
return [
|
|
2264
|
+
...deEscalated,
|
|
2265
|
+
{ pubkey: hookProgramId, isSigner: false, isWritable: false },
|
|
2266
|
+
{ pubkey: validationPda, isSigner: false, isWritable: false }
|
|
2267
|
+
];
|
|
1043
2268
|
}
|
|
1044
2269
|
|
|
1045
2270
|
// src/client/spl-token-client.ts
|
|
@@ -1069,11 +2294,20 @@ var SplTokenClient = class {
|
|
|
1069
2294
|
this.programId = programId;
|
|
1070
2295
|
}
|
|
1071
2296
|
/**
|
|
1072
|
-
*
|
|
2297
|
+
* Returns the Token-2022 program ID used by this client.
|
|
1073
2298
|
*/
|
|
1074
2299
|
getProgramId() {
|
|
1075
2300
|
return this.programId;
|
|
1076
2301
|
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Returns the underlying RialoClient.
|
|
2304
|
+
*
|
|
2305
|
+
* Useful for advanced operations that need direct RPC access,
|
|
2306
|
+
* such as MintBuilder which needs to calculate rent.
|
|
2307
|
+
*/
|
|
2308
|
+
getRialoClient() {
|
|
2309
|
+
return this.client;
|
|
2310
|
+
}
|
|
1077
2311
|
/**
|
|
1078
2312
|
* Retrieves mint account information.
|
|
1079
2313
|
*
|
|
@@ -1083,10 +2317,8 @@ var SplTokenClient = class {
|
|
|
1083
2317
|
*
|
|
1084
2318
|
* @example
|
|
1085
2319
|
* ```typescript
|
|
1086
|
-
* const mintInfo = await tokenClient.getMintInfo({ mint
|
|
1087
|
-
* console.log(
|
|
1088
|
-
* console.log(`Decimals: ${mintInfo.decimals}`);
|
|
1089
|
-
* console.log(`Mint Authority: ${mintInfo.mintAuthority?.toString() ?? 'disabled'}`);
|
|
2320
|
+
* const mintInfo = await tokenClient.getMintInfo({ mint });
|
|
2321
|
+
* console.log(mintInfo.decimals);
|
|
1090
2322
|
* ```
|
|
1091
2323
|
*/
|
|
1092
2324
|
async getMintInfo({ mint }) {
|
|
@@ -1094,7 +2326,10 @@ var SplTokenClient = class {
|
|
|
1094
2326
|
if (!accountInfo) {
|
|
1095
2327
|
throw SplTokenError.mintNotFound({ address: mint.toString() });
|
|
1096
2328
|
}
|
|
1097
|
-
return parseMintAccount({
|
|
2329
|
+
return parseMintAccount({
|
|
2330
|
+
address: mint,
|
|
2331
|
+
data: decodeAccountData(accountInfo.data)
|
|
2332
|
+
});
|
|
1098
2333
|
}
|
|
1099
2334
|
/**
|
|
1100
2335
|
* Retrieves Token-2022 native metadata from a mint.
|
|
@@ -1105,20 +2340,52 @@ var SplTokenClient = class {
|
|
|
1105
2340
|
*
|
|
1106
2341
|
* @example
|
|
1107
2342
|
* ```typescript
|
|
1108
|
-
* const metadata = await tokenClient.getTokenMetadata({ mint
|
|
2343
|
+
* const metadata = await tokenClient.getTokenMetadata({ mint });
|
|
1109
2344
|
* if (metadata) {
|
|
1110
|
-
* console.log(
|
|
1111
|
-
* console.log(`Symbol: ${metadata.symbol}`);
|
|
1112
|
-
* console.log(`URI: ${metadata.uri}`);
|
|
2345
|
+
* console.log(metadata.name, metadata.symbol);
|
|
1113
2346
|
* }
|
|
1114
2347
|
* ```
|
|
1115
2348
|
*/
|
|
1116
|
-
async getTokenMetadata({
|
|
2349
|
+
async getTokenMetadata({
|
|
2350
|
+
mint
|
|
2351
|
+
}) {
|
|
2352
|
+
const accountInfo = await this.client.getAccountInfo(mint);
|
|
2353
|
+
if (!accountInfo) {
|
|
2354
|
+
throw SplTokenError.mintNotFound({ address: mint.toString() });
|
|
2355
|
+
}
|
|
2356
|
+
return parseTokenMetadata({
|
|
2357
|
+
mint,
|
|
2358
|
+
data: decodeAccountData(accountInfo.data)
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Retrieves complete mint account data in a single RPC call.
|
|
2363
|
+
*
|
|
2364
|
+
* Returns parsed mint info, token metadata, and raw extensions from one
|
|
2365
|
+
* `getAccountInfo` call, avoiding the redundant RPC calls that occur when
|
|
2366
|
+
* calling `getMintInfo()` and `getTokenMetadata()` separately.
|
|
2367
|
+
*
|
|
2368
|
+
* Raw extensions include ALL extension types (including PermanentDelegate,
|
|
2369
|
+
* TransferHook, etc.) unlike `getMintExtensionStates()` which only parses
|
|
2370
|
+
* a subset.
|
|
2371
|
+
*
|
|
2372
|
+
* @param options - Options containing the mint public key
|
|
2373
|
+
* @returns Combined mint info, metadata, and raw extensions
|
|
2374
|
+
* @throws {SplTokenError} If the mint account doesn't exist
|
|
2375
|
+
*/
|
|
2376
|
+
async getMintAccountData({
|
|
2377
|
+
mint
|
|
2378
|
+
}) {
|
|
1117
2379
|
const accountInfo = await this.client.getAccountInfo(mint);
|
|
1118
2380
|
if (!accountInfo) {
|
|
1119
2381
|
throw SplTokenError.mintNotFound({ address: mint.toString() });
|
|
1120
2382
|
}
|
|
1121
|
-
|
|
2383
|
+
const data = decodeAccountData(accountInfo.data);
|
|
2384
|
+
return {
|
|
2385
|
+
mintInfo: parseMintAccount({ address: mint, data }),
|
|
2386
|
+
metadata: parseTokenMetadata({ mint, data }),
|
|
2387
|
+
rawExtensions: getMintExtensions({ data })
|
|
2388
|
+
};
|
|
1122
2389
|
}
|
|
1123
2390
|
/**
|
|
1124
2391
|
* Retrieves token account information.
|
|
@@ -1132,9 +2399,14 @@ var SplTokenClient = class {
|
|
|
1132
2399
|
}) {
|
|
1133
2400
|
const accountInfo = await this.client.getAccountInfo(tokenAccount);
|
|
1134
2401
|
if (!accountInfo) {
|
|
1135
|
-
throw SplTokenError.tokenAccountNotFound({
|
|
2402
|
+
throw SplTokenError.tokenAccountNotFound({
|
|
2403
|
+
address: tokenAccount.toString()
|
|
2404
|
+
});
|
|
1136
2405
|
}
|
|
1137
|
-
return parseTokenAccount({
|
|
2406
|
+
return parseTokenAccount({
|
|
2407
|
+
address: tokenAccount,
|
|
2408
|
+
data: decodeAccountData(accountInfo.data)
|
|
2409
|
+
});
|
|
1138
2410
|
}
|
|
1139
2411
|
/**
|
|
1140
2412
|
* Gets the token balance for a wallet.
|
|
@@ -1151,13 +2423,23 @@ var SplTokenClient = class {
|
|
|
1151
2423
|
* console.log(`Token balance: ${balance}`);
|
|
1152
2424
|
* ```
|
|
1153
2425
|
*/
|
|
1154
|
-
async getBalance({
|
|
1155
|
-
|
|
2426
|
+
async getBalance({
|
|
2427
|
+
wallet,
|
|
2428
|
+
mint
|
|
2429
|
+
}) {
|
|
2430
|
+
const ata = getAssociatedTokenAddressSync({
|
|
2431
|
+
wallet,
|
|
2432
|
+
mint,
|
|
2433
|
+
programId: this.programId
|
|
2434
|
+
});
|
|
1156
2435
|
const accountInfo = await this.client.getAccountInfo(ata);
|
|
1157
2436
|
if (!accountInfo) {
|
|
1158
2437
|
return 0n;
|
|
1159
2438
|
}
|
|
1160
|
-
const tokenAccount = parseTokenAccount({
|
|
2439
|
+
const tokenAccount = parseTokenAccount({
|
|
2440
|
+
address: ata,
|
|
2441
|
+
data: decodeAccountData(accountInfo.data)
|
|
2442
|
+
});
|
|
1161
2443
|
return tokenAccount.amount;
|
|
1162
2444
|
}
|
|
1163
2445
|
/**
|
|
@@ -1165,6 +2447,7 @@ var SplTokenClient = class {
|
|
|
1165
2447
|
*
|
|
1166
2448
|
* @param options - Options containing wallet and mint public keys
|
|
1167
2449
|
* @returns The derived ATA address and bump seed
|
|
2450
|
+
* @throws {SplTokenError} If the ATA derivation fails
|
|
1168
2451
|
*
|
|
1169
2452
|
* @example
|
|
1170
2453
|
* ```typescript
|
|
@@ -1176,7 +2459,11 @@ var SplTokenClient = class {
|
|
|
1176
2459
|
wallet,
|
|
1177
2460
|
mint
|
|
1178
2461
|
}) {
|
|
1179
|
-
return findAssociatedTokenAddress({
|
|
2462
|
+
return findAssociatedTokenAddress({
|
|
2463
|
+
wallet,
|
|
2464
|
+
mint,
|
|
2465
|
+
programId: this.programId
|
|
2466
|
+
});
|
|
1180
2467
|
}
|
|
1181
2468
|
/**
|
|
1182
2469
|
* Checks if an Associated Token Account exists.
|
|
@@ -1184,20 +2471,26 @@ var SplTokenClient = class {
|
|
|
1184
2471
|
* @param options - Options containing wallet and mint public keys
|
|
1185
2472
|
* @returns True if the ATA exists, false otherwise
|
|
1186
2473
|
*/
|
|
1187
|
-
async ataExists({
|
|
1188
|
-
|
|
2474
|
+
async ataExists({
|
|
2475
|
+
wallet,
|
|
2476
|
+
mint
|
|
2477
|
+
}) {
|
|
2478
|
+
const ata = getAssociatedTokenAddressSync({
|
|
2479
|
+
wallet,
|
|
2480
|
+
mint,
|
|
2481
|
+
programId: this.programId
|
|
2482
|
+
});
|
|
1189
2483
|
const accountInfo = await this.client.getAccountInfo(ata);
|
|
1190
2484
|
return accountInfo !== null;
|
|
1191
2485
|
}
|
|
1192
2486
|
/**
|
|
1193
2487
|
* Creates instructions for a token transfer.
|
|
1194
2488
|
*
|
|
1195
|
-
*
|
|
2489
|
+
* Builds the necessary instructions for transferring tokens,
|
|
1196
2490
|
* optionally creating the destination ATA if it doesn't exist.
|
|
1197
2491
|
*
|
|
1198
2492
|
* @param params - Transfer parameters
|
|
1199
2493
|
* @returns Array of instructions to execute
|
|
1200
|
-
* @throws {SplTokenError} If the mint doesn't exist
|
|
1201
2494
|
*
|
|
1202
2495
|
* @example
|
|
1203
2496
|
* ```typescript
|
|
@@ -1212,7 +2505,14 @@ var SplTokenClient = class {
|
|
|
1212
2505
|
* ```
|
|
1213
2506
|
*/
|
|
1214
2507
|
async createTransferInstructions(params) {
|
|
1215
|
-
const {
|
|
2508
|
+
const {
|
|
2509
|
+
source,
|
|
2510
|
+
destination,
|
|
2511
|
+
mint,
|
|
2512
|
+
amount,
|
|
2513
|
+
decimals,
|
|
2514
|
+
createDestinationAta = true
|
|
2515
|
+
} = params;
|
|
1216
2516
|
const instructions = [];
|
|
1217
2517
|
const sourceAta = getAssociatedTokenAddressSync({
|
|
1218
2518
|
wallet: source,
|
|
@@ -1225,35 +2525,63 @@ var SplTokenClient = class {
|
|
|
1225
2525
|
programId: this.programId
|
|
1226
2526
|
});
|
|
1227
2527
|
if (createDestinationAta) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
})
|
|
1238
|
-
);
|
|
1239
|
-
}
|
|
2528
|
+
instructions.push(
|
|
2529
|
+
createAssociatedTokenAccountIdempotentInstruction({
|
|
2530
|
+
payer: source,
|
|
2531
|
+
associatedToken: destinationAta,
|
|
2532
|
+
owner: destination,
|
|
2533
|
+
mint,
|
|
2534
|
+
programId: this.programId
|
|
2535
|
+
})
|
|
2536
|
+
);
|
|
1240
2537
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
2538
|
+
if (params.transferFee) {
|
|
2539
|
+
instructions.push(
|
|
2540
|
+
transferCheckedWithFeeInstruction({
|
|
2541
|
+
source: sourceAta,
|
|
2542
|
+
mint,
|
|
2543
|
+
destination: destinationAta,
|
|
2544
|
+
authority: source,
|
|
2545
|
+
amount,
|
|
2546
|
+
decimals,
|
|
2547
|
+
fee: params.transferFee.fee,
|
|
2548
|
+
programId: this.programId
|
|
2549
|
+
})
|
|
2550
|
+
);
|
|
2551
|
+
} else {
|
|
2552
|
+
instructions.push(
|
|
2553
|
+
transferCheckedInstruction({
|
|
2554
|
+
source: sourceAta,
|
|
2555
|
+
mint,
|
|
2556
|
+
destination: destinationAta,
|
|
2557
|
+
authority: source,
|
|
2558
|
+
amount,
|
|
2559
|
+
decimals,
|
|
2560
|
+
programId: this.programId
|
|
2561
|
+
})
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
if (params.transferHook) {
|
|
2565
|
+
const { hookProgramId } = params.transferHook;
|
|
2566
|
+
const transferIx = instructions[instructions.length - 1];
|
|
2567
|
+
const extraAccounts = await resolveTransferHookExtraAccounts({
|
|
1244
2568
|
mint,
|
|
1245
|
-
|
|
2569
|
+
hookProgramId,
|
|
2570
|
+
sourceAta,
|
|
2571
|
+
destinationAta,
|
|
1246
2572
|
authority: source,
|
|
1247
2573
|
amount,
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
2574
|
+
fetchAccountData: async (pubkey) => {
|
|
2575
|
+
const info = await this.client.getAccountInfo(pubkey);
|
|
2576
|
+
return info ? decodeAccountData(info.data) : null;
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
for (const account of extraAccounts) {
|
|
2580
|
+
transferIx.accounts.push(account);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
1252
2583
|
return instructions;
|
|
1253
2584
|
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Options for creating a transfer transaction.
|
|
1256
|
-
*/
|
|
1257
2585
|
/**
|
|
1258
2586
|
* Creates a transaction for a token transfer.
|
|
1259
2587
|
*
|
|
@@ -1286,18 +2614,19 @@ var SplTokenClient = class {
|
|
|
1286
2614
|
*/
|
|
1287
2615
|
async createTransferTransaction({
|
|
1288
2616
|
params,
|
|
2617
|
+
payer,
|
|
1289
2618
|
validFrom,
|
|
1290
2619
|
configHashPrefix
|
|
1291
2620
|
}) {
|
|
1292
2621
|
const instructions = await this.createTransferInstructions(params);
|
|
1293
|
-
const builder = TransactionBuilder.create().setPayer(
|
|
1294
|
-
for (const
|
|
1295
|
-
builder.addInstruction(
|
|
2622
|
+
const builder = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).setConfigHashPrefix(configHashPrefix);
|
|
2623
|
+
for (const instruction of instructions) {
|
|
2624
|
+
builder.addInstruction(instruction);
|
|
1296
2625
|
}
|
|
1297
2626
|
return builder.build();
|
|
1298
2627
|
}
|
|
1299
2628
|
/**
|
|
1300
|
-
* Transfers tokens
|
|
2629
|
+
* Transfers tokens and sends the transaction.
|
|
1301
2630
|
*
|
|
1302
2631
|
* This is a high-level convenience method that:
|
|
1303
2632
|
* 1. Creates the destination ATA if needed
|
|
@@ -1305,7 +2634,7 @@ var SplTokenClient = class {
|
|
|
1305
2634
|
* 3. Signs and sends the transaction
|
|
1306
2635
|
* 4. Waits for confirmation
|
|
1307
2636
|
*
|
|
1308
|
-
* @param options -
|
|
2637
|
+
* @param options - Transfer params, payer, validFrom, and signers
|
|
1309
2638
|
* @returns Transaction signature
|
|
1310
2639
|
*
|
|
1311
2640
|
* @example
|
|
@@ -1329,21 +2658,209 @@ var SplTokenClient = class {
|
|
|
1329
2658
|
*/
|
|
1330
2659
|
async transfer({
|
|
1331
2660
|
params,
|
|
2661
|
+
payer,
|
|
1332
2662
|
validFrom,
|
|
1333
2663
|
configHashPrefix,
|
|
1334
|
-
|
|
2664
|
+
signers
|
|
2665
|
+
}) {
|
|
2666
|
+
const tx = await this.createTransferTransaction({
|
|
2667
|
+
params,
|
|
2668
|
+
payer,
|
|
2669
|
+
validFrom,
|
|
2670
|
+
configHashPrefix
|
|
2671
|
+
});
|
|
2672
|
+
return this.signAndSend({ tx, signers });
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Initializes a mint and sends the transaction.
|
|
2676
|
+
*
|
|
2677
|
+
* @param options - Initialize mint params, payer, validFrom, and signers
|
|
2678
|
+
* @returns Transaction signature
|
|
2679
|
+
*/
|
|
2680
|
+
initializeMint({
|
|
2681
|
+
params,
|
|
2682
|
+
payer,
|
|
2683
|
+
validFrom,
|
|
2684
|
+
signers
|
|
2685
|
+
}) {
|
|
2686
|
+
const instruction = initializeMintInstruction({
|
|
2687
|
+
...params,
|
|
2688
|
+
programId: this.programId
|
|
2689
|
+
});
|
|
2690
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2691
|
+
return this.signAndSend({ tx, signers });
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Mints tokens and sends the transaction.
|
|
2695
|
+
*
|
|
2696
|
+
* @param options - Mint-to params, payer, validFrom, and signers
|
|
2697
|
+
* @returns Transaction signature
|
|
2698
|
+
*/
|
|
2699
|
+
mintTo({
|
|
2700
|
+
params,
|
|
2701
|
+
payer,
|
|
2702
|
+
validFrom,
|
|
2703
|
+
signers
|
|
2704
|
+
}) {
|
|
2705
|
+
const instruction = mintToInstruction({
|
|
2706
|
+
...params,
|
|
2707
|
+
programId: this.programId
|
|
2708
|
+
});
|
|
2709
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2710
|
+
return this.signAndSend({ tx, signers });
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* Initializes token metadata on a mint.
|
|
2714
|
+
*
|
|
2715
|
+
* Use this for the on-mint TokenMetadata extension flow.
|
|
2716
|
+
*
|
|
2717
|
+
* @param options - Token metadata params, payer, validFrom, and signers
|
|
2718
|
+
* @returns Transaction signature
|
|
2719
|
+
*/
|
|
2720
|
+
initializeTokenMetadata({
|
|
2721
|
+
params,
|
|
2722
|
+
payer,
|
|
2723
|
+
validFrom,
|
|
2724
|
+
signers
|
|
2725
|
+
}) {
|
|
2726
|
+
const instruction = initializeTokenMetadataInstruction({
|
|
2727
|
+
...params,
|
|
2728
|
+
programId: params.programId ?? this.programId
|
|
2729
|
+
});
|
|
2730
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2731
|
+
return this.signAndSend({ tx, signers });
|
|
2732
|
+
}
|
|
2733
|
+
/**
|
|
2734
|
+
* Initializes a metadata pointer extension on a mint.
|
|
2735
|
+
*
|
|
2736
|
+
* Use this for the Metadata Pointer flow, where metadata lives in
|
|
2737
|
+
* a separate account and the mint stores a pointer to it.
|
|
2738
|
+
*
|
|
2739
|
+
* @param options - Metadata pointer params, payer, validFrom, and signers
|
|
2740
|
+
* @returns Transaction signature
|
|
2741
|
+
*/
|
|
2742
|
+
initializeMetadataPointer({
|
|
2743
|
+
params,
|
|
2744
|
+
payer,
|
|
2745
|
+
validFrom,
|
|
2746
|
+
signers
|
|
2747
|
+
}) {
|
|
2748
|
+
const instruction = initializeMetadataPointerInstruction({
|
|
2749
|
+
...params,
|
|
2750
|
+
programId: this.programId
|
|
2751
|
+
});
|
|
2752
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2753
|
+
return this.signAndSend({ tx, signers });
|
|
2754
|
+
}
|
|
2755
|
+
/**
|
|
2756
|
+
* Updates a metadata pointer extension on a mint.
|
|
2757
|
+
*
|
|
2758
|
+
* @param options - Metadata pointer params, payer, validFrom, and signers
|
|
2759
|
+
* @returns Transaction signature
|
|
2760
|
+
*/
|
|
2761
|
+
updateMetadataPointer({
|
|
2762
|
+
params,
|
|
2763
|
+
payer,
|
|
2764
|
+
validFrom,
|
|
2765
|
+
signers
|
|
2766
|
+
}) {
|
|
2767
|
+
const instruction = updateMetadataPointerInstruction({
|
|
2768
|
+
...params,
|
|
2769
|
+
programId: this.programId
|
|
2770
|
+
});
|
|
2771
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2772
|
+
return this.signAndSend({ tx, signers });
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Initializes a transfer fee config on a mint.
|
|
2776
|
+
*
|
|
2777
|
+
* @param options - Transfer fee config params, payer, validFrom, and signers
|
|
2778
|
+
* @returns Transaction signature
|
|
2779
|
+
*/
|
|
2780
|
+
initializeTransferFeeConfig({
|
|
2781
|
+
params,
|
|
2782
|
+
payer,
|
|
2783
|
+
validFrom,
|
|
2784
|
+
signers
|
|
2785
|
+
}) {
|
|
2786
|
+
const instruction = initializeTransferFeeConfigInstruction({
|
|
2787
|
+
...params,
|
|
2788
|
+
programId: this.programId
|
|
2789
|
+
});
|
|
2790
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2791
|
+
return this.signAndSend({ tx, signers });
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* Transfers tokens with a fee and sends the transaction.
|
|
2795
|
+
*
|
|
2796
|
+
* @param options - Transfer params, payer, validFrom, and signers
|
|
2797
|
+
* @returns Transaction signature
|
|
2798
|
+
*/
|
|
2799
|
+
transferCheckedWithFee({
|
|
2800
|
+
params,
|
|
2801
|
+
payer,
|
|
2802
|
+
validFrom,
|
|
2803
|
+
signers
|
|
2804
|
+
}) {
|
|
2805
|
+
const instruction = transferCheckedWithFeeInstruction({
|
|
2806
|
+
...params,
|
|
2807
|
+
programId: this.programId
|
|
2808
|
+
});
|
|
2809
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2810
|
+
return this.signAndSend({ tx, signers });
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Initializes a non-transferable mint.
|
|
2814
|
+
*
|
|
2815
|
+
* @param options - Non-transferable mint params, payer, validFrom, and signers
|
|
2816
|
+
* @returns Transaction signature
|
|
2817
|
+
*/
|
|
2818
|
+
initializeNonTransferableMint({
|
|
2819
|
+
params,
|
|
2820
|
+
payer,
|
|
2821
|
+
validFrom,
|
|
2822
|
+
signers
|
|
2823
|
+
}) {
|
|
2824
|
+
const instruction = initializeNonTransferableMintInstruction({
|
|
2825
|
+
...params,
|
|
2826
|
+
programId: this.programId
|
|
2827
|
+
});
|
|
2828
|
+
const tx = TransactionBuilder.create().setPayer(payer).setValidFrom(validFrom).addInstruction(instruction).build();
|
|
2829
|
+
return this.signAndSend({ tx, signers });
|
|
2830
|
+
}
|
|
2831
|
+
async signAndSend({
|
|
2832
|
+
tx,
|
|
2833
|
+
signers
|
|
1335
2834
|
}) {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
2835
|
+
if (signers.length === 0) {
|
|
2836
|
+
throw new Error("At least one signer required.");
|
|
2837
|
+
}
|
|
2838
|
+
const signedTx = await tx.signAllWith(signers);
|
|
2839
|
+
const result = await this.client.sendAndConfirmTransaction(
|
|
2840
|
+
signedTx.serialize()
|
|
2841
|
+
);
|
|
1339
2842
|
return result.signature;
|
|
1340
2843
|
}
|
|
1341
2844
|
};
|
|
1342
|
-
function createSplTokenClient({
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
2845
|
+
function createSplTokenClient(options) {
|
|
2846
|
+
return new SplTokenClient(options);
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
// src/recipes/create-token-with-metadata.ts
|
|
2850
|
+
async function createTokenWithMetadata(tokenClient, params, options) {
|
|
2851
|
+
const builder = MintBuilder.create(tokenClient).withDecimals(params.decimals).withMintAuthority(params.mintAuthority).withMetadata(params.metadata);
|
|
2852
|
+
if (params.freezeAuthority) {
|
|
2853
|
+
builder.withFreezeAuthority(params.freezeAuthority);
|
|
2854
|
+
}
|
|
2855
|
+
if (params.initialMint) {
|
|
2856
|
+
builder.withInitialSupply(params.initialMint);
|
|
2857
|
+
}
|
|
2858
|
+
return await builder.send({
|
|
2859
|
+
payer: options.payer,
|
|
2860
|
+
mintKeypair: options.mintKeypair,
|
|
2861
|
+
validFrom: options.validFrom,
|
|
2862
|
+
signers: options.signers
|
|
2863
|
+
});
|
|
1347
2864
|
}
|
|
1348
2865
|
/*! Bundled license information:
|
|
1349
2866
|
|
|
@@ -1351,6 +2868,6 @@ function createSplTokenClient({
|
|
|
1351
2868
|
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
1352
2869
|
*/
|
|
1353
2870
|
|
|
1354
|
-
export { ASSOCIATED_TOKEN_PROGRAM_ID, AccountType, ExtensionType, MINT_SIZE, SplTokenClient, SplTokenError, SplTokenErrorCode, TOKEN_2022_PROGRAM_ID, TOKEN_ACCOUNT_SIZE, TOKEN_PROGRAM_ID, TokenAccountState, TokenInstruction, createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountInstruction, createSplTokenClient, findAssociatedTokenAddress, getAssociatedTokenAddressSync, getMintExtensions, initializeMintInstruction, mintToInstruction, parseMetadataPointerExtension, parseMintAccount, parseTokenAccount, parseTokenMetadata, transferCheckedInstruction, transferInstruction };
|
|
2871
|
+
export { ASSOCIATED_TOKEN_PROGRAM_ID, AccountType, EXECUTE_DISCRIMINATOR, EXTRA_ACCOUNT_META_SIZE, ExtensionType, MINT_SIZE, MintBuilder, SplTokenClient, SplTokenError, SplTokenErrorCode, TLV_LENGTH_SIZE, TLV_TYPE_SIZE, TOKEN_2022_PROGRAM_ID, TOKEN_ACCOUNT_SIZE, TOKEN_PROGRAM_ID, TokenAccountState, TokenInstruction, buildExecuteInstructionData, createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountInstruction, createSplTokenClient, createTokenWithMetadata, findAssociatedTokenAddress, getAssociatedTokenAddressSync, getExtraAccountMetaAddress, getMetadataExtensionSize, getMinRentForMint, getMinRentForMintWithMetadata, getMintExtensionStates, getMintExtensions, getMintSizeWithExtensions, getMintSizeWithMetadata, getTokenAccountExtensionStates, getTokenAccountExtensions, getTokenMetadataDataSize, initializeMetadataPointerInstruction, initializeMintInstruction, initializeNonTransferableMintInstruction, initializePermanentDelegateInstruction, initializeTokenMetadataInstruction, initializeTransferFeeConfigInstruction, initializeTransferHookInstruction, mintToInstruction, parseExtension, parseExtraAccountMetaList, parseMetadataPointerExtension, parseMetadataPointerExtensionData, parseMintAccount, parsePermanentDelegateExtension, parseTokenAccount, parseTokenMetadata, parseTokenMetadataExtensionData, parseTransferFeeConfigExtension, parseTransferHookExtension, resolveExtraAccountMeta, resolveTransferHookExtraAccounts, setTransferFeeInstruction, transferCheckedInstruction, transferCheckedWithFeeInstruction, transferInstruction, unpackSeeds, updateMetadataPointerInstruction };
|
|
1355
2872
|
//# sourceMappingURL=index.mjs.map
|
|
1356
2873
|
//# sourceMappingURL=index.mjs.map
|