@xgamesfun/solana 0.0.0-stage → 0.24.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/DISCLAIMER.md +41 -0
- package/LICENSE +202 -0
- package/NOTICE +13 -0
- package/README.md +172 -2
- package/bin/xsolana.mjs +189 -0
- package/examples/artwork-auction.json +51 -0
- package/examples/blind-forge.json +128 -0
- package/examples/coin-flip-arena.json +103 -0
- package/examples/duel-arena.json +137 -0
- package/examples/elements-set.json +274 -0
- package/examples/event-badges.json +67 -0
- package/examples/expedition-guild.json +154 -0
- package/examples/fair-presale.json +48 -0
- package/examples/falling-drop.json +90 -0
- package/examples/guild-missions.json +137 -0
- package/examples/halving-seasons.json +107 -0
- package/examples/hero-trials.json +93 -0
- package/examples/ideas.json +789 -0
- package/examples/level-ladder.json +65 -0
- package/examples/merge-2048.json +128 -0
- package/examples/nft-raffle.json +33 -0
- package/examples/paper-hands-farm.json +44 -0
- package/examples/partner-vault.json +112 -0
- package/examples/sacrifice-raffle.json +118 -0
- package/examples/scorched-treasury.json +84 -0
- package/examples/shelter-fair.json +164 -0
- package/examples/spark-pity.json +153 -0
- package/examples/stakers-draw.json +118 -0
- package/examples/star-fleet.json +150 -0
- package/examples/starforce.json +116 -0
- package/examples/stud-farm.json +134 -0
- package/examples/team-vesting.json +70 -0
- package/examples/trade-in-shop.json +168 -0
- package/js/approve/details.mjs +263 -0
- package/js/approve/ed25519.mjs +36 -0
- package/js/approve/live-check.mjs +144 -0
- package/js/approve/page.html +86 -0
- package/js/approve/page.js +203 -0
- package/js/approve/server.mjs +726 -0
- package/js/chain/base58.js +73 -0
- package/js/chain/borsh.js +270 -0
- package/js/chain/bytes.js +141 -0
- package/js/chain/core.js +181 -0
- package/js/chain/curve.js +52 -0
- package/js/chain/lookup.js +279 -0
- package/js/chain/mint.js +96 -0
- package/js/chain/pda.js +66 -0
- package/js/chain/programs.js +102 -0
- package/js/chain/rpc.js +219 -0
- package/js/chain/sha256.js +68 -0
- package/js/chain/token.js +85 -0
- package/js/chain/token2022.js +247 -0
- package/js/chain/tx.js +224 -0
- package/js/client/accounts.js +679 -0
- package/js/client/compressed.js +46 -0
- package/js/client/grant.js +299 -0
- package/js/client/idl/loot.js +2 -0
- package/js/client/idl/loot.json +17206 -0
- package/js/client/index.js +10 -0
- package/js/client/ix.js +2887 -0
- package/js/client/pda.js +114 -0
- package/js/client/policy.js +69 -0
- package/js/client/random.js +70 -0
- package/js/client/sizes.js +297 -0
- package/js/client/traits.js +44 -0
- package/js/connect/connect.mjs +182 -0
- package/js/economy/check.mjs +2236 -0
- package/js/economy/cost.mjs +185 -0
- package/js/economy/examples.mjs +114 -0
- package/js/economy/explain.mjs +239 -0
- package/js/economy/guide.mjs +232 -0
- package/js/economy/legal.mjs +15 -0
- package/js/economy/live.mjs +130 -0
- package/js/economy/model.mjs +698 -0
- package/js/economy/plan.mjs +1155 -0
- package/js/economy/schema.json +3071 -0
- package/js/economy/secrets.mjs +60 -0
- package/js/economy/simulate.mjs +1019 -0
- package/js/economy/txsize.mjs +45 -0
- package/js/economy/validate.mjs +85 -0
- package/js/index.js +10 -0
- package/js/mcp/server.mjs +113 -0
- package/js/mcp/tools.mjs +485 -0
- package/js/play/index.js +2855 -0
- package/js/play/keys.js +167 -0
- package/js/results/index.js +184 -0
- package/js/results/wire.js +90 -0
- package/package.json +100 -4
- package/release.json +8 -0
- package/skills/xsolana-economy/SKILL.md +109 -0
- package/skills/xsolana-economy/references/after-launch.md +124 -0
- package/skills/xsolana-economy/references/apps.md +193 -0
- package/skills/xsolana-economy/references/sentence-to-spec.md +180 -0
- package/skills/xsolana-economy/references/telling-the-person.md +134 -0
- package/types/chain/base58.d.ts +10 -0
- package/types/chain/borsh.d.ts +23 -0
- package/types/chain/bytes.d.ts +45 -0
- package/types/chain/core.d.ts +52 -0
- package/types/chain/curve.d.ts +1 -0
- package/types/chain/lookup.d.ts +194 -0
- package/types/chain/mint.d.ts +25 -0
- package/types/chain/pda.d.ts +25 -0
- package/types/chain/programs.d.ts +65 -0
- package/types/chain/rpc.d.ts +68 -0
- package/types/chain/sha256.d.ts +1 -0
- package/types/chain/token.d.ts +141 -0
- package/types/chain/token2022.d.ts +53 -0
- package/types/chain/tx.d.ts +60 -0
- package/types/client/accounts.d.ts +318 -0
- package/types/client/compressed.d.ts +29 -0
- package/types/client/grant.d.ts +170 -0
- package/types/client/idl/loot.d.ts +4498 -0
- package/types/client/index.d.ts +9 -0
- package/types/client/ix.d.ts +5315 -0
- package/types/client/pda.d.ts +95 -0
- package/types/client/policy.d.ts +48 -0
- package/types/client/random.d.ts +26 -0
- package/types/client/sizes.d.ts +101 -0
- package/types/client/traits.d.ts +12 -0
- package/types/economy/check.d.mts +100 -0
- package/types/economy/cost.d.mts +69 -0
- package/types/economy/examples.d.mts +27 -0
- package/types/economy/explain.d.mts +19 -0
- package/types/economy/guide.d.mts +140 -0
- package/types/economy/legal.d.mts +12 -0
- package/types/economy/live.d.mts +124 -0
- package/types/economy/model.d.mts +431 -0
- package/types/economy/plan.d.mts +1014 -0
- package/types/economy/secrets.d.mts +4 -0
- package/types/economy/simulate.d.mts +164 -0
- package/types/economy/txsize.d.mts +17 -0
- package/types/economy/validate.d.mts +3 -0
- package/types/index.d.ts +9 -0
- package/types/mcp/tools.d.mts +635 -0
- package/types/play/index.d.ts +880 -0
- package/types/play/keys.d.ts +51 -0
- package/types/results/index.d.ts +155 -0
- package/types/results/wire.d.ts +122 -0
package/js/chain/tx.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Solana transactions without @solana/web3.js: compile instructions into a legacy or v0 message (v0 can load
|
|
2
|
+
// accounts from address lookup tables), wrap a message with signature slots, read a wire transaction back, and put
|
|
3
|
+
// a signature in its slot. Used by browser apps and Node alike.
|
|
4
|
+
import { addressBytes, encodeBase58 } from './base58.js';
|
|
5
|
+
import { compactU16, concat, equalBytes, readCompactU16 } from './bytes.js';
|
|
6
|
+
|
|
7
|
+
/** A serialized transaction must fit one packet. */
|
|
8
|
+
export const MAX_TRANSACTION_BYTES = 1232;
|
|
9
|
+
export const SIGNATURE_BYTES = 64;
|
|
10
|
+
const V0_PREFIX = 0x80;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Every account once, in first-seen order: the payer, then each instruction's program and its accounts. Signing or
|
|
14
|
+
* writable anywhere means signing or writable. Same order as @solana/web3.js's MessageV0.compile.
|
|
15
|
+
*/
|
|
16
|
+
function mergedAccounts(payer, instructions) {
|
|
17
|
+
const merged = new Map([[payer, { pubkey: payer, isSigner: true, isWritable: true, invoked: false }]]);
|
|
18
|
+
const add = (pubkey, isSigner, isWritable, invoked) => {
|
|
19
|
+
addressBytes(pubkey);
|
|
20
|
+
const seen = merged.get(pubkey);
|
|
21
|
+
if (!seen) merged.set(pubkey, { pubkey, isSigner, isWritable, invoked });
|
|
22
|
+
else {
|
|
23
|
+
seen.isSigner ||= isSigner;
|
|
24
|
+
seen.isWritable ||= isWritable;
|
|
25
|
+
seen.invoked ||= invoked;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
for (const ix of instructions) {
|
|
29
|
+
add(ix.programId, false, false, true);
|
|
30
|
+
for (const key of ix.keys) add(key.pubkey, key.isSigner, key.isWritable, false);
|
|
31
|
+
}
|
|
32
|
+
return [...merged.values()];
|
|
33
|
+
}
|
|
34
|
+
|
|
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);
|
|
37
|
+
|
|
38
|
+
function compileInstructions(instructions, index) {
|
|
39
|
+
return instructions.map((ix) =>
|
|
40
|
+
concat(
|
|
41
|
+
Uint8Array.of(index.get(ix.programId)),
|
|
42
|
+
compactU16(ix.keys.length),
|
|
43
|
+
Uint8Array.from(ix.keys.map((k) => index.get(k.pubkey))),
|
|
44
|
+
compactU16(ix.data.length),
|
|
45
|
+
ix.data,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A message. `version` is 'legacy' (default) or 0. For v0, `lookupTables` is [{ address, addresses: [base58] }]:
|
|
52
|
+
* read-only and writable accounts that are neither signers nor invoked programs load from a table when it holds them.
|
|
53
|
+
*/
|
|
54
|
+
export function compileMessage({ payer, blockhash, instructions, version = 'legacy', lookupTables = [] }) {
|
|
55
|
+
if (!instructions?.length) throw new Error('A transaction needs at least one instruction');
|
|
56
|
+
let accounts = mergedAccounts(payer, instructions);
|
|
57
|
+
const lookups = [];
|
|
58
|
+
if (version === 0) {
|
|
59
|
+
for (const table of lookupTables) {
|
|
60
|
+
const drain = (writable) => {
|
|
61
|
+
const indexes = [];
|
|
62
|
+
const keys = [];
|
|
63
|
+
accounts = accounts.filter((m) => {
|
|
64
|
+
if (m.isSigner || m.invoked || m.isWritable !== writable) return true;
|
|
65
|
+
const at = table.addresses.indexOf(m.pubkey);
|
|
66
|
+
if (at < 0) return true;
|
|
67
|
+
if (at > 255) throw new Error('Lookup table index past 255');
|
|
68
|
+
indexes.push(at);
|
|
69
|
+
keys.push(m.pubkey);
|
|
70
|
+
return false;
|
|
71
|
+
});
|
|
72
|
+
return { indexes, keys };
|
|
73
|
+
};
|
|
74
|
+
const w = drain(true);
|
|
75
|
+
const r = drain(false);
|
|
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 });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const [head, ...rest] = accounts;
|
|
81
|
+
const statics = [head, ...rest.sort((a, b) => rank(a) - rank(b))];
|
|
82
|
+
const loaded = [...lookups.flatMap((l) => l.writableKeys), ...lookups.flatMap((l) => l.readonlyKeys)];
|
|
83
|
+
const index = new Map([...statics.map((m, i) => [m.pubkey, i]), ...loaded.map((k, i) => [k, statics.length + i])]);
|
|
84
|
+
if (index.size > 256) throw new Error('Too many accounts for one transaction');
|
|
85
|
+
const signers = statics.filter((m) => m.isSigner);
|
|
86
|
+
const header = Uint8Array.of(
|
|
87
|
+
signers.length,
|
|
88
|
+
signers.filter((m) => !m.isWritable).length,
|
|
89
|
+
statics.filter((m) => !m.isSigner && !m.isWritable).length,
|
|
90
|
+
);
|
|
91
|
+
const body = concat(
|
|
92
|
+
header,
|
|
93
|
+
compactU16(statics.length),
|
|
94
|
+
...statics.map((m) => addressBytes(m.pubkey)),
|
|
95
|
+
addressBytes(blockhash),
|
|
96
|
+
compactU16(instructions.length),
|
|
97
|
+
...compileInstructions(instructions, index),
|
|
98
|
+
);
|
|
99
|
+
if (version !== 0) return body;
|
|
100
|
+
const tables = lookups.map((l) =>
|
|
101
|
+
concat(
|
|
102
|
+
addressBytes(l.address),
|
|
103
|
+
compactU16(l.writable.length),
|
|
104
|
+
Uint8Array.from(l.writable),
|
|
105
|
+
compactU16(l.readonly.length),
|
|
106
|
+
Uint8Array.from(l.readonly),
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
return concat(Uint8Array.of(V0_PREFIX), body, compactU16(tables.length), ...tables);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The parts of a message: version, header, static keys, blockhash and compiled instructions. Throws, saying so, on a
|
|
114
|
+
* message cut short or with bytes after its end.
|
|
115
|
+
*/
|
|
116
|
+
export function readMessage(message) {
|
|
117
|
+
let at = 0;
|
|
118
|
+
/** The next `n` bytes, refusing to read past the end. */
|
|
119
|
+
const take = (n, what) => {
|
|
120
|
+
if (at + n > message.length) throw new Error(`Truncated message: ${what} runs past its end`);
|
|
121
|
+
const bytes = message.subarray(at, at + n);
|
|
122
|
+
at += n;
|
|
123
|
+
return bytes;
|
|
124
|
+
};
|
|
125
|
+
const length = (what) => {
|
|
126
|
+
if (at >= message.length) throw new Error(`Truncated message: ${what} runs past its end`);
|
|
127
|
+
const prefix = readCompactU16(message, at);
|
|
128
|
+
at += prefix.size;
|
|
129
|
+
return prefix.value;
|
|
130
|
+
};
|
|
131
|
+
const address = (what) => encodeBase58(take(32, what));
|
|
132
|
+
let version = 'legacy';
|
|
133
|
+
if (message.length && message[0] & V0_PREFIX) {
|
|
134
|
+
version = message[0] & 0x7f;
|
|
135
|
+
if (version !== 0) throw new Error(`Unsupported transaction version ${version}`);
|
|
136
|
+
at = 1;
|
|
137
|
+
}
|
|
138
|
+
const [numRequiredSignatures, numReadonlySigned, numReadonlyUnsigned] = take(3, 'the header');
|
|
139
|
+
const header = { numRequiredSignatures, numReadonlySigned, numReadonlyUnsigned };
|
|
140
|
+
const keyCount = length('the account count');
|
|
141
|
+
const keys = [];
|
|
142
|
+
for (let i = 0; i < keyCount; i++) keys.push(address('an account'));
|
|
143
|
+
const blockhash = address('the blockhash');
|
|
144
|
+
const ixCount = length('the instruction count');
|
|
145
|
+
const instructions = [];
|
|
146
|
+
for (let i = 0; i < ixCount; i++) {
|
|
147
|
+
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();
|
|
150
|
+
instructions.push({ programIndex, accountIndexes, data });
|
|
151
|
+
}
|
|
152
|
+
const lookups = [];
|
|
153
|
+
if (version === 0) {
|
|
154
|
+
const n = length('the lookup table count');
|
|
155
|
+
for (let i = 0; i < n; i++) {
|
|
156
|
+
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'));
|
|
159
|
+
lookups.push({ address: tableAddress, writable, readonly });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (at !== message.length) throw new Error('Trailing bytes after the message');
|
|
163
|
+
return { version, header, keys, blockhash, instructions, lookups };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The addresses that must sign a message, in signature-slot order. */
|
|
167
|
+
export function signersOf(message) {
|
|
168
|
+
const { header, keys } = readMessage(message);
|
|
169
|
+
return keys.slice(0, header.numRequiredSignatures);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The wire form: one signature slot per required signer (empty unless given), then the message. */
|
|
173
|
+
export function wireTransaction(message, signatures = []) {
|
|
174
|
+
const count = readMessage(message).header.numRequiredSignatures;
|
|
175
|
+
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');
|
|
177
|
+
return concat(compactU16(count), ...slots, message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** A wire transaction read back: its signatures, its message bytes and its signers. */
|
|
181
|
+
export function readTransaction(wire) {
|
|
182
|
+
const count = readCompactU16(wire, 0);
|
|
183
|
+
let at = count.size;
|
|
184
|
+
const signatures = [];
|
|
185
|
+
for (let i = 0; i < count.value; i++) {
|
|
186
|
+
if (at + SIGNATURE_BYTES > wire.length) throw new Error('Truncated signatures');
|
|
187
|
+
signatures.push(wire.slice(at, at + SIGNATURE_BYTES));
|
|
188
|
+
at += SIGNATURE_BYTES;
|
|
189
|
+
}
|
|
190
|
+
const message = wire.slice(at);
|
|
191
|
+
const signers = signersOf(message);
|
|
192
|
+
if (signers.length !== signatures.length) throw new Error('Signature count does not match the message');
|
|
193
|
+
return { signatures, message, signers };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** A copy of the wire transaction with `signer`'s slot holding `signature`. */
|
|
197
|
+
export function withSignature(wire, signer, signature) {
|
|
198
|
+
const { signatures, message, signers } = readTransaction(wire);
|
|
199
|
+
const at = signers.indexOf(signer);
|
|
200
|
+
if (at < 0) throw new Error(`${signer} does not sign this transaction`);
|
|
201
|
+
signatures[at] = signature;
|
|
202
|
+
return wireTransaction(message, signatures);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** The transaction's id: its first signature, in base58. */
|
|
206
|
+
export const transactionId = (wire) => encodeBase58(readTransaction(wire).signatures[0]);
|
|
207
|
+
|
|
208
|
+
/** Whether a signature slot is still empty. */
|
|
209
|
+
export const isEmptySignature = (signature) => signature.every((b) => b === 0);
|
|
210
|
+
|
|
211
|
+
/** Whether two wire transactions carry the same message. */
|
|
212
|
+
export const sameMessage = (a, b) => equalBytes(readTransaction(a).message, readTransaction(b).message);
|
|
213
|
+
|
|
214
|
+
/** The size this message's transaction takes on the wire. */
|
|
215
|
+
export const transactionSize = (message) => wireTransaction(message).length;
|
|
216
|
+
|
|
217
|
+
/** Whether these instructions fit one transaction from this payer. */
|
|
218
|
+
export function fits(input) {
|
|
219
|
+
try {
|
|
220
|
+
return transactionSize(compileMessage(input)) <= MAX_TRANSACTION_BYTES;
|
|
221
|
+
} catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|