@xgamesfun/solana 0.24.0 → 0.25.1

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.
Files changed (99) hide show
  1. package/DISCLAIMER.md +1 -1
  2. package/NOTICE +4 -4
  3. package/README.md +100 -52
  4. package/bin/xsolana.mjs +32 -11
  5. package/examples/blind-forge.json +2 -4
  6. package/examples/coin-flip-arena.json +2 -5
  7. package/examples/duel-arena.json +1 -4
  8. package/examples/fair-presale.json +1 -3
  9. package/examples/hero-trials.json +14 -2
  10. package/examples/ideas.json +788 -788
  11. package/examples/nft-raffle.json +1 -0
  12. package/examples/partner-vault.json +19 -3
  13. package/examples/sacrifice-raffle.json +2 -10
  14. package/examples/shelter-fair.json +1 -0
  15. package/examples/stakers-draw.json +4 -7
  16. package/examples/trade-in-shop.json +2 -3
  17. package/js/approve/details.mjs +117 -48
  18. package/js/approve/ed25519.mjs +5 -1
  19. package/js/approve/live-check.mjs +83 -26
  20. package/js/approve/page.html +217 -78
  21. package/js/approve/page.js +43 -15
  22. package/js/approve/server.mjs +276 -74
  23. package/js/chain/base58.js +7 -1
  24. package/js/chain/borsh.js +15 -11
  25. package/js/chain/bytes.js +11 -8
  26. package/js/chain/core.js +21 -5
  27. package/js/chain/curve.js +1 -1
  28. package/js/chain/lookup.js +118 -36
  29. package/js/chain/mint.js +6 -2
  30. package/js/chain/pda.js +2 -2
  31. package/js/chain/programs.js +6 -6
  32. package/js/chain/rpc.js +40 -14
  33. package/js/chain/token.js +13 -2
  34. package/js/chain/token2022.js +55 -16
  35. package/js/chain/tx.js +32 -22
  36. package/js/client/accounts.js +270 -80
  37. package/js/client/compressed.js +4 -3
  38. package/js/client/grant.js +165 -40
  39. package/js/client/idl/loot.js +1 -1
  40. package/js/client/idl/loot.json +104 -16
  41. package/js/client/ix.js +2121 -457
  42. package/js/client/pda.js +20 -18
  43. package/js/client/policy.js +12 -5
  44. package/js/client/random.js +2 -2
  45. package/js/client/sizes.js +220 -65
  46. package/js/client/traits.js +14 -5
  47. package/js/connect/connect.mjs +59 -17
  48. package/js/economy/check.mjs +3884 -690
  49. package/js/economy/cost.mjs +381 -57
  50. package/js/economy/examples.mjs +176 -35
  51. package/js/economy/explain.mjs +468 -110
  52. package/js/economy/guide.mjs +282 -59
  53. package/js/economy/legal.mjs +12 -6
  54. package/js/economy/live.mjs +62 -20
  55. package/js/economy/model.mjs +320 -97
  56. package/js/economy/plan.mjs +1022 -203
  57. package/js/economy/review.mjs +760 -0
  58. package/js/economy/schema.json +134 -372
  59. package/js/economy/secrets.mjs +17 -7
  60. package/js/economy/simulate.mjs +771 -178
  61. package/js/economy/trust.mjs +15 -0
  62. package/js/economy/txsize.mjs +15 -3
  63. package/js/economy/validate.mjs +61 -24
  64. package/js/index.js +1 -0
  65. package/js/mcp/server.mjs +43 -15
  66. package/js/mcp/tools.mjs +318 -80
  67. package/js/play/index.js +1960 -461
  68. package/js/play/keys.js +35 -9
  69. package/js/results/index.js +225 -32
  70. package/js/results/server.js +230 -0
  71. package/js/results/wire.js +64 -16
  72. package/package.json +6 -4
  73. package/release.json +5 -5
  74. package/skills/xsolana-economy/SKILL.md +52 -22
  75. package/skills/xsolana-economy/references/after-launch.md +32 -21
  76. package/skills/xsolana-economy/references/apps.md +47 -36
  77. package/skills/xsolana-economy/references/sentence-to-spec.md +149 -129
  78. package/skills/xsolana-economy/references/telling-the-person.md +15 -6
  79. package/skills/xsolana-economy/references/trust-and-cheating.md +126 -0
  80. package/types/chain/core.d.ts +1 -1
  81. package/types/chain/lookup.d.ts +1 -1
  82. package/types/client/accounts.d.ts +24 -3
  83. package/types/client/grant.d.ts +2 -2
  84. package/types/client/idl/loot.d.ts +22 -0
  85. package/types/client/ix.d.ts +54 -45
  86. package/types/client/pda.d.ts +3 -1
  87. package/types/economy/cost.d.mts +1 -1
  88. package/types/economy/explain.d.mts +3 -0
  89. package/types/economy/guide.d.mts +5 -0
  90. package/types/economy/model.d.mts +48 -3
  91. package/types/economy/plan.d.mts +44 -2
  92. package/types/economy/review.d.mts +45 -0
  93. package/types/economy/trust.d.mts +8 -0
  94. package/types/index.d.ts +1 -0
  95. package/types/mcp/tools.d.mts +62 -0
  96. package/types/play/index.d.ts +14 -25
  97. package/types/results/index.d.ts +8 -7
  98. package/types/results/server.d.ts +126 -0
  99. package/types/results/wire.d.ts +1 -1
@@ -30,7 +30,13 @@ export function encodeBase58(bytes) {
30
30
  if (byte !== 0) break;
31
31
  zeros += '1';
32
32
  }
33
- return zeros + digits.reverse().map((d) => ALPHABET[d]).join('');
33
+ return (
34
+ zeros +
35
+ digits
36
+ .reverse()
37
+ .map(d => ALPHABET[d])
38
+ .join('')
39
+ );
34
40
  }
35
41
 
36
42
  export function decodeBase58(text) {
package/js/chain/borsh.js CHANGED
@@ -14,7 +14,7 @@ const INT = { i32: 4, i64: 8 };
14
14
  const ADDRESS_BYTES = 32;
15
15
 
16
16
  function typeDef(idl, name) {
17
- const def = idl.types.find((t) => t.name === name);
17
+ const def = idl.types.find(t => t.name === name);
18
18
  if (!def) throw new Error(`Unknown IDL type ${name}`);
19
19
  return def;
20
20
  }
@@ -43,13 +43,13 @@ function encodeScalar(type, value, out) {
43
43
  }
44
44
 
45
45
  /** A variant's field list: named ({ name, type }) or tuple (bare types). */
46
- const isNamed = (fields) => typeof fields[0] === 'object' && fields[0] !== null && 'name' in fields[0];
46
+ const isNamed = fields => typeof fields[0] === 'object' && fields[0] !== null && 'name' in fields[0];
47
47
 
48
48
  function encodeDefined(idl, name, value, out) {
49
49
  const def = typeDef(idl, name).type;
50
50
  if (def.kind === 'enum') {
51
51
  const variantName = typeof value === 'string' ? value : Object.keys(value ?? {})[0];
52
- const index = def.variants.findIndex((v) => v.name === variantName);
52
+ const index = def.variants.findIndex(v => v.name === variantName);
53
53
  if (index < 0) throw new Error(`${name} has no variant ${String(variantName)}`);
54
54
  out.push(Uint8Array.of(index));
55
55
  const fields = def.variants[index].fields ?? [];
@@ -71,7 +71,9 @@ function encodeDefined(idl, name, value, out) {
71
71
  * a number past a byte) is refused rather than written as other bytes.
72
72
  */
73
73
  function byteArray(value, length) {
74
- const bytes = value instanceof Uint8Array || (Array.isArray(value) && value.every((b) => Number.isInteger(b) && b >= 0 && b <= 255));
74
+ const bytes =
75
+ value instanceof Uint8Array ||
76
+ (Array.isArray(value) && value.every(b => Number.isInteger(b) && b >= 0 && b <= 255));
75
77
  if (!bytes) throw new TypeError(`Expected ${length} bytes: a Uint8Array or an array of integers from 0 to 255`);
76
78
  if (value.length !== length) throw new Error(`Expected ${length} bytes, got ${value.length}`);
77
79
  return Uint8Array.from(value);
@@ -114,7 +116,7 @@ export function encodeBorsh(idl, type, value) {
114
116
 
115
117
  /** An instruction's data: its discriminator, then each argument in IDL order. */
116
118
  export function encodeInstruction(idl, name, args) {
117
- const ix = idl.instructions.find((i) => i.name === name);
119
+ const ix = idl.instructions.find(i => i.name === name);
118
120
  if (!ix) throw new Error(`Unknown instruction ${name}`);
119
121
  const out = [Uint8Array.from(ix.discriminator)];
120
122
  for (const arg of ix.args) {
@@ -126,7 +128,7 @@ export function encodeInstruction(idl, name, args) {
126
128
 
127
129
  /** An account's bytes as the program writes them: its discriminator, then its fields. */
128
130
  export function encodeAccount(idl, name, value) {
129
- const account = idl.accounts.find((a) => a.name === name);
131
+ const account = idl.accounts.find(a => a.name === name);
130
132
  if (!account) throw new Error(`Unknown account type ${name}`);
131
133
  return concat(Uint8Array.from(account.discriminator), encodeBorsh(idl, { defined: { name } }, value));
132
134
  }
@@ -218,11 +220,12 @@ function decodeAt(idl, type, data, at) {
218
220
  /** One value of `type` at `offset`: [value, next offset]. */
219
221
  export const decodeBorsh = (idl, type, data, offset = 0) => decodeAt(idl, type, data, offset);
220
222
 
221
- const startsWith = (data, prefix) => data.length >= prefix.length && equalBytes(data.subarray(0, prefix.length), Uint8Array.from(prefix));
223
+ const startsWith = (data, prefix) =>
224
+ data.length >= prefix.length && equalBytes(data.subarray(0, prefix.length), Uint8Array.from(prefix));
222
225
 
223
226
  /** An account of the named type, after checking its discriminator. Bytes after the fields are left alone. */
224
227
  export function decodeAccount(idl, name, data) {
225
- const account = idl.accounts.find((a) => a.name === name);
228
+ const account = idl.accounts.find(a => a.name === name);
226
229
  if (!account) throw new Error(`Unknown account type ${name}`);
227
230
  if (!startsWith(data, account.discriminator)) throw new Error(`Not a ${name} account`);
228
231
  return decodeDefined(idl, name, data, account.discriminator.length)[0];
@@ -230,7 +233,7 @@ export function decodeAccount(idl, name, data) {
230
233
 
231
234
  /** Whether the data starts with the named account's discriminator. */
232
235
  export function isAccount(idl, name, data) {
233
- const account = idl.accounts.find((a) => a.name === name);
236
+ const account = idl.accounts.find(a => a.name === name);
234
237
  return Boolean(account) && startsWith(data, account.discriminator);
235
238
  }
236
239
 
@@ -263,8 +266,9 @@ export function decodeEvents(idl, logs) {
263
266
  } catch {
264
267
  continue;
265
268
  }
266
- const event = (idl.events ?? []).find((e) => startsWith(bytes, e.discriminator));
267
- if (event) out.push({ name: event.name, data: decodeDefined(idl, event.name, bytes, event.discriminator.length)[0] });
269
+ const event = (idl.events ?? []).find(e => startsWith(bytes, e.discriminator));
270
+ if (event)
271
+ out.push({ name: event.name, data: decodeDefined(idl, event.name, bytes, event.discriminator.length)[0] });
268
272
  }
269
273
  return out;
270
274
  }
package/js/chain/bytes.js CHANGED
@@ -7,10 +7,10 @@ const decoder = new TextDecoder();
7
7
  const BASE64_CHUNK = 0x8000;
8
8
 
9
9
  /** UTF-8 bytes of a string. */
10
- export const utf8 = (text) => encoder.encode(text);
10
+ export const utf8 = text => encoder.encode(text);
11
11
 
12
12
  /** A string from UTF-8 bytes. */
13
- export const fromUtf8 = (bytes) => decoder.decode(bytes);
13
+ export const fromUtf8 = bytes => decoder.decode(bytes);
14
14
 
15
15
  /** The parts, one after another. */
16
16
  export function concat(...parts) {
@@ -39,17 +39,18 @@ export function equalBytes(a, b) {
39
39
 
40
40
  export function toBase64(bytes) {
41
41
  let text = '';
42
- for (let i = 0; i < bytes.length; i += BASE64_CHUNK) text += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));
42
+ for (let i = 0; i < bytes.length; i += BASE64_CHUNK)
43
+ text += String.fromCharCode(...bytes.subarray(i, i + BASE64_CHUNK));
43
44
  return btoa(text);
44
45
  }
45
46
 
46
47
  /** Standard base64 to bytes; throws on anything that is not base64. */
47
48
  export function fromBase64(text) {
48
49
  if (typeof text !== 'string' || !/^[A-Za-z0-9+/]*={0,2}$/.test(text)) throw new Error('Not base64');
49
- return Uint8Array.from(atob(text), (c) => c.charCodeAt(0));
50
+ return Uint8Array.from(atob(text), c => c.charCodeAt(0));
50
51
  }
51
52
 
52
- export const toHex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
53
+ export const toHex = bytes => Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
53
54
 
54
55
  export function fromHex(text) {
55
56
  if (typeof text !== 'string' || text.length % 2 || /[^0-9a-f]/i.test(text)) throw new Error('Not hex');
@@ -81,7 +82,8 @@ export function uintLE(value, size) {
81
82
  export function intLE(value, size) {
82
83
  const v = toBigInt(value);
83
84
  const bits = BigInt(size * 8);
84
- if (v < -(1n << (bits - 1n)) || v >= 1n << (bits - 1n)) throw new RangeError(`${value} does not fit in a signed ${size}-byte integer`);
85
+ if (v < -(1n << (bits - 1n)) || v >= 1n << (bits - 1n))
86
+ throw new RangeError(`${value} does not fit in a signed ${size}-byte integer`);
85
87
  return uintLE(v < 0n ? v + (1n << bits) : v, size);
86
88
  }
87
89
 
@@ -90,7 +92,8 @@ export function intLE(value, size) {
90
92
  * lost its low digits, so it is refused rather than turned into some other value.
91
93
  */
92
94
  export function toBigInt(value) {
93
- if (typeof value === 'number' && !Number.isSafeInteger(value)) throw new RangeError(`${value} is not a safe integer: pass it as a bigint`);
95
+ if (typeof value === 'number' && !Number.isSafeInteger(value))
96
+ throw new RangeError(`${value} is not a safe integer: pass it as a bigint`);
94
97
  return BigInt(value);
95
98
  }
96
99
 
@@ -138,4 +141,4 @@ export function readCompactU16(bytes, offset) {
138
141
  }
139
142
 
140
143
  /** Random bytes from the platform's CSPRNG (WebCrypto in browsers and Node 24). */
141
- export const randomBytes = (n) => globalThis.crypto.getRandomValues(new Uint8Array(n));
144
+ export const randomBytes = n => globalThis.crypto.getRandomValues(new Uint8Array(n));
package/js/chain/core.js CHANGED
@@ -9,14 +9,21 @@ import { meta } from './programs.js';
9
9
  export { MPL_CORE_PROGRAM };
10
10
  /** MPL Core `CreateCollectionV2`. */
11
11
  const CREATE_COLLECTION_V2 = 21;
12
- const PLUGIN = { Royalties: 0, FreezeDelegate: 1, UpdateDelegate: 4, PermanentFreezeDelegate: 5, Attributes: 6, BubblegumV2: 15 };
12
+ const PLUGIN = {
13
+ Royalties: 0,
14
+ FreezeDelegate: 1,
15
+ UpdateDelegate: 4,
16
+ PermanentFreezeDelegate: 5,
17
+ Attributes: 6,
18
+ BubblegumV2: 15,
19
+ };
13
20
  const ASSET_V1 = 1;
14
21
  const COLLECTION_V1 = 5;
15
22
  const PLUGIN_HEADER = 3;
16
23
  const UPDATE_AUTHORITY_COLLECTION = 2;
17
24
 
18
25
  /** A Borsh string as Core writes it. */
19
- const coreString = (text) => {
26
+ const coreString = text => {
20
27
  const bytes = utf8(text);
21
28
  return concat(uintLE(bytes.length, 4), bytes);
22
29
  };
@@ -27,8 +34,17 @@ const coreString = (text) => {
27
34
  * it also carries Bubblegum's plugin, so compressed NFTs can be minted into it. The collection address signs too (a
28
35
  * fresh key).
29
36
  */
30
- export function createCollectionInstruction({ collection, creator, name, uri, royaltyBps = 500, project, compressed = false }) {
31
- if (!project) throw new Error('createCollectionInstruction needs the loot project that mints into the collection (`project`).');
37
+ export function createCollectionInstruction({
38
+ collection,
39
+ creator,
40
+ name,
41
+ uri,
42
+ royaltyBps = 500,
43
+ project,
44
+ compressed = false,
45
+ }) {
46
+ if (!project)
47
+ throw new Error('createCollectionInstruction needs the loot project that mints into the collection (`project`).');
32
48
  const data = concat(
33
49
  Uint8Array.of(CREATE_COLLECTION_V2),
34
50
  coreString(name),
@@ -64,7 +80,7 @@ export function createCollectionInstruction({ collection, creator, name, uri, ro
64
80
 
65
81
  function reader(data, start) {
66
82
  let at = start;
67
- const need = (n) => {
83
+ const need = n => {
68
84
  if (at + n > data.length) throw new RangeError('read past the end of the account');
69
85
  };
70
86
  return {
package/js/chain/curve.js CHANGED
@@ -9,7 +9,7 @@ const P = 2n ** 255n - 19n;
9
9
  const D = 37095705934669439343138083508754565189542113879843219016388785533085940283555n;
10
10
  const Y_MASK = (1n << 255n) - 1n;
11
11
 
12
- const mod = (a) => {
12
+ const mod = a => {
13
13
  const r = a % P;
14
14
  return r >= 0n ? r : r + P;
15
15
  };
@@ -26,38 +26,65 @@ const meta = (pubkey, isSigner, isWritable) => ({ pubkey, isSigner, isWritable }
26
26
  const ix = (keys, data) => ({ programId: LOOKUP_TABLE_PROGRAM, keys, data });
27
27
 
28
28
  /** The table `authority` makes at `slot` (a recent slot: the table's address names it): [address, bump]. */
29
- export const tableAddress = (authority, slot) => findProgramAddress([addressBytes(authority), uintLE(slot, 8)], LOOKUP_TABLE_PROGRAM);
29
+ export const tableAddress = (authority, slot) =>
30
+ findProgramAddress([addressBytes(authority), uintLE(slot, 8)], LOOKUP_TABLE_PROGRAM);
30
31
 
31
32
  /** Makes the table `authority` owns at `slot`, its rent paid by `payer`. Returns { address, instruction }. */
32
33
  export function createTableIx({ authority, payer = authority, slot }) {
33
34
  const [address, bump] = tableAddress(authority, slot);
34
- const instruction = ix([meta(address, false, true), meta(authority, true, false), meta(payer, true, true), meta(SYSTEM_PROGRAM, false, false)], concat(uintLE(0, 4), uintLE(slot, 8), Uint8Array.of(bump)));
35
+ const instruction = ix(
36
+ [
37
+ meta(address, false, true),
38
+ meta(authority, true, false),
39
+ meta(payer, true, true),
40
+ meta(SYSTEM_PROGRAM, false, false),
41
+ ],
42
+ concat(uintLE(0, 4), uintLE(slot, 8), Uint8Array.of(bump)),
43
+ );
35
44
  return { address, instruction };
36
45
  }
37
46
 
38
47
  /** Adds `addresses` to `table`, the rent of its growth paid by `payer`. */
39
48
  export const extendTableIx = ({ table, authority, payer = authority, addresses }) =>
40
- ix([meta(table, false, true), meta(authority, true, false), meta(payer, true, true), meta(SYSTEM_PROGRAM, false, false)], concat(uintLE(2, 4), uintLE(addresses.length, 8), ...addresses.map(addressBytes)));
49
+ ix(
50
+ [
51
+ meta(table, false, true),
52
+ meta(authority, true, false),
53
+ meta(payer, true, true),
54
+ meta(SYSTEM_PROGRAM, false, false),
55
+ ],
56
+ concat(uintLE(2, 4), uintLE(addresses.length, 8), ...addresses.map(addressBytes)),
57
+ );
41
58
 
42
59
  /** Freezes `table`: nobody can add to it or close it after. */
43
- export const freezeTableIx = ({ table, authority }) => ix([meta(table, false, true), meta(authority, true, false)], uintLE(1, 4));
60
+ export const freezeTableIx = ({ table, authority }) =>
61
+ ix([meta(table, false, true), meta(authority, true, false)], uintLE(1, 4));
44
62
 
45
63
  /** Deactivates `table`: transactions can still use it until it cools down, then it can be closed. */
46
- export const deactivateTableIx = ({ table, authority }) => ix([meta(table, false, true), meta(authority, true, false)], uintLE(3, 4));
64
+ export const deactivateTableIx = ({ table, authority }) =>
65
+ ix([meta(table, false, true), meta(authority, true, false)], uintLE(3, 4));
47
66
 
48
67
  /** Closes a cooled-down `table`: its rent goes to `recipient`. */
49
- export const closeTableIx = ({ table, authority, recipient = authority }) => ix([meta(table, false, true), meta(authority, true, false), meta(recipient, false, true)], uintLE(4, 4));
68
+ export const closeTableIx = ({ table, authority, recipient = authority }) =>
69
+ ix([meta(table, false, true), meta(authority, true, false), meta(recipient, false, true)], uintLE(4, 4));
50
70
 
51
71
  /**
52
72
  * A table's account read back: { authority (null once frozen), deactivationSlot (null while active),
53
73
  * lastExtendedSlot, addresses }, or null when `account` is not a lookup table.
54
74
  */
55
75
  export function readTable(account) {
56
- if (!account || account.owner !== LOOKUP_TABLE_PROGRAM || account.data.length < TABLE_HEADER_BYTES || readUintLE(account.data, 0, 4) !== 1n) return null;
76
+ if (
77
+ !account ||
78
+ account.owner !== LOOKUP_TABLE_PROGRAM ||
79
+ account.data.length < TABLE_HEADER_BYTES ||
80
+ readUintLE(account.data, 0, 4) !== 1n
81
+ )
82
+ return null;
57
83
  const data = account.data;
58
84
  const deactivation = readUintLE(data, 4, 8);
59
85
  const addresses = [];
60
- for (let at = TABLE_HEADER_BYTES; at + 32 <= data.length; at += 32) addresses.push(encodeBase58(data.subarray(at, at + 32)));
86
+ for (let at = TABLE_HEADER_BYTES; at + 32 <= data.length; at += 32)
87
+ addresses.push(encodeBase58(data.subarray(at, at + 32)));
61
88
  return {
62
89
  authority: data[21] === 1 ? encodeBase58(data.subarray(22, 54)) : null,
63
90
  deactivationSlot: deactivation === ACTIVE ? null : deactivation,
@@ -67,7 +94,7 @@ export function readTable(account) {
67
94
  }
68
95
 
69
96
  /** The filter that finds the tables `authority` owns (getProgramAccounts on LOOKUP_TABLE_PROGRAM). */
70
- export const tablesOf = (authority) => [{ memcmp: { offset: 22, bytes: authority } }];
97
+ export const tablesOf = authority => [{ memcmp: { offset: 22, bytes: authority } }];
71
98
 
72
99
  /**
73
100
  * The slots the SlotHashes sysvar holds (its account's data: a count, then a slot and a hash each), newest first. It
@@ -95,7 +122,8 @@ export const SLOT_HASHES_HELD = 512;
95
122
  export function tableStep(table, slot, recent = null) {
96
123
  if (table.deactivationSlot === null) return 'active';
97
124
  if (BigInt(slot) <= table.deactivationSlot) return 'cooling';
98
- if (recent) return recent.length >= SLOT_HASHES_HELD && recent.every((s) => s > table.deactivationSlot) ? 'close' : 'cooling';
125
+ if (recent)
126
+ return recent.length >= SLOT_HASHES_HELD && recent.every(s => s > table.deactivationSlot) ? 'close' : 'cooling';
99
127
  return BigInt(slot) > table.deactivationSlot + TABLE_COOLDOWN_SLOTS ? 'close' : 'cooling';
100
128
  }
101
129
 
@@ -105,17 +133,21 @@ export function tableStep(table, slot, recent = null) {
105
133
  * (`asV0`).
106
134
  */
107
135
  export function transactionLocks(payer, instructions, { asV0 = false } = {}) {
108
- return new Set([payer, ...instructions.flatMap((i) => [i.programId, ...i.keys.map((k) => k.pubkey)])]).size + (asV0 ? 2 : 0);
136
+ return (
137
+ new Set([payer, ...instructions.flatMap(i => [i.programId, ...i.keys.map(k => k.pubkey)])]).size + (asV0 ? 2 : 0)
138
+ );
109
139
  }
110
140
 
111
141
  /** The accounts of `instructions` a table can hold: every one that is neither a signer nor a program they call, once. */
112
142
  export function tableAccounts(instructions) {
113
- const signers = new Set(instructions.flatMap((i) => i.keys.filter((k) => k.isSigner).map((k) => k.pubkey)));
114
- const programs = new Set(instructions.map((i) => i.programId));
115
- return [...new Set(instructions.flatMap((i) => i.keys.map((k) => k.pubkey)))].filter((a) => !signers.has(a) && !programs.has(a));
143
+ const signers = new Set(instructions.flatMap(i => i.keys.filter(k => k.isSigner).map(k => k.pubkey)));
144
+ const programs = new Set(instructions.map(i => i.programId));
145
+ return [...new Set(instructions.flatMap(i => i.keys.map(k => k.pubkey)))].filter(
146
+ a => !signers.has(a) && !programs.has(a),
147
+ );
116
148
  }
117
149
 
118
- const sizeOf = (input) => {
150
+ const sizeOf = input => {
119
151
  try {
120
152
  return transactionSize(compileMessage(input));
121
153
  } catch {
@@ -151,14 +183,23 @@ export function planTable({ payer, blockhash, slot, instructions, first = [] })
151
183
  const final = [...instructions, deactivateTableIx({ table: address, authority: payer })];
152
184
  // A transaction locks at most 64 accounts, its programs and those it loads from a table included.
153
185
  if (transactionLocks(payer, final) > MAX_ACCOUNT_LOCKS) return null;
154
- if (sizeOf({ payer, blockhash, instructions: final, version: 0, lookupTables: [table] }) > MAX_TRANSACTION_BYTES) return null;
186
+ if (sizeOf({ payer, blockhash, instructions: final, version: 0, lookupTables: [table] }) > MAX_TRANSACTION_BYTES)
187
+ return null;
155
188
  const steps = [];
156
189
  let step = [create];
157
190
  let left = addresses;
158
191
  while (left.length) {
159
192
  // As many addresses as fit this step's transaction.
160
193
  let n = left.length;
161
- while (n > 0 && sizeOf({ payer, blockhash, instructions: [...step, extendTableIx({ table: address, authority: payer, addresses: left.slice(0, n) })] }) > MAX_TRANSACTION_BYTES) n--;
194
+ while (
195
+ n > 0 &&
196
+ sizeOf({
197
+ payer,
198
+ blockhash,
199
+ instructions: [...step, extendTableIx({ table: address, authority: payer, addresses: left.slice(0, n) })],
200
+ }) > MAX_TRANSACTION_BYTES
201
+ )
202
+ n--;
162
203
  if (n === 0) {
163
204
  // Not one address fits beside the table's creation: the creation goes alone. Not one fits a transaction of its
164
205
  // own: no table can be made for this payer.
@@ -187,17 +228,23 @@ export function planTable({ payer, blockhash, slot, instructions, first = [] })
187
228
  * null when nothing fits: more accounts than a transaction locks, or instructions too long even as v0.
188
229
  */
189
230
  export function sendPlan({ payer, blockhash = SYSTEM_PROGRAM, instructions, alone = () => false }) {
190
- if (compileFitting({ payer, blockhash, instructions })) return { legacy: true, first: [], budget: [], rest: instructions, table: false };
191
- if (planTable({ payer, blockhash, slot: 0n, instructions })) return { legacy: false, first: [], budget: [], rest: instructions, table: true };
192
- const budget = instructions.filter((i) => i.programId === COMPUTE_BUDGET_PROGRAM);
193
- const first = instructions.filter((i) => alone(i));
194
- const rest = instructions.filter((i) => !alone(i));
195
- if (!first.length || first.some((i) => !compileFitting({ payer, blockhash, instructions: [...budget, i] }))) return null;
196
- if (compileFitting({ payer, blockhash, instructions: rest })) return { legacy: false, first, budget, rest, table: false };
197
- return planTable({ payer, blockhash, slot: 0n, instructions: rest }) ? { legacy: false, first, budget, rest, table: true } : null;
231
+ if (compileFitting({ payer, blockhash, instructions }))
232
+ return { legacy: true, first: [], budget: [], rest: instructions, table: false };
233
+ if (planTable({ payer, blockhash, slot: 0n, instructions }))
234
+ return { legacy: false, first: [], budget: [], rest: instructions, table: true };
235
+ const budget = instructions.filter(i => i.programId === COMPUTE_BUDGET_PROGRAM);
236
+ const first = instructions.filter(i => alone(i));
237
+ const rest = instructions.filter(i => !alone(i));
238
+ if (!first.length || first.some(i => !compileFitting({ payer, blockhash, instructions: [...budget, i] })))
239
+ return null;
240
+ if (compileFitting({ payer, blockhash, instructions: rest }))
241
+ return { legacy: false, first, budget, rest, table: false };
242
+ return planTable({ payer, blockhash, slot: 0n, instructions: rest })
243
+ ? { legacy: false, first, budget, rest, table: true }
244
+ : null;
198
245
  }
199
246
 
200
- const pause = (ms) => new Promise((r) => setTimeout(r, ms));
247
+ const pause = ms => new Promise(r => setTimeout(r, ms));
201
248
  /** The tables this page made, by address: two sends never name the same one, even in one slot. */
202
249
  const madeHere = new Set();
203
250
 
@@ -229,24 +276,47 @@ async function freshSlot(rpc, payer) {
229
276
  * plain sentence when it can't be sent. Returns { signature (the last transaction's), table (its address, or null),
230
277
  * steps (the signatures of the transactions before it) }.
231
278
  */
232
- export async function sendFitting({ rpc, payer, instructions, sign, table = true, alone = () => false, first = [], standing = [], sleep = pause }) {
233
- const sendOne = async (build) => {
279
+ export async function sendFitting({
280
+ rpc,
281
+ payer,
282
+ instructions,
283
+ sign,
284
+ table = true,
285
+ alone = () => false,
286
+ first = [],
287
+ standing = [],
288
+ sleep = pause,
289
+ }) {
290
+ const sendOne = async build => {
234
291
  const { blockhash, lastValidBlockHeight } = await rpc.latestBlockhash();
235
292
  const message = build(blockhash);
236
293
  const signature = await rpc.send(await sign(message));
237
294
  await rpc.confirm?.(signature, { lastValidBlockHeight });
238
295
  return signature;
239
296
  };
240
- const plain = (list) => sendOne((bh) => compileMessage({ payer, blockhash: bh, instructions: list }));
297
+ const plain = list => sendOne(bh => compileMessage({ payer, blockhash: bh, instructions: list }));
241
298
  const { blockhash } = await rpc.latestBlockhash();
242
299
  const plan = sendPlan({ payer, blockhash, instructions, alone });
243
- if (!plan) throw new Error(`This transaction is too large to send even as a v0 one with a lookup table: it names ${tableAccounts(instructions).length + 1} accounts or more (a transaction locks at most 64), or its instructions are too long.`);
300
+ if (!plan)
301
+ throw new Error(
302
+ `This transaction is too large to send even as a v0 one with a lookup table: it names ${tableAccounts(instructions).length + 1} accounts or more (a transaction locks at most 64), or its instructions are too long.`,
303
+ );
244
304
  if (plan.legacy) return { signature: await plain(instructions), table: null, steps: [] };
245
305
  // Standing tables (a project's, made at go-live; each still active) that hold enough of its accounts: one v0
246
306
  // transaction with them, and no table of its own.
247
307
  const fits = standing.length && compileFitting({ payer, blockhash, instructions, tables: standing });
248
- if (fits && fits[0] === 0x80) return { signature: await sendOne((bh) => compileMessage({ payer, blockhash: bh, instructions, version: 0, lookupTables: standing })), table: null, steps: [] };
249
- if (plan.table && !table) throw new Error(`This transaction takes ${sizeOf({ payer, blockhash, instructions })} bytes, more than the ${MAX_TRANSACTION_BYTES} a legacy one holds, and this key holds no rent for a lookup table.`);
308
+ if (fits && fits[0] === 0x80)
309
+ return {
310
+ signature: await sendOne(bh =>
311
+ compileMessage({ payer, blockhash: bh, instructions, version: 0, lookupTables: standing }),
312
+ ),
313
+ table: null,
314
+ steps: [],
315
+ };
316
+ if (plan.table && !table)
317
+ throw new Error(
318
+ `This transaction takes ${sizeOf({ payer, blockhash, instructions })} bytes, more than the ${MAX_TRANSACTION_BYTES} a legacy one holds, and this key holds no rent for a lookup table.`,
319
+ );
250
320
  const steps = [];
251
321
  const proofs = async () => {
252
322
  for (const ix of plan.first) steps.push(await plain([...plan.budget, ix]));
@@ -264,16 +334,28 @@ export async function sendFitting({ rpc, payer, instructions, sign, table = true
264
334
  let usable = false;
265
335
  for (let i = 0; i < 40 && !usable; i++) {
266
336
  held = readTable(await rpc.account(made.table.address));
267
- usable = Boolean(held && made.table.addresses.every((a) => held.addresses.includes(a)) && BigInt(await rpc.call('getSlot', [{ commitment: 'confirmed' }])) > held.lastExtendedSlot);
337
+ usable = Boolean(
338
+ held &&
339
+ made.table.addresses.every(a => held.addresses.includes(a)) &&
340
+ BigInt(await rpc.call('getSlot', [{ commitment: 'confirmed' }])) > held.lastExtendedSlot,
341
+ );
268
342
  if (!usable) await sleep(400);
269
343
  }
270
- if (!held || !made.table.addresses.every((a) => held.addresses.includes(a))) throw new Error(`The lookup table ${made.table.address} doesn't hold this transaction's accounts yet: try again in a moment.`);
344
+ if (!held || !made.table.addresses.every(a => held.addresses.includes(a)))
345
+ throw new Error(
346
+ `The lookup table ${made.table.address} doesn't hold this transaction's accounts yet: try again in a moment.`,
347
+ );
271
348
  // Sent before the slot passes the table's last addition, the transaction would be refused for addresses it can't load.
272
- if (!usable) throw new Error(`The lookup table ${made.table.address} is made, but the network has not moved past the slot it was last added to: try again in a moment.`);
349
+ if (!usable)
350
+ throw new Error(
351
+ `The lookup table ${made.table.address} is made, but the network has not moved past the slot it was last added to: try again in a moment.`,
352
+ );
273
353
  await proofs();
274
354
  const tables = [{ address: made.table.address, addresses: held.addresses }];
275
355
  const message = compileFitting({ payer, blockhash, instructions: made.final, tables });
276
356
  if (!message || message[0] !== 0x80) throw new Error('This transaction doesn’t fit even with its lookup table.');
277
- const signature = await sendOne((bh) => compileMessage({ payer, blockhash: bh, instructions: made.final, version: 0, lookupTables: tables }));
357
+ const signature = await sendOne(bh =>
358
+ compileMessage({ payer, blockhash: bh, instructions: made.final, version: 0, lookupTables: tables }),
359
+ );
278
360
  return { signature, table: made.table.address, steps };
279
361
  }
package/js/chain/mint.js CHANGED
@@ -20,7 +20,9 @@ function option(data, at) {
20
20
  function paddedString(data, at) {
21
21
  const length = Number(readUintLE(data, at, 4));
22
22
  if (at + 4 + length > data.length) throw new RangeError('string past the end');
23
- const value = fromUtf8(data.subarray(at + 4, at + 4 + length)).replaceAll('\0', '').trim();
23
+ const value = fromUtf8(data.subarray(at + 4, at + 4 + length))
24
+ .replaceAll('\0', '')
25
+ .trim();
24
26
  return { value, next: at + 4 + length };
25
27
  }
26
28
 
@@ -62,7 +64,9 @@ function token2022Metadata(data) {
62
64
  export function parseMint(address, owner, data, metadataData = null) {
63
65
  const program = owner === TOKEN_PROGRAM ? 'spl-token' : owner === TOKEN_2022_PROGRAM ? 'token-2022' : null;
64
66
  if (!program) return null;
65
- const mintLayout = data.length === MINT_BYTES || (program === 'token-2022' && data.length > ACCOUNT_TYPE_OFFSET && data[ACCOUNT_TYPE_OFFSET] === ACCOUNT_TYPE_MINT);
67
+ const mintLayout =
68
+ data.length === MINT_BYTES ||
69
+ (program === 'token-2022' && data.length > ACCOUNT_TYPE_OFFSET && data[ACCOUNT_TYPE_OFFSET] === ACCOUNT_TYPE_MINT);
66
70
  if (!mintLayout || data[45] !== 1) return null;
67
71
  let meta = null;
68
72
  if (metadataData) {
package/js/chain/pda.js CHANGED
@@ -24,7 +24,7 @@ const MAX_SEEDS = 16;
24
24
 
25
25
  /** The address for these seeds (bump included), or null when it lands on the curve (not a valid PDA). */
26
26
  export function createProgramAddress(seeds, programId) {
27
- if (seeds.length > MAX_SEEDS || seeds.some((s) => s.length > MAX_SEED_BYTES)) throw new Error('Invalid PDA seeds');
27
+ if (seeds.length > MAX_SEEDS || seeds.some(s => s.length > MAX_SEED_BYTES)) throw new Error('Invalid PDA seeds');
28
28
  const hash = sha256(concat(...seeds, addressBytes(programId), MARKER));
29
29
  return isOnCurve(hash) ? null : encodeBase58(hash);
30
30
  }
@@ -34,7 +34,7 @@ const found = new Map();
34
34
 
35
35
  /** The canonical PDA and its bump: the highest bump whose address is off the curve. */
36
36
  export function findProgramAddress(seeds, programId) {
37
- const key = `${programId}:${seeds.map((s) => Array.from(s).join(',')).join('|')}`;
37
+ const key = `${programId}:${seeds.map(s => Array.from(s).join(',')).join('|')}`;
38
38
  if (found.has(key)) return found.get(key);
39
39
  for (let bump = 255; bump >= 0; bump--) {
40
40
  const address = createProgramAddress([...seeds, Uint8Array.of(bump)], programId);
@@ -35,19 +35,19 @@ export function transferLamports(from, to, lamports) {
35
35
  }
36
36
 
37
37
  /** Compute Budget `RequestHeapFrame`: the transaction's programs get a heap of `bytes` (a multiple of 1 KiB, 32 KiB to 256 KiB). */
38
- export const requestHeapFrame = (bytes) => ({
38
+ export const requestHeapFrame = bytes => ({
39
39
  programId: COMPUTE_BUDGET_PROGRAM,
40
40
  keys: [],
41
41
  data: concat(Uint8Array.of(REQUEST_HEAP_FRAME), uintLE(bytes, 4)),
42
42
  });
43
43
 
44
- export const computeUnitLimit = (units) => ({
44
+ export const computeUnitLimit = units => ({
45
45
  programId: COMPUTE_BUDGET_PROGRAM,
46
46
  keys: [],
47
47
  data: concat(Uint8Array.of(SET_COMPUTE_UNIT_LIMIT), uintLE(units, 4)),
48
48
  });
49
49
 
50
- export const computeUnitPrice = (microLamports) => ({
50
+ export const computeUnitPrice = microLamports => ({
51
51
  programId: COMPUTE_BUDGET_PROGRAM,
52
52
  keys: [],
53
53
  data: concat(Uint8Array.of(SET_COMPUTE_UNIT_PRICE), uintLE(microLamports, 8)),
@@ -69,7 +69,7 @@ export function createAssociatedTokenAccountIdempotent(payer, owner, mint, token
69
69
  };
70
70
  }
71
71
 
72
- export const syncNative = (account) => ({
72
+ export const syncNative = account => ({
73
73
  programId: TOKEN_PROGRAM,
74
74
  keys: [meta(account, false, true)],
75
75
  data: Uint8Array.of(TOKEN_SYNC_NATIVE),
@@ -89,8 +89,8 @@ export const closeTokenAccount = (account, destination, owner) => ({
89
89
  export function withSolWrap(player, mint, price, instructions) {
90
90
  if (mint !== NATIVE_MINT) return instructions;
91
91
  const wrapped = associatedTokenAddress(player, NATIVE_MINT);
92
- const budget = instructions.filter((ix) => ix.programId === COMPUTE_BUDGET_PROGRAM);
93
- const rest = instructions.filter((ix) => ix.programId !== COMPUTE_BUDGET_PROGRAM);
92
+ const budget = instructions.filter(ix => ix.programId === COMPUTE_BUDGET_PROGRAM);
93
+ const rest = instructions.filter(ix => ix.programId !== COMPUTE_BUDGET_PROGRAM);
94
94
  return [
95
95
  ...budget,
96
96
  createAssociatedTokenAccountIdempotent(player, player, NATIVE_MINT),