@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
package/js/chain/rpc.js CHANGED
@@ -29,7 +29,7 @@ export class RpcError extends Error {
29
29
  }
30
30
  }
31
31
 
32
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
33
33
 
34
34
  /** The first block of each public Solana cluster: it names the cluster whatever URL reaches it. */
35
35
  export const GENESIS = Object.freeze({
@@ -38,7 +38,8 @@ export const GENESIS = Object.freeze({
38
38
  testnet: '4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY',
39
39
  });
40
40
 
41
- const DEVNET_ONLY = 'loot runs on devnet only: mainnet waits for an audit. Unset XSOLANA_RPC_URL, or point it at a devnet RPC.';
41
+ const DEVNET_ONLY =
42
+ 'loot runs on devnet only: mainnet waits for an audit. Unset XSOLANA_RPC_URL, or point it at a devnet RPC.';
42
43
 
43
44
  /** Refuses an RPC URL that names mainnet. Returns the URL. */
44
45
  export function notMainnet(url) {
@@ -56,17 +57,17 @@ export async function checkCluster(rpc) {
56
57
  if (genesis === GENESIS.testnet) throw new RpcError(`${rpc.url ?? 'This RPC'} is on testnet. ${DEVNET_ONLY}`);
57
58
  return genesis;
58
59
  }
59
- const decodeAccount = (raw) =>
60
+ const decodeAccount = raw =>
60
61
  raw ? { owner: raw.owner, lamports: raw.lamports, data: fromBase64(raw.data[0]), executable: raw.executable } : null;
61
62
 
62
63
  /** The program's own words for a failed run (`AnchorError … Error Message: …`), else the last error log line. A program
63
64
  * built without its errors' text logs the error's name as its message: then the name alone (the client's IDL has
64
65
  * the sentence: `loot.lootError`). */
65
66
  export function programError(err, logs) {
66
- const anchor = logs?.find((line) => line.includes('AnchorError'));
67
+ const anchor = logs?.find(line => line.includes('AnchorError'));
67
68
  const said = anchor ? /Error Message: (.+?)\.?$/.exec(anchor)?.[1] : undefined;
68
69
  const code = anchor ? /Error Code: (\w+)/.exec(anchor)?.[1] : undefined;
69
- const last = logs?.filter((l) => /error|failed|insufficient/i.test(l)).at(-1);
70
+ const last = logs?.filter(l => /error|failed|insufficient/i.test(l)).at(-1);
70
71
  if (said) return code && code !== said ? `${said} (${code})` : said;
71
72
  return last ?? (typeof err === 'string' ? err : JSON.stringify(err));
72
73
  }
@@ -117,7 +118,9 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
117
118
  // A send whose preflight failed ("Transaction simulation failed: … custom program error: 0x17b5") carries the
118
119
  // logs, where the program says why in words.
119
120
  const logs = body?.error?.data?.logs;
120
- const said = logs?.length ? `The network refused this in a dry run: ${programError(body.error.data.err, logs)}` : message;
121
+ const said = logs?.length
122
+ ? `The network refused this in a dry run: ${programError(body.error.data.err, logs)}`
123
+ : message;
121
124
  throw new RpcError(said, { code: body?.error?.code, logs });
122
125
  }
123
126
  return body.result;
@@ -128,7 +131,14 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
128
131
  const out = [];
129
132
  for (let i = 0; i < addresses.length; i += ACCOUNTS_PER_CALL) {
130
133
  const batch = addresses.slice(i, i + ACCOUNTS_PER_CALL);
131
- const r = await call('getMultipleAccounts', [batch, { encoding: 'base64', commitment, ...(minContextSlot != null ? { minContextSlot: Number(minContextSlot) } : {}) }]);
134
+ const r = await call('getMultipleAccounts', [
135
+ batch,
136
+ {
137
+ encoding: 'base64',
138
+ commitment,
139
+ ...(minContextSlot != null ? { minContextSlot: Number(minContextSlot) } : {}),
140
+ },
141
+ ]);
132
142
  out.push(...r.value.map(decodeAccount));
133
143
  }
134
144
  return out;
@@ -172,7 +182,7 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
172
182
  },
173
183
  async programAccounts(program, filters) {
174
184
  const r = await call('getProgramAccounts', [program, { encoding: 'base64', commitment: 'confirmed', filters }]);
175
- return r.map((x) => ({ pubkey: x.pubkey, account: decodeAccount(x.account) }));
185
+ return r.map(x => ({ pubkey: x.pubkey, account: decodeAccount(x.account) }));
176
186
  },
177
187
  /**
178
188
  * Dry-run a wire transaction (empty signature slots are fine). Throws with the program's reason when it fails.
@@ -190,11 +200,21 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
190
200
  },
191
201
  ]);
192
202
  const value = r.value;
193
- if (value.err) throw new RpcError(`The network refused this in a dry run: ${programError(value.err, value.logs)}`, { logs: value.logs });
194
- return { unitsConsumed: value.unitsConsumed ?? null, logs: value.logs ?? [], accounts: (value.accounts ?? []).map(decodeAccount) };
203
+ if (value.err)
204
+ throw new RpcError(`The network refused this in a dry run: ${programError(value.err, value.logs)}`, {
205
+ logs: value.logs,
206
+ });
207
+ return {
208
+ unitsConsumed: value.unitsConsumed ?? null,
209
+ logs: value.logs ?? [],
210
+ accounts: (value.accounts ?? []).map(decodeAccount),
211
+ };
195
212
  },
196
213
  async send(wire) {
197
- return call('sendTransaction', [toBase64(wire), { encoding: 'base64', preflightCommitment: 'confirmed', maxRetries: 3 }]);
214
+ return call('sendTransaction', [
215
+ toBase64(wire),
216
+ { encoding: 'base64', preflightCommitment: 'confirmed', maxRetries: 3 },
217
+ ]);
198
218
  },
199
219
  signatureStatus,
200
220
  /** Wait until a sent transaction is confirmed; throws with the program's reason when it failed. */
@@ -203,13 +223,19 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
203
223
  for (let poll = 0; Date.now() < deadline; poll++) {
204
224
  const status = await signatureStatus(signature);
205
225
  if (status?.err) {
206
- const tx = await call('getTransaction', [signature, { commitment: 'confirmed', maxSupportedTransactionVersion: 0 }]).catch(() => null);
207
- throw new RpcError(`Transaction ${signature} failed: ${programError(status.err, tx?.meta?.logMessages)}`, { logs: tx?.meta?.logMessages });
226
+ const tx = await call('getTransaction', [
227
+ signature,
228
+ { commitment: 'confirmed', maxSupportedTransactionVersion: 0 },
229
+ ]).catch(() => null);
230
+ throw new RpcError(`Transaction ${signature} failed: ${programError(status.err, tx?.meta?.logMessages)}`, {
231
+ logs: tx?.meta?.logMessages,
232
+ });
208
233
  }
209
234
  if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') return signature;
210
235
  if (lastValidBlockHeight !== null && poll % 5 === 4) {
211
236
  const height = await call('getBlockHeight', [{ commitment: 'confirmed' }]).catch(() => 0);
212
- if (height > lastValidBlockHeight) throw new RpcError(`Transaction ${signature} expired before it landed. Try again.`);
237
+ if (height > lastValidBlockHeight)
238
+ throw new RpcError(`Transaction ${signature} expired before it landed. Try again.`);
213
239
  }
214
240
  await sleepImpl(CONFIRM_POLL_MS);
215
241
  }
package/js/chain/token.js CHANGED
@@ -28,7 +28,15 @@ export function createAccountWithSeedIx({ from, address, seed, lamports, space,
28
28
  return {
29
29
  programId: SYSTEM_PROGRAM,
30
30
  keys: [meta(from, true, true), meta(address, false, true)],
31
- data: concat(uintLE(3, 4), addressBytes(from), uintLE(text.length, 8), text, uintLE(lamports, 8), uintLE(space, 8), addressBytes(owner)),
31
+ data: concat(
32
+ uintLE(3, 4),
33
+ addressBytes(from),
34
+ uintLE(text.length, 8),
35
+ text,
36
+ uintLE(lamports, 8),
37
+ uintLE(space, 8),
38
+ addressBytes(owner),
39
+ ),
32
40
  };
33
41
  }
34
42
 
@@ -81,5 +89,8 @@ export const transferCheckedIx = ({ from, mint, to, owner, amount, decimals, tok
81
89
  export const setAuthorityIx = ({ account, current, kind = 0, newAuthority, tokenProgram = TOKEN_PROGRAM }) => ({
82
90
  programId: tokenProgram,
83
91
  keys: [meta(account, false, true), meta(current, true, false)],
84
- data: concat(Uint8Array.of(6, kind), newAuthority ? concat(Uint8Array.of(1), addressBytes(newAuthority)) : Uint8Array.of(0)),
92
+ data: concat(
93
+ Uint8Array.of(6, kind),
94
+ newAuthority ? concat(Uint8Array.of(1), addressBytes(newAuthority)) : Uint8Array.of(0),
95
+ ),
85
96
  });
@@ -19,11 +19,18 @@ export const MEMO_PROGRAM = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
19
19
  export const EXECUTE_DISCRIMINATOR = Uint8Array.of(105, 37, 101, 197, 75, 251, 102, 26);
20
20
 
21
21
  const ACCOUNT_TYPE_MINT = 1;
22
- const EXTENSION = { DefaultAccountState: 6, MemoTransfer: 8, PermanentDelegate: 12, TransferHook: 14, PausableConfig: 26 };
22
+ const EXTENSION = {
23
+ DefaultAccountState: 6,
24
+ MemoTransfer: 8,
25
+ PermanentDelegate: 12,
26
+ TransferHook: 14,
27
+ PausableConfig: 26,
28
+ };
23
29
  const META_BYTES = 35;
24
30
 
25
31
  function checkedSlice(data, at, length, what) {
26
- if (!Number.isInteger(at) || !Number.isInteger(length) || at < 0 || length < 0 || at + length > data.length) throw new RangeError(`${what} runs past the data`);
32
+ if (!Number.isInteger(at) || !Number.isInteger(length) || at < 0 || length < 0 || at + length > data.length)
33
+ throw new RangeError(`${what} runs past the data`);
27
34
  return data.subarray(at, at + length);
28
35
  }
29
36
 
@@ -57,7 +64,12 @@ export function hookProgramOf(mintData) {
57
64
  * each reads the setting once, as it opens, and a refund or payout opened into a frozen account would never land.
58
65
  */
59
66
  export function mayStartFrozen({ owner, data }) {
60
- if (owner !== TOKEN_2022_PROGRAM || data.length <= TOKEN_ACCOUNT_BYTES || data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT) return false;
67
+ if (
68
+ owner !== TOKEN_2022_PROGRAM ||
69
+ data.length <= TOKEN_ACCOUNT_BYTES ||
70
+ data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT
71
+ )
72
+ return false;
61
73
  const state = extension(data, EXTENSION.DefaultAccountState);
62
74
  if (!state) return false;
63
75
  const freezer = data.length >= 50 && readUintLE(data, 46, 4) !== 0n;
@@ -73,10 +85,16 @@ export function mayStartFrozen({ owner, data }) {
73
85
  export function mintPowers({ owner, data }) {
74
86
  const out = [];
75
87
  if (owner !== TOKEN_PROGRAM && owner !== TOKEN_2022_PROGRAM) return out;
76
- const option = (at) => (data.length >= at + 36 && readUintLE(data, at, 4) === 1n ? encodeBase58(data.subarray(at + 4, at + 36)) : null);
88
+ const option = at =>
89
+ data.length >= at + 36 && readUintLE(data, at, 4) === 1n ? encodeBase58(data.subarray(at + 4, at + 36)) : null;
77
90
  const freezer = option(46);
78
91
  if (freezer) out.push({ power: 'freeze', holder: freezer });
79
- if (owner !== TOKEN_2022_PROGRAM || data.length <= TOKEN_ACCOUNT_BYTES || data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT) return out;
92
+ if (
93
+ owner !== TOKEN_2022_PROGRAM ||
94
+ data.length <= TOKEN_ACCOUNT_BYTES ||
95
+ data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT
96
+ )
97
+ return out;
80
98
  const address = (value, at) => {
81
99
  const a = value && value.length >= at + 32 ? encodeBase58(value.subarray(at, at + 32)) : null;
82
100
  return a === NO_ADDRESS ? null : a;
@@ -97,7 +115,8 @@ export function requiresMemo(accountData) {
97
115
  }
98
116
 
99
117
  /** The hook's list of extra accounts for this mint. */
100
- export const validationAddress = (mint, hookProgram) => pdaOf([utf8('extra-account-metas'), addressBytes(mint)], hookProgram);
118
+ export const validationAddress = (mint, hookProgram) =>
119
+ pdaOf([utf8('extra-account-metas'), addressBytes(mint)], hookProgram);
101
120
 
102
121
  /** The ExtraAccountMeta entries of an "extra-account-metas" account: [{ discriminator, config, isSigner, isWritable }]. */
103
122
  export function readExtraMetas(data) {
@@ -113,7 +132,12 @@ export function readExtraMetas(data) {
113
132
  return Array.from({ length: count }, (_, i) => {
114
133
  const o = at + 16 + i * META_BYTES;
115
134
  if (o + META_BYTES > at + 12 + length) throw new RangeError('extra account list runs past its entry');
116
- return { discriminator: data[o], config: data.subarray(o + 1, o + 33), isSigner: data[o + 33] !== 0, isWritable: data[o + 34] !== 0 };
135
+ return {
136
+ discriminator: data[o],
137
+ config: data.subarray(o + 1, o + 33),
138
+ isSigner: data[o + 33] !== 0,
139
+ isWritable: data[o + 34] !== 0,
140
+ };
117
141
  });
118
142
  }
119
143
  at += 12 + length;
@@ -124,7 +148,7 @@ export function readExtraMetas(data) {
124
148
  /** The seeds a PDA entry packs into its 32 bytes: [{ literal } | { ixData: [index, length] } | { key: index } | { data: [account, at, length] }]. */
125
149
  function unpackSeeds(config) {
126
150
  const seeds = [];
127
- for (let at = 0; at < config.length && config[at] !== 0; ) {
151
+ for (let at = 0; at < config.length && config[at] !== 0;) {
128
152
  const kind = config[at];
129
153
  if (kind === 1) {
130
154
  seeds.push({ literal: checkedSlice(config, at + 2, config[at + 1], 'literal seed') });
@@ -182,8 +206,12 @@ async function resolveFor(mint, hookProgram, validation, metas, transfer, read)
182
206
  }
183
207
 
184
208
  /** Whether the hook's list depends on which transfer it is (an account, the amount or account data). */
185
- const perTransfer = (metas) =>
186
- metas.some((m) => m.discriminator >= 128 || (m.discriminator === 1 && unpackSeeds(m.config).some((s) => !s.literal && !(s.key === 1 || s.key === 4))));
209
+ const perTransfer = metas =>
210
+ metas.some(
211
+ m =>
212
+ m.discriminator >= 128 ||
213
+ (m.discriminator === 1 && unpackSeeds(m.config).some(s => !s.literal && !(s.key === 1 || s.key === 4))),
214
+ );
187
215
 
188
216
  /**
189
217
  * The accounts to append after an instruction's own remaining accounts so every transfer of `mint` it makes can run:
@@ -205,13 +233,20 @@ export async function transferExtras(rpc, mint, transfers = []) {
205
233
  if (!list) throw new Error('The transfer hook account list is missing.');
206
234
  if (list.owner !== hookProgram) throw new Error('The transfer hook account list has the wrong owner.');
207
235
  const metas = readExtraMetas(list.data);
208
- const read = async (address) => (await rpc.accounts([address]))[0]?.data ?? null;
209
- const cases = perTransfer(metas) ? transfers : [{ source: NO_ADDRESS, destination: NO_ADDRESS, authority: NO_ADDRESS, amount: 0n }];
236
+ const read = async address => (await rpc.accounts([address]))[0]?.data ?? null;
237
+ const cases = perTransfer(metas)
238
+ ? transfers
239
+ : [{ source: NO_ADDRESS, destination: NO_ADDRESS, authority: NO_ADDRESS, amount: 0n }];
210
240
  if (!cases.length) throw new Error('This token’s transfer hook needs to know each transfer; pass them.');
211
- for (const transfer of cases) for (const m of await resolveFor(mint, hookProgram, validation, metas, transfer, read)) extras.push(m);
241
+ for (const transfer of cases)
242
+ for (const m of await resolveFor(mint, hookProgram, validation, metas, transfer, read)) extras.push(m);
212
243
  // One entry per address; writable if any transfer needs it writable.
213
244
  const merged = new Map();
214
- for (const m of extras) merged.set(m.pubkey, merged.has(m.pubkey) ? { ...m, isWritable: m.isWritable || merged.get(m.pubkey).isWritable } : m);
245
+ for (const m of extras)
246
+ merged.set(
247
+ m.pubkey,
248
+ merged.has(m.pubkey) ? { ...m, isWritable: m.isWritable || merged.get(m.pubkey).isWritable } : m,
249
+ );
215
250
  return [...merged.values()];
216
251
  }
217
252
 
@@ -220,7 +255,11 @@ export function associatedAccountSize(mintAccount) {
220
255
  if (mintAccount?.owner === TOKEN_PROGRAM) return TOKEN_ACCOUNT_BYTES;
221
256
  if (mintAccount?.owner !== TOKEN_2022_PROGRAM) throw new Error('The mint is not owned by a supported token program.');
222
257
  const data = mintAccount.data;
223
- if (data.length !== MINT_BYTES && (data.length < TOKEN_ACCOUNT_BYTES + 1 || data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT)) throw new Error('Invalid Token-2022 mint layout.');
258
+ if (
259
+ data.length !== MINT_BYTES &&
260
+ (data.length < TOKEN_ACCOUNT_BYTES + 1 || data[TOKEN_ACCOUNT_BYTES] !== ACCOUNT_TYPE_MINT)
261
+ )
262
+ throw new Error('Invalid Token-2022 mint layout.');
224
263
  const required = new Map([[7, 0]]); // ImmutableOwner, set by the associated token program.
225
264
  for (let at = TOKEN_ACCOUNT_BYTES + 1; at + 4 <= data.length;) {
226
265
  const kind = Number(readUintLE(data, at, 2));
@@ -242,6 +281,6 @@ export async function resolveTransfers(rpc, mint, transfers = []) {
242
281
  const [account] = await rpc.accounts([mint]);
243
282
  if (!account) throw new Error(`There is no token ${mint} on this network.`);
244
283
  const accountSize = associatedAccountSize(account);
245
- const cached = { accounts: async (keys) => keys.length === 1 && keys[0] === mint ? [account] : rpc.accounts(keys) };
284
+ const cached = { accounts: async keys => (keys.length === 1 && keys[0] === mint ? [account] : rpc.accounts(keys)) };
246
285
  return { tokenProgram: account.owner, accountSize, extraAccounts: await transferExtras(cached, mint, transfers) };
247
286
  }
package/js/chain/tx.js CHANGED
@@ -33,14 +33,14 @@ function mergedAccounts(payer, instructions) {
33
33
  }
34
34
 
35
35
  /** The order a message lists accounts in: signers first, writable before read-only; payer at the head. */
36
- const rank = (m) => (m.isSigner ? 0 : 2) + (m.isWritable ? 0 : 1);
36
+ const rank = m => (m.isSigner ? 0 : 2) + (m.isWritable ? 0 : 1);
37
37
 
38
38
  function compileInstructions(instructions, index) {
39
- return instructions.map((ix) =>
39
+ return instructions.map(ix =>
40
40
  concat(
41
41
  Uint8Array.of(index.get(ix.programId)),
42
42
  compactU16(ix.keys.length),
43
- Uint8Array.from(ix.keys.map((k) => index.get(k.pubkey))),
43
+ Uint8Array.from(ix.keys.map(k => index.get(k.pubkey))),
44
44
  compactU16(ix.data.length),
45
45
  ix.data,
46
46
  ),
@@ -57,10 +57,10 @@ export function compileMessage({ payer, blockhash, instructions, version = 'lega
57
57
  const lookups = [];
58
58
  if (version === 0) {
59
59
  for (const table of lookupTables) {
60
- const drain = (writable) => {
60
+ const drain = writable => {
61
61
  const indexes = [];
62
62
  const keys = [];
63
- accounts = accounts.filter((m) => {
63
+ accounts = accounts.filter(m => {
64
64
  if (m.isSigner || m.invoked || m.isWritable !== writable) return true;
65
65
  const at = table.addresses.indexOf(m.pubkey);
66
66
  if (at < 0) return true;
@@ -74,30 +74,36 @@ export function compileMessage({ payer, blockhash, instructions, version = 'lega
74
74
  const w = drain(true);
75
75
  const r = drain(false);
76
76
  if (w.indexes.length || r.indexes.length)
77
- lookups.push({ address: table.address, writable: w.indexes, readonly: r.indexes, writableKeys: w.keys, readonlyKeys: r.keys });
77
+ lookups.push({
78
+ address: table.address,
79
+ writable: w.indexes,
80
+ readonly: r.indexes,
81
+ writableKeys: w.keys,
82
+ readonlyKeys: r.keys,
83
+ });
78
84
  }
79
85
  }
80
86
  const [head, ...rest] = accounts;
81
87
  const statics = [head, ...rest.sort((a, b) => rank(a) - rank(b))];
82
- const loaded = [...lookups.flatMap((l) => l.writableKeys), ...lookups.flatMap((l) => l.readonlyKeys)];
88
+ const loaded = [...lookups.flatMap(l => l.writableKeys), ...lookups.flatMap(l => l.readonlyKeys)];
83
89
  const index = new Map([...statics.map((m, i) => [m.pubkey, i]), ...loaded.map((k, i) => [k, statics.length + i])]);
84
90
  if (index.size > 256) throw new Error('Too many accounts for one transaction');
85
- const signers = statics.filter((m) => m.isSigner);
91
+ const signers = statics.filter(m => m.isSigner);
86
92
  const header = Uint8Array.of(
87
93
  signers.length,
88
- signers.filter((m) => !m.isWritable).length,
89
- statics.filter((m) => !m.isSigner && !m.isWritable).length,
94
+ signers.filter(m => !m.isWritable).length,
95
+ statics.filter(m => !m.isSigner && !m.isWritable).length,
90
96
  );
91
97
  const body = concat(
92
98
  header,
93
99
  compactU16(statics.length),
94
- ...statics.map((m) => addressBytes(m.pubkey)),
100
+ ...statics.map(m => addressBytes(m.pubkey)),
95
101
  addressBytes(blockhash),
96
102
  compactU16(instructions.length),
97
103
  ...compileInstructions(instructions, index),
98
104
  );
99
105
  if (version !== 0) return body;
100
- const tables = lookups.map((l) =>
106
+ const tables = lookups.map(l =>
101
107
  concat(
102
108
  addressBytes(l.address),
103
109
  compactU16(l.writable.length),
@@ -122,13 +128,13 @@ export function readMessage(message) {
122
128
  at += n;
123
129
  return bytes;
124
130
  };
125
- const length = (what) => {
131
+ const length = what => {
126
132
  if (at >= message.length) throw new Error(`Truncated message: ${what} runs past its end`);
127
133
  const prefix = readCompactU16(message, at);
128
134
  at += prefix.size;
129
135
  return prefix.value;
130
136
  };
131
- const address = (what) => encodeBase58(take(32, what));
137
+ const address = what => encodeBase58(take(32, what));
132
138
  let version = 'legacy';
133
139
  if (message.length && message[0] & V0_PREFIX) {
134
140
  version = message[0] & 0x7f;
@@ -145,8 +151,8 @@ export function readMessage(message) {
145
151
  const instructions = [];
146
152
  for (let i = 0; i < ixCount; i++) {
147
153
  const [programIndex] = take(1, 'an instruction');
148
- const accountIndexes = Array.from(take(length('an instruction\'s accounts'), 'an instruction\'s accounts'));
149
- const data = take(length('an instruction\'s data'), 'an instruction\'s data').slice();
154
+ const accountIndexes = Array.from(take(length("an instruction's accounts"), "an instruction's accounts"));
155
+ const data = take(length("an instruction's data"), "an instruction's data").slice();
150
156
  instructions.push({ programIndex, accountIndexes, data });
151
157
  }
152
158
  const lookups = [];
@@ -154,8 +160,12 @@ export function readMessage(message) {
154
160
  const n = length('the lookup table count');
155
161
  for (let i = 0; i < n; i++) {
156
162
  const tableAddress = address('a lookup table');
157
- const writable = Array.from(take(length('a lookup table\'s writable indexes'), 'a lookup table\'s writable indexes'));
158
- const readonly = Array.from(take(length('a lookup table\'s read-only indexes'), 'a lookup table\'s read-only indexes'));
163
+ const writable = Array.from(
164
+ take(length("a lookup table's writable indexes"), "a lookup table's writable indexes"),
165
+ );
166
+ const readonly = Array.from(
167
+ take(length("a lookup table's read-only indexes"), "a lookup table's read-only indexes"),
168
+ );
159
169
  lookups.push({ address: tableAddress, writable, readonly });
160
170
  }
161
171
  }
@@ -173,7 +183,7 @@ export function signersOf(message) {
173
183
  export function wireTransaction(message, signatures = []) {
174
184
  const count = readMessage(message).header.numRequiredSignatures;
175
185
  const slots = Array.from({ length: count }, (_, i) => signatures[i] ?? new Uint8Array(SIGNATURE_BYTES));
176
- if (slots.some((s) => s.length !== SIGNATURE_BYTES)) throw new Error('A signature is 64 bytes');
186
+ if (slots.some(s => s.length !== SIGNATURE_BYTES)) throw new Error('A signature is 64 bytes');
177
187
  return concat(compactU16(count), ...slots, message);
178
188
  }
179
189
 
@@ -203,16 +213,16 @@ export function withSignature(wire, signer, signature) {
203
213
  }
204
214
 
205
215
  /** The transaction's id: its first signature, in base58. */
206
- export const transactionId = (wire) => encodeBase58(readTransaction(wire).signatures[0]);
216
+ export const transactionId = wire => encodeBase58(readTransaction(wire).signatures[0]);
207
217
 
208
218
  /** Whether a signature slot is still empty. */
209
- export const isEmptySignature = (signature) => signature.every((b) => b === 0);
219
+ export const isEmptySignature = signature => signature.every(b => b === 0);
210
220
 
211
221
  /** Whether two wire transactions carry the same message. */
212
222
  export const sameMessage = (a, b) => equalBytes(readTransaction(a).message, readTransaction(b).message);
213
223
 
214
224
  /** The size this message's transaction takes on the wire. */
215
- export const transactionSize = (message) => wireTransaction(message).length;
225
+ export const transactionSize = message => wireTransaction(message).length;
216
226
 
217
227
  /** Whether these instructions fit one transaction from this payer. */
218
228
  export function fits(input) {