@opeqlabs/sdk 0.0.0-stage → 0.1.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.
@@ -0,0 +1,129 @@
1
+ import { Sha256, sha256 } from "./sha256.js";
2
+ import { N, LEAVES, verifyCost } from "./wots.js";
3
+ import { concat, findPda, u32, u64 } from "./solana.js";
4
+
5
+ const enc = new TextEncoder();
6
+
7
+ export const IX_INIT = 0, IX_EXEC = 1, IX_REKEY = 2;
8
+ export const STATE_MAGIC = "OPEQST01";
9
+ export const STATE_LEN = 88;
10
+ export const F_SIGNER = 1, F_WRITABLE = 2;
11
+ export const R_LEN = 16;
12
+ export const REKEY_RESERVE = 16;
13
+ export const EXEC_LEAF_LIMIT = LEAVES - REKEY_RESERVE;
14
+ export const GRIND_TRIES = 1 << 17;
15
+ export const SALT_OFF = 13, R_OFF = 17, SIG_OFF = 33;
16
+ export const TAG_EXEC = enc.encode("opeq/exec/v1");
17
+ export const TAG_REKEY = enc.encode("opeq/rekey/v1");
18
+
19
+ export const ERRORS = {
20
+ 1: "bad instruction", 2: "too many accounts", 3: "state not owned by program", 4: "bad state account",
21
+ 5: "wallet/state address mismatch", 6: "signature expired", 7: "leaf already used", 8: "leaf out of range",
22
+ 9: "self cpi", 10: "bad meta flags", 11: "signer escalation", 12: "malformed inner instructions",
23
+ 13: "bad signature", 14: "state not writable", 15: "init address mismatch", 16: "too many slices",
24
+ 17: "account index out of range", 18: "payer must sign", 19: "already initialized", 20: "bad system program",
25
+ 21: "too many metas", 22: "rekey to the current root", 23: "expiry too far in the future",
26
+ };
27
+
28
+ export function accountId(pubSeed, root) {
29
+ return sha256(enc.encode("opeq/id/v1"), pubSeed, root);
30
+ }
31
+
32
+ export const statePda = (aid, program) => findPda([enc.encode("s"), aid], program);
33
+ export const walletPda = (aid, program) => findPda([enc.encode("w"), aid], program);
34
+
35
+ function leafEpochExpiry(epoch, leaf, expiry) {
36
+ return concat(u32(epoch), u32(leaf), u64(expiry));
37
+ }
38
+
39
+ export function execHasher(program, aid, epoch, leaf, expiry, inner, r) {
40
+ checkInner(inner);
41
+ if (r.length !== N) throw new Error("randomizer must be 16 bytes");
42
+ const h = new Sha256();
43
+ h.update(r).update(TAG_EXEC).update(program).update(aid).update(leafEpochExpiry(epoch, leaf, expiry));
44
+ h.update(new Uint8Array([inner.length]));
45
+ for (const [prog, metas, data] of inner) {
46
+ h.update(prog).update(new Uint8Array([metas.length]));
47
+ for (const [key, signer, writable] of metas) {
48
+ h.update(key).update(new Uint8Array([(signer ? F_SIGNER : 0) | (writable ? F_WRITABLE : 0)]));
49
+ }
50
+ h.update(new Uint8Array([data.length & 0xff, data.length >> 8])).update(data);
51
+ }
52
+ return h;
53
+ }
54
+
55
+ export function rekeyHasher(program, aid, epoch, leaf, expiry, newPubSeed, newRoot, r) {
56
+ if (r.length !== N) throw new Error("randomizer must be 16 bytes");
57
+ return new Sha256().update(r).update(TAG_REKEY).update(program).update(aid)
58
+ .update(leafEpochExpiry(epoch, leaf, expiry)).update(newPubSeed).update(newRoot);
59
+ }
60
+
61
+ export function salted(hasher, salt) {
62
+ return hasher.clone().update(u32(salt)).digest().slice(0, N);
63
+ }
64
+
65
+ export function grind(hasher, tries = GRIND_TRIES, progress) {
66
+ let best = null;
67
+ const sb = new Uint8Array(4);
68
+ for (let salt = 0; salt < tries; salt++) {
69
+ sb[0] = salt & 0xff; sb[1] = (salt >>> 8) & 0xff; sb[2] = (salt >>> 16) & 0xff; sb[3] = salt >>> 24;
70
+ const d = hasher.clone().update(sb).digest().slice(0, N);
71
+ const c = verifyCost(d);
72
+ if (!best || c < best.cost) best = { salt, digest: d, cost: c };
73
+ if (progress && (salt & 8191) === 8191) progress((salt + 1) / tries);
74
+ }
75
+ return best;
76
+ }
77
+
78
+ function header(tag, leaf, expiry, salt, r) {
79
+ return concat(new Uint8Array([tag]), u32(leaf), u64(expiry), u32(salt), r);
80
+ }
81
+
82
+ function checkInner(inner) {
83
+ if (inner.length > 255) throw new Error("too many inner instructions");
84
+ for (const [, metas, data] of inner) {
85
+ if (metas.length > 64) throw new Error("too many accounts in an inner instruction");
86
+ if (data.length > 0xffff) throw new Error("inner instruction data too long");
87
+ }
88
+ }
89
+
90
+ export function buildExec(leaf, expiry, salt, r, sig, path, inner, outerKeys) {
91
+ checkInner(inner);
92
+ const pos = new Map(outerKeys.map((k, i) => [k.join(","), i]));
93
+ const at = (k) => { const i = pos.get(k.join(",")); if (i === undefined || i > 255) throw new Error("inner instruction key missing from the transaction"); return i; };
94
+ const parts = [header(IX_EXEC, leaf, expiry, salt, r), sig, path, new Uint8Array([inner.length])];
95
+ for (const [prog, metas, data] of inner) {
96
+ parts.push(new Uint8Array([at(prog), metas.length]));
97
+ for (const [key, signer, writable] of metas) {
98
+ parts.push(new Uint8Array([at(key), (signer ? F_SIGNER : 0) | (writable ? F_WRITABLE : 0)]));
99
+ }
100
+ parts.push(new Uint8Array([data.length & 0xff, data.length >> 8]), data);
101
+ }
102
+ return concat(...parts);
103
+ }
104
+
105
+ export function buildRekey(leaf, expiry, salt, r, sig, path, newPubSeed, newRoot) {
106
+ return concat(header(IX_REKEY, leaf, expiry, salt, r), sig, path, newPubSeed, newRoot);
107
+ }
108
+
109
+ export function buildInit(pubSeed, root, lamports) {
110
+ return concat(new Uint8Array([IX_INIT]), pubSeed, root, u64(lamports));
111
+ }
112
+
113
+ export function unpackState(data) {
114
+ if (data.length < STATE_LEN || new TextDecoder().decode(data.slice(0, 8)) !== STATE_MAGIC) throw new Error("not an opeq state account");
115
+ const dv = new DataView(data.buffer, data.byteOffset);
116
+ return {
117
+ id: data.slice(8, 40), pubSeed: data.slice(40, 56), root: data.slice(56, 72),
118
+ next: dv.getUint32(72, true), epoch: dv.getUint32(76, true), sbump: data[80], wbump: data[81],
119
+ };
120
+ }
121
+
122
+ export function outerLayout(state, wallet, inner) {
123
+ const outer = [state, wallet];
124
+ const has = (k) => outer.some((o) => o.join(",") === k.join(","));
125
+ for (const [prog, metas] of inner) for (const k of [prog, ...metas.map((m) => m[0])]) if (!has(k)) outer.push(k);
126
+ const writable = new Set([state.join(","), wallet.join(",")]);
127
+ for (const [, metas] of inner) for (const [k, , w] of metas) if (w) writable.add(k.join(","));
128
+ return { outer, metas: outer.map((k) => [k, false, writable.has(k.join(","))]) };
129
+ }
@@ -0,0 +1,123 @@
1
+ import { sha256 } from "./sha256.js";
2
+ import { keccak256 } from "./keccak.js";
3
+
4
+ const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
5
+ const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
6
+ const GX = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
7
+ const GY = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
8
+ const enc = new TextEncoder();
9
+
10
+ const mod = (a, m = P) => { const r = a % m; return r < 0n ? r + m : r; };
11
+
12
+ function inv(a, m = P) {
13
+ let [r0, r1, t0, t1] = [mod(a, m), m, 1n, 0n];
14
+ while (r1 !== 0n) {
15
+ const q = r0 / r1;
16
+ [r0, r1] = [r1, r0 - q * r1];
17
+ [t0, t1] = [t1, t0 - q * t1];
18
+ }
19
+ if (r0 !== 1n) throw new Error("no inverse");
20
+ return mod(t0, m);
21
+ }
22
+
23
+ function dbl([x, y, z]) {
24
+ if (y === 0n || z === 0n) return [0n, 1n, 0n];
25
+ const a = mod(x * x), b = mod(y * y), c = mod(b * b);
26
+ const d = mod(2n * (mod((x + b) * (x + b)) - a - c));
27
+ const e = mod(3n * a), f = mod(e * e);
28
+ const x3 = mod(f - 2n * d);
29
+ return [x3, mod(e * (d - x3) - 8n * c), mod(2n * y * z)];
30
+ }
31
+
32
+ function add(p1, p2) {
33
+ if (p1[2] === 0n) return p2;
34
+ if (p2[2] === 0n) return p1;
35
+ const [x1, y1, z1] = p1, [x2, y2, z2] = p2;
36
+ const z1z1 = mod(z1 * z1), z2z2 = mod(z2 * z2);
37
+ const u1 = mod(x1 * z2z2), u2 = mod(x2 * z1z1);
38
+ const s1 = mod(y1 * z2 * z2z2), s2 = mod(y2 * z1 * z1z1);
39
+ if (u1 === u2) return s1 === s2 ? dbl(p1) : [0n, 1n, 0n];
40
+ const h = mod(u2 - u1), i = mod(4n * h * h), j = mod(h * i), r = mod(2n * (s2 - s1)), v = mod(u1 * i);
41
+ const x3 = mod(r * r - j - 2n * v);
42
+ return [x3, mod(r * (v - x3) - 2n * s1 * j), mod((mod((z1 + z2) * (z1 + z2)) - z1z1 - z2z2) * h)];
43
+ }
44
+
45
+ function mul(k, pt = [GX, GY, 1n]) {
46
+ let acc = [0n, 1n, 0n];
47
+ for (let i = 255; i >= 0; i--) {
48
+ acc = dbl(acc);
49
+ if ((k >> BigInt(i)) & 1n) acc = add(acc, pt);
50
+ }
51
+ return acc;
52
+ }
53
+
54
+ function affine([x, y, z]) {
55
+ if (z === 0n) throw new Error("point at infinity");
56
+ const zi = inv(z), zi2 = mod(zi * zi);
57
+ return [mod(x * zi2), mod(y * zi2 * zi)];
58
+ }
59
+
60
+ export function toBig(bytes) {
61
+ let v = 0n;
62
+ for (const b of bytes) v = (v << 8n) | BigInt(b);
63
+ return v;
64
+ }
65
+
66
+ export function toBytes32(v) {
67
+ const out = new Uint8Array(32);
68
+ for (let i = 31; i >= 0; i--) { out[i] = Number(v & 0xffn); v >>= 8n; }
69
+ return out;
70
+ }
71
+
72
+ function hmac(key, ...parts) {
73
+ const k = new Uint8Array(64);
74
+ k.set(key.length > 64 ? sha256(key) : key);
75
+ const ipad = k.map((b) => b ^ 0x36), opad = k.map((b) => b ^ 0x5c);
76
+ return sha256(opad, sha256(ipad, ...parts));
77
+ }
78
+
79
+ function rfc6979(priv, h) {
80
+ const x = toBytes32(priv), m = toBytes32(mod(toBig(h), N));
81
+ let v = new Uint8Array(32).fill(1), k = new Uint8Array(32);
82
+ k = hmac(k, v, Uint8Array.of(0), x, m);
83
+ v = hmac(k, v);
84
+ k = hmac(k, v, Uint8Array.of(1), x, m);
85
+ v = hmac(k, v);
86
+ for (;;) {
87
+ v = hmac(k, v);
88
+ const t = toBig(v);
89
+ if (t > 0n && t < N) return t;
90
+ k = hmac(k, v, Uint8Array.of(0));
91
+ v = hmac(k, v);
92
+ }
93
+ }
94
+
95
+ export function feeKeyFromMaster(master) {
96
+ const h = toBig(sha256(enc.encode("opeq/evm/fee/v1"), master));
97
+ return mod(h, N - 1n) + 1n;
98
+ }
99
+
100
+ export function publicKey(priv) {
101
+ if (priv <= 0n || priv >= N) throw new Error("private key out of range");
102
+ return affine(mul(priv));
103
+ }
104
+
105
+ export function addressOf(priv) {
106
+ const [x, y] = publicKey(priv);
107
+ const a = keccak256(toBytes32(x), toBytes32(y)).slice(12);
108
+ return "0x" + Array.from(a, (b) => b.toString(16).padStart(2, "0")).join("");
109
+ }
110
+
111
+ export function sign(hash32, priv) {
112
+ if (hash32.length !== 32) throw new Error("hash must be 32 bytes");
113
+ const z = mod(toBig(hash32), N);
114
+ const k = rfc6979(priv, hash32);
115
+ const [rx, ry] = affine(mul(k));
116
+ const r = mod(rx, N);
117
+ if (r === 0n) throw new Error("bad nonce");
118
+ let s = mod(inv(k, N) * (z + r * priv), N);
119
+ let recovery = Number(ry & 1n) | (rx >= N ? 2 : 0);
120
+ if (s > N / 2n) { s = N - s; recovery ^= 1; }
121
+ if (s === 0n || recovery > 1) throw new Error("bad signature");
122
+ return { r, s, recovery };
123
+ }
@@ -0,0 +1,120 @@
1
+ const K = new Int32Array([
2
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
3
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
4
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
5
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
6
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
7
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
8
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
9
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
10
+ ]);
11
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
12
+ const Wbuf = new Int32Array(64);
13
+
14
+ function compress(H, block, off) {
15
+ const W = Wbuf;
16
+ for (let i = 0; i < 16; i++) {
17
+ const j = off + 4 * i;
18
+ W[i] = (block[j] << 24) | (block[j + 1] << 16) | (block[j + 2] << 8) | block[j + 3];
19
+ }
20
+ for (let i = 16; i < 64; i++) {
21
+ const a = W[i - 15], b = W[i - 2];
22
+ const s0 = ((a >>> 7) | (a << 25)) ^ ((a >>> 18) | (a << 14)) ^ (a >>> 3);
23
+ const s1 = ((b >>> 17) | (b << 15)) ^ ((b >>> 19) | (b << 13)) ^ (b >>> 10);
24
+ W[i] = (W[i - 16] + s0 + W[i - 7] + s1) | 0;
25
+ }
26
+ let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
27
+ for (let i = 0; i < 64; i++) {
28
+ const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
29
+ const t1 = (h + S1 + ((e & f) ^ (~e & g)) + K[i] + W[i]) | 0;
30
+ const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
31
+ const t2 = (S0 + ((a & b) ^ (a & c) ^ (b & c))) | 0;
32
+ h = g; g = f; f = e; e = (d + t1) | 0; d = c; c = b; b = a; a = (t1 + t2) | 0;
33
+ }
34
+ H[0] = (H[0] + a) | 0; H[1] = (H[1] + b) | 0; H[2] = (H[2] + c) | 0; H[3] = (H[3] + d) | 0;
35
+ H[4] = (H[4] + e) | 0; H[5] = (H[5] + f) | 0; H[6] = (H[6] + g) | 0; H[7] = (H[7] + h) | 0;
36
+ }
37
+
38
+ export class Sha256 {
39
+ constructor() {
40
+ this.H = new Int32Array(IV);
41
+ this.buf = new Uint8Array(64);
42
+ this.len = 0;
43
+ this.total = 0;
44
+ }
45
+
46
+ clone() {
47
+ const c = new Sha256();
48
+ c.H.set(this.H);
49
+ c.buf.set(this.buf);
50
+ c.len = this.len;
51
+ c.total = this.total;
52
+ return c;
53
+ }
54
+
55
+ update(data) {
56
+ let i = 0;
57
+ this.total += data.length;
58
+ if (this.len) {
59
+ const take = Math.min(64 - this.len, data.length);
60
+ this.buf.set(data.subarray(0, take), this.len);
61
+ this.len += take;
62
+ i = take;
63
+ if (this.len < 64) return this;
64
+ compress(this.H, this.buf, 0);
65
+ this.len = 0;
66
+ }
67
+ for (; i + 64 <= data.length; i += 64) compress(this.H, data, i);
68
+ if (i < data.length) {
69
+ this.buf.set(data.subarray(i), 0);
70
+ this.len = data.length - i;
71
+ }
72
+ return this;
73
+ }
74
+
75
+ digest() {
76
+ const c = this.clone();
77
+ const bits = c.total * 8;
78
+ const pad = new Uint8Array(((c.len < 56 ? 56 : 120) - c.len) + 8);
79
+ pad[0] = 0x80;
80
+ const hi = Math.floor(bits / 0x100000000), lo = bits >>> 0;
81
+ const n = pad.length;
82
+ pad[n - 8] = hi >>> 24; pad[n - 7] = hi >>> 16; pad[n - 6] = hi >>> 8; pad[n - 5] = hi;
83
+ pad[n - 4] = lo >>> 24; pad[n - 3] = lo >>> 16; pad[n - 2] = lo >>> 8; pad[n - 1] = lo;
84
+ const H = c.H, buf = c.buf;
85
+ let len = c.len;
86
+ for (let k = 0; k < pad.length; k++) {
87
+ buf[len++] = pad[k];
88
+ if (len === 64) { compress(H, buf, 0); len = 0; }
89
+ }
90
+ const out = new Uint8Array(32);
91
+ for (let k = 0; k < 8; k++) {
92
+ out[4 * k] = H[k] >>> 24; out[4 * k + 1] = H[k] >>> 16; out[4 * k + 2] = H[k] >>> 8; out[4 * k + 3] = H[k];
93
+ }
94
+ return out;
95
+ }
96
+ }
97
+
98
+ export function sha256(...parts) {
99
+ const h = new Sha256();
100
+ for (const p of parts) h.update(p);
101
+ return h.digest();
102
+ }
103
+
104
+ const oneH = new Int32Array(8);
105
+ const oneBlock = new Uint8Array(64);
106
+
107
+ export function sha256Short(msg, out, outLen = 32) {
108
+ const n = msg.length;
109
+ if (n > 55) throw new Error("sha256Short takes at most 55 bytes");
110
+ oneBlock.fill(0);
111
+ oneBlock.set(msg);
112
+ oneBlock[n] = 0x80;
113
+ const bits = n * 8;
114
+ oneBlock[62] = bits >>> 8;
115
+ oneBlock[63] = bits & 0xff;
116
+ oneH.set(IV);
117
+ compress(oneH, oneBlock, 0);
118
+ for (let k = 0; k < outLen; k++) out[k] = oneH[k >> 2] >>> (24 - 8 * (k & 3));
119
+ return out;
120
+ }
@@ -0,0 +1,264 @@
1
+ import { sha256 } from "./sha256.js";
2
+
3
+ const B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4
+ const enc = new TextEncoder();
5
+
6
+ export function b58encode(bytes) {
7
+ let n = 0n;
8
+ for (const b of bytes) n = (n << 8n) | BigInt(b);
9
+ let s = "";
10
+ while (n > 0n) { s = B58[Number(n % 58n)] + s; n /= 58n; }
11
+ for (const b of bytes) { if (b !== 0) break; s = "1" + s; }
12
+ return s;
13
+ }
14
+
15
+ export function b58decode(s) {
16
+ let n = 0n;
17
+ for (const c of s) {
18
+ const i = B58.indexOf(c);
19
+ if (i < 0) throw new Error("invalid base58");
20
+ n = n * 58n + BigInt(i);
21
+ }
22
+ const out = [];
23
+ while (n > 0n) { out.unshift(Number(n & 0xffn)); n >>= 8n; }
24
+ for (const c of s) { if (c !== "1") break; out.unshift(0); }
25
+ return new Uint8Array(out);
26
+ }
27
+
28
+ export function pubkey(s) {
29
+ const b = b58decode(s);
30
+ if (b.length !== 32) throw new Error(`not a 32 byte address: ${s}`);
31
+ return b;
32
+ }
33
+
34
+ export const eq = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
35
+ export const hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
36
+ export const unhex = (h) => new Uint8Array(h.match(/../g).map((x) => parseInt(x, 16)));
37
+
38
+ const P = (1n << 255n) - 19n;
39
+ function modpow(b, e, m) {
40
+ let r = 1n;
41
+ b %= m;
42
+ while (e > 0n) { if (e & 1n) r = (r * b) % m; b = (b * b) % m; e >>= 1n; }
43
+ return r;
44
+ }
45
+ const D = ((-121665n * modpow(121666n, P - 2n, P)) % P + P) % P;
46
+
47
+ export function isOnCurve(bytes) {
48
+ let y = 0n;
49
+ for (let i = 31; i >= 0; i--) y = (y << 8n) | BigInt(bytes[i]);
50
+ y = (y & ((1n << 255n) - 1n)) % P;
51
+ const y2 = (y * y) % P;
52
+ const u = (y2 - 1n + P) % P;
53
+ const v = (D * y2 + 1n) % P;
54
+ if (v === 0n) return false;
55
+ const x2 = (u * modpow(v, P - 2n, P)) % P;
56
+ return x2 === 0n || modpow(x2, (P - 1n) / 2n, P) === 1n;
57
+ }
58
+
59
+ export function findPda(seeds, program) {
60
+ for (let bump = 255; bump >= 0; bump--) {
61
+ const h = sha256(...seeds, new Uint8Array([bump]), program, enc.encode("ProgramDerivedAddress"));
62
+ if (!isOnCurve(h)) return { key: h, bump };
63
+ }
64
+ throw new Error("no program address found");
65
+ }
66
+
67
+ export function compact(n) {
68
+ const out = [];
69
+ for (;;) {
70
+ const b = n & 0x7f;
71
+ n >>= 7;
72
+ if (n) out.push(b | 0x80);
73
+ else { out.push(b); return out; }
74
+ }
75
+ }
76
+
77
+ export function concat(...parts) {
78
+ const n = parts.reduce((s, p) => s + p.length, 0);
79
+ const out = new Uint8Array(n);
80
+ let o = 0;
81
+ for (const p of parts) { out.set(p, o); o += p.length; }
82
+ return out;
83
+ }
84
+
85
+ export function u32(v) { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, v, true); return b; }
86
+ export function u64(v) {
87
+ const n = BigInt(v);
88
+ if (n < 0n || n >= 1n << 64n) throw new Error("value out of u64 range");
89
+ const b = new Uint8Array(8);
90
+ new DataView(b.buffer).setBigUint64(0, n, true);
91
+ return b;
92
+ }
93
+
94
+ export const SYSTEM = new Uint8Array(32);
95
+ export const COMPUTE_BUDGET = pubkey("ComputeBudget111111111111111111111111111111");
96
+
97
+ export function compileV0(payer, ixs, blockhash, tables = []) {
98
+ const key = (k) => b58encode(k);
99
+ const metas = new Map([[key(payer), { k: payer, s: true, w: true }]]);
100
+ const order = [key(payer)];
101
+ const programs = new Set();
102
+ for (const [prog, accs] of ixs) {
103
+ for (const [k, s, w] of accs) {
104
+ const id = key(k);
105
+ if (metas.has(id)) { const m = metas.get(id); m.s ||= s; m.w ||= w; }
106
+ else { metas.set(id, { k, s, w }); order.push(id); }
107
+ }
108
+ programs.add(key(prog));
109
+ if (!metas.has(key(prog))) { metas.set(key(prog), { k: prog, s: false, w: false }); order.push(key(prog)); }
110
+ }
111
+ const where = new Map();
112
+ tables.forEach((t, ti) => t.addresses.forEach((a, i) => { if (!where.has(key(a))) where.set(key(a), [ti, i]); }));
113
+ const isStatic = (id) => metas.get(id).s || programs.has(id) || !where.has(id);
114
+ const rank = (id) => (id === key(payer) ? 0 : 1) * 4 + (metas.get(id).s ? 0 : 2) + (metas.get(id).w ? 0 : 1);
115
+ const statics = order.filter(isStatic).sort((a, b) => rank(a) - rank(b) || order.indexOf(a) - order.indexOf(b));
116
+ const loaded = order.filter((id) => !isStatic(id));
117
+ const lookups = [], loadedW = [], loadedR = [];
118
+ tables.forEach((t, ti) => {
119
+ const wi = loaded.filter((id) => where.get(id)[0] === ti && metas.get(id).w);
120
+ const ri = loaded.filter((id) => where.get(id)[0] === ti && !metas.get(id).w);
121
+ if (wi.length || ri.length) {
122
+ lookups.push([t.key, wi.map((id) => where.get(id)[1]), ri.map((id) => where.get(id)[1])]);
123
+ loadedW.push(...wi);
124
+ loadedR.push(...ri);
125
+ }
126
+ });
127
+ const all = [...statics, ...loadedW, ...loadedR];
128
+ const idx = new Map(all.map((k, i) => [k, i]));
129
+ const nsig = statics.filter((k) => metas.get(k).s).length;
130
+ const roSigned = statics.filter((k) => metas.get(k).s && !metas.get(k).w).length;
131
+ const roUnsigned = statics.filter((k) => !metas.get(k).s && !metas.get(k).w).length;
132
+ const body = [new Uint8Array([0x80, nsig, roSigned, roUnsigned, ...compact(statics.length)]), ...statics.map((k) => metas.get(k).k), blockhash, new Uint8Array(compact(ixs.length))];
133
+ for (const [prog, accs, data] of ixs) {
134
+ body.push(new Uint8Array([idx.get(key(prog)), ...compact(accs.length), ...accs.map((a) => idx.get(key(a[0]))), ...compact(data.length)]));
135
+ body.push(data);
136
+ }
137
+ body.push(new Uint8Array(compact(lookups.length)));
138
+ for (const [t, wi, ri] of lookups) body.push(t, new Uint8Array([...compact(wi.length), ...wi, ...compact(ri.length), ...ri]));
139
+ return { message: concat(...body), signers: statics.slice(0, nsig) };
140
+ }
141
+
142
+ export function unsignedTx(message, nsig) {
143
+ return concat(new Uint8Array(compact(nsig)), new Uint8Array(64 * nsig), message);
144
+ }
145
+
146
+ export class Rpc {
147
+ constructor(url) { this.url = url; this.id = 0; }
148
+
149
+ async call(method, params = []) {
150
+ for (let attempt = 0; ; attempt++) {
151
+ const ctl = new AbortController();
152
+ const timer = setTimeout(() => ctl.abort(), 20000);
153
+ let res;
154
+ try {
155
+ res = await fetch(this.url, {
156
+ method: "POST",
157
+ headers: { "content-type": "application/json" },
158
+ body: JSON.stringify({ jsonrpc: "2.0", id: ++this.id, method, params }),
159
+ signal: ctl.signal,
160
+ });
161
+ } catch (e) {
162
+ if (method !== "sendTransaction" && attempt < 3) { await new Promise((r) => setTimeout(r, 1000 * (attempt + 1))); continue; }
163
+ throw new Error(`network error talking to the RPC (${method})`);
164
+ }
165
+ if ((res.status === 429 || res.status >= 500) && attempt < 4) {
166
+ clearTimeout(timer);
167
+ await new Promise((r) => setTimeout(r, 1500 * (attempt + 1)));
168
+ continue;
169
+ }
170
+ let j;
171
+ try { j = await res.json(); } catch { throw new Error(`the RPC sent an unreadable answer (${method})`); } finally { clearTimeout(timer); }
172
+ if (j.error) {
173
+ const e = new Error(String(j.error.message || "RPC error").slice(0, 200));
174
+ const logs = j.error.data && j.error.data.logs;
175
+ e.data = { logs: Array.isArray(logs) ? logs.filter((l) => typeof l === "string").slice(0, 60) : [] };
176
+ e.fromRpc = true;
177
+ throw e;
178
+ }
179
+ return j.result;
180
+ }
181
+ }
182
+
183
+ async account(k) {
184
+ const v = (await this.call("getAccountInfo", [b58encode(k), { encoding: "base64", commitment: "confirmed" }])).value;
185
+ if (!v) return null;
186
+ return { owner: pubkey(v.owner), lamports: v.lamports, executable: v.executable === true, data: Uint8Array.from(atob(v.data[0]), (c) => c.charCodeAt(0)) };
187
+ }
188
+
189
+ async simulate(tx, addresses, minContextSlot = 0) {
190
+ const b64 = btoa(String.fromCharCode(...tx));
191
+ const r = await this.call("simulateTransaction", [b64, { encoding: "base64", sigVerify: true, commitment: "processed", minContextSlot, accounts: { encoding: "base64", addresses: addresses.map(b58encode) } }]);
192
+ const v = r && r.value;
193
+ if (!r || !r.context || !(r.context.slot >= minContextSlot)) throw new Error("the RPC simulated on an older state than the balance snapshot");
194
+ if (!v || !Array.isArray(v.accounts) || v.accounts.length !== addresses.length) throw new Error("the RPC returned a malformed simulation");
195
+ return { err: v.err, logs: Array.isArray(v.logs) ? v.logs.filter((l) => typeof l === "string").slice(0, 80) : [],
196
+ accounts: v.accounts.map((a) => (a ? { lamports: BigInt(a.lamports), data: Uint8Array.from(atob(a.data[0]), (c) => c.charCodeAt(0)) } : null)) };
197
+ }
198
+
199
+ async snapshot(addresses) {
200
+ const r = await this.call("getMultipleAccounts", [addresses.map(b58encode), { encoding: "base64", commitment: "processed" }]);
201
+ if (!r || !Array.isArray(r.value) || r.value.length !== addresses.length) throw new Error("the RPC returned a malformed account list");
202
+ const out = r.value.map((a) => (a ? { lamports: BigInt(a.lamports), data: Uint8Array.from(atob(a.data[0]), (c) => c.charCodeAt(0)) } : null));
203
+ out.slot = r.context && r.context.slot ? r.context.slot : 0;
204
+ return out;
205
+ }
206
+
207
+ async executables(keys) {
208
+ const out = new Map();
209
+ for (let i = 0; i < keys.length; i += 100) {
210
+ const part = keys.slice(i, i + 100);
211
+ const r = await this.call("getMultipleAccounts", [part.map(b58encode), { encoding: "base64", dataSlice: { offset: 0, length: 0 }, commitment: "confirmed" }]);
212
+ if (!r || !Array.isArray(r.value) || r.value.length !== part.length) throw new Error("the RPC returned a malformed account list");
213
+ r.value.forEach((v, j) => out.set(b58encode(part[j]), !!(v && v.executable === true)));
214
+ }
215
+ return out;
216
+ }
217
+
218
+ async balance(k) { return (await this.call("getBalance", [b58encode(k), { commitment: "confirmed" }])).value; }
219
+ async slot() { return this.call("getSlot", [{ commitment: "confirmed" }]); }
220
+ async blockhash() { return b58decode((await this.call("getLatestBlockhash", [{ commitment: "confirmed" }])).value.blockhash); }
221
+ async rent(size) { return this.call("getMinimumBalanceForRentExemption", [size]); }
222
+
223
+ async lookupTable(addr) {
224
+ const acc = await this.account(addr);
225
+ if (!acc) throw new Error("lookup table not found");
226
+ if (b58encode(acc.owner) !== "AddressLookupTab1e1111111111111111111111111") throw new Error("account is not an address lookup table");
227
+ const addresses = [];
228
+ for (let o = 56; o + 32 <= acc.data.length; o += 32) addresses.push(acc.data.slice(o, o + 32));
229
+ return { key: addr, addresses };
230
+ }
231
+
232
+ async tokenAccounts(owner) {
233
+ const out = [];
234
+ for (const prog of ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]) {
235
+ const r = await this.call("getTokenAccountsByOwner", [b58encode(owner), { programId: prog }, { encoding: "jsonParsed", commitment: "confirmed" }]);
236
+ for (const a of Array.isArray(r && r.value) ? r.value.slice(0, 200) : []) {
237
+ try {
238
+ const info = a.account.data.parsed.info;
239
+ const amt = info.tokenAmount.amount, dec = info.tokenAmount.decimals;
240
+ if (typeof amt !== "string" || !/^\d{1,20}$/.test(amt) || !Number.isInteger(dec) || dec < 0 || dec > 18) continue;
241
+ if (BigInt(amt) >= 1n << 64n) continue;
242
+ pubkey(a.pubkey); pubkey(info.mint);
243
+ out.push({ account: a.pubkey, program: prog, mint: info.mint, amount: BigInt(amt), decimals: dec });
244
+ } catch {}
245
+ }
246
+ }
247
+ return out;
248
+ }
249
+
250
+ async tokenAmount(acc) {
251
+ const amt = (await this.call("getTokenAccountBalance", [b58encode(acc), { commitment: "confirmed" }])).value.amount;
252
+ if (typeof amt !== "string" || !/^\d{1,20}$/.test(amt)) throw new Error("the RPC returned an invalid token amount");
253
+ return BigInt(amt);
254
+ }
255
+
256
+ async sendRaw(tx) {
257
+ const b64 = btoa(String.fromCharCode(...tx));
258
+ return this.call("sendTransaction", [b64, { encoding: "base64", preflightCommitment: "confirmed" }]);
259
+ }
260
+
261
+ async status(sig) {
262
+ return (await this.call("getSignatureStatuses", [[sig], { searchTransactionHistory: true }])).value[0];
263
+ }
264
+ }