@usearete/react 0.1.4 → 0.2.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/README.md +119 -65
- package/dist/index.d.ts +93 -32
- package/dist/index.esm.js +2210 -280
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2214 -280
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1481,7 +1481,7 @@ const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */
|
|
|
1481
1481
|
|
|
1482
1482
|
const OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
|
|
1483
1483
|
|
|
1484
|
-
const err = (strm, errorCode) => {
|
|
1484
|
+
const err$1 = (strm, errorCode) => {
|
|
1485
1485
|
strm.msg = messages[errorCode];
|
|
1486
1486
|
return errorCode;
|
|
1487
1487
|
};
|
|
@@ -2798,7 +2798,7 @@ const deflateStateCheck = (strm) => {
|
|
|
2798
2798
|
const deflateResetKeep = (strm) => {
|
|
2799
2799
|
|
|
2800
2800
|
if (deflateStateCheck(strm)) {
|
|
2801
|
-
return err(strm, Z_STREAM_ERROR$2);
|
|
2801
|
+
return err$1(strm, Z_STREAM_ERROR$2);
|
|
2802
2802
|
}
|
|
2803
2803
|
|
|
2804
2804
|
strm.total_in = strm.total_out = 0;
|
|
@@ -2872,7 +2872,7 @@ const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {
|
|
|
2872
2872
|
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 ||
|
|
2873
2873
|
windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
|
|
2874
2874
|
strategy < 0 || strategy > Z_FIXED || (windowBits === 8 && wrap !== 1)) {
|
|
2875
|
-
return err(strm, Z_STREAM_ERROR$2);
|
|
2875
|
+
return err$1(strm, Z_STREAM_ERROR$2);
|
|
2876
2876
|
}
|
|
2877
2877
|
|
|
2878
2878
|
|
|
@@ -2977,7 +2977,7 @@ const deflateInit = (strm, level) => {
|
|
|
2977
2977
|
const deflate$2 = (strm, flush) => {
|
|
2978
2978
|
|
|
2979
2979
|
if (deflateStateCheck(strm) || flush > Z_BLOCK$1 || flush < 0) {
|
|
2980
|
-
return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;
|
|
2980
|
+
return strm ? err$1(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;
|
|
2981
2981
|
}
|
|
2982
2982
|
|
|
2983
2983
|
const s = strm.state;
|
|
@@ -2985,7 +2985,7 @@ const deflate$2 = (strm, flush) => {
|
|
|
2985
2985
|
if (!strm.output ||
|
|
2986
2986
|
(strm.avail_in !== 0 && !strm.input) ||
|
|
2987
2987
|
(s.status === FINISH_STATE && flush !== Z_FINISH$3)) {
|
|
2988
|
-
return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);
|
|
2988
|
+
return err$1(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);
|
|
2989
2989
|
}
|
|
2990
2990
|
|
|
2991
2991
|
const old_flush = s.last_flush;
|
|
@@ -3011,12 +3011,12 @@ const deflate$2 = (strm, flush) => {
|
|
|
3011
3011
|
*/
|
|
3012
3012
|
} else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
|
|
3013
3013
|
flush !== Z_FINISH$3) {
|
|
3014
|
-
return err(strm, Z_BUF_ERROR$1);
|
|
3014
|
+
return err$1(strm, Z_BUF_ERROR$1);
|
|
3015
3015
|
}
|
|
3016
3016
|
|
|
3017
3017
|
/* User must not provide more input after the first FINISH: */
|
|
3018
3018
|
if (s.status === FINISH_STATE && strm.avail_in !== 0) {
|
|
3019
|
-
return err(strm, Z_BUF_ERROR$1);
|
|
3019
|
+
return err$1(strm, Z_BUF_ERROR$1);
|
|
3020
3020
|
}
|
|
3021
3021
|
|
|
3022
3022
|
/* Write the header */
|
|
@@ -3338,7 +3338,7 @@ const deflateEnd = (strm) => {
|
|
|
3338
3338
|
|
|
3339
3339
|
strm.state = null;
|
|
3340
3340
|
|
|
3341
|
-
return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3;
|
|
3341
|
+
return status === BUSY_STATE ? err$1(strm, Z_DATA_ERROR$2) : Z_OK$3;
|
|
3342
3342
|
};
|
|
3343
3343
|
|
|
3344
3344
|
|
|
@@ -6764,6 +6764,453 @@ var inflate_1$1 = {
|
|
|
6764
6764
|
const { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1;
|
|
6765
6765
|
var inflate_1 = inflate;
|
|
6766
6766
|
|
|
6767
|
+
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
|
|
6768
|
+
/**
|
|
6769
|
+
* 4KB JS implementation of ed25519 EdDSA signatures.
|
|
6770
|
+
* Compliant with RFC8032, FIPS 186-5 & ZIP215.
|
|
6771
|
+
* @module
|
|
6772
|
+
* @example
|
|
6773
|
+
* ```js
|
|
6774
|
+
import * as ed from '@noble/ed25519';
|
|
6775
|
+
(async () => {
|
|
6776
|
+
const privKey = ed.utils.randomPrivateKey();
|
|
6777
|
+
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
|
|
6778
|
+
const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods are also present
|
|
6779
|
+
const signature = await ed.signAsync(message, privKey);
|
|
6780
|
+
const isValid = await ed.verifyAsync(signature, message, pubKey);
|
|
6781
|
+
})();
|
|
6782
|
+
```
|
|
6783
|
+
*/
|
|
6784
|
+
/**
|
|
6785
|
+
* Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².
|
|
6786
|
+
* * P = `2n**255n - 19n` // field over which calculations are done
|
|
6787
|
+
* * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points
|
|
6788
|
+
* * h = 8 // cofactor
|
|
6789
|
+
* * a = `Fp.create(BigInt(-1))` // equation param
|
|
6790
|
+
* * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param
|
|
6791
|
+
* * Gx, Gy are coordinates of Generator / base point
|
|
6792
|
+
*/
|
|
6793
|
+
const ed25519_CURVE = {
|
|
6794
|
+
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
|
|
6795
|
+
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
|
|
6796
|
+
h: 8n,
|
|
6797
|
+
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
|
|
6798
|
+
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
|
|
6799
|
+
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
|
|
6800
|
+
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
|
|
6801
|
+
};
|
|
6802
|
+
const { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
|
|
6803
|
+
const h = 8n; // cofactor
|
|
6804
|
+
const L = 32; // field / group byte length
|
|
6805
|
+
const L2 = 64;
|
|
6806
|
+
// Helpers and Precomputes sections are reused between libraries
|
|
6807
|
+
// ## Helpers
|
|
6808
|
+
// ----------
|
|
6809
|
+
// error helper, messes-up stack trace
|
|
6810
|
+
const err = (m = '') => {
|
|
6811
|
+
throw new Error(m);
|
|
6812
|
+
};
|
|
6813
|
+
const isBig = (n) => typeof n === 'bigint'; // is big integer
|
|
6814
|
+
const isStr = (s) => typeof s === 'string'; // is string
|
|
6815
|
+
const isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
6816
|
+
/** assert is Uint8Array (of specific length) */
|
|
6817
|
+
const abytes = (a, l) => !isBytes(a) || (typeof l === 'number' && l > 0 && a.length !== l)
|
|
6818
|
+
? err('Uint8Array expected')
|
|
6819
|
+
: a;
|
|
6820
|
+
/** create Uint8Array */
|
|
6821
|
+
const u8n = (len) => new Uint8Array(len);
|
|
6822
|
+
const u8fr = (buf) => Uint8Array.from(buf);
|
|
6823
|
+
const padh = (n, pad) => n.toString(16).padStart(pad, '0');
|
|
6824
|
+
const bytesToHex = (b) => Array.from(abytes(b))
|
|
6825
|
+
.map((e) => padh(e, 2))
|
|
6826
|
+
.join('');
|
|
6827
|
+
const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
|
|
6828
|
+
const _ch = (ch) => {
|
|
6829
|
+
if (ch >= C._0 && ch <= C._9)
|
|
6830
|
+
return ch - C._0; // '2' => 50-48
|
|
6831
|
+
if (ch >= C.A && ch <= C.F)
|
|
6832
|
+
return ch - (C.A - 10); // 'B' => 66-(65-10)
|
|
6833
|
+
if (ch >= C.a && ch <= C.f)
|
|
6834
|
+
return ch - (C.a - 10); // 'b' => 98-(97-10)
|
|
6835
|
+
return;
|
|
6836
|
+
};
|
|
6837
|
+
const hexToBytes = (hex) => {
|
|
6838
|
+
const e = 'hex invalid';
|
|
6839
|
+
if (!isStr(hex))
|
|
6840
|
+
return err(e);
|
|
6841
|
+
const hl = hex.length;
|
|
6842
|
+
const al = hl / 2;
|
|
6843
|
+
if (hl % 2)
|
|
6844
|
+
return err(e);
|
|
6845
|
+
const array = u8n(al);
|
|
6846
|
+
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
6847
|
+
// treat each char as ASCII
|
|
6848
|
+
const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
|
|
6849
|
+
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
|
|
6850
|
+
if (n1 === undefined || n2 === undefined)
|
|
6851
|
+
return err(e);
|
|
6852
|
+
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
|
|
6853
|
+
}
|
|
6854
|
+
return array;
|
|
6855
|
+
};
|
|
6856
|
+
/** normalize hex or ui8a to ui8a */
|
|
6857
|
+
const toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
|
|
6858
|
+
const big = BigInt;
|
|
6859
|
+
const arange = (n, min, max, msg = 'bad number: out of range') => isBig(n) && min <= n && n < max ? n : err(msg);
|
|
6860
|
+
/** modular division */
|
|
6861
|
+
const M = (a, b = P) => {
|
|
6862
|
+
const r = a % b;
|
|
6863
|
+
return r >= 0n ? r : b + r;
|
|
6864
|
+
};
|
|
6865
|
+
/** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
|
|
6866
|
+
// prettier-ignore
|
|
6867
|
+
const invert = (num, md) => {
|
|
6868
|
+
if (num === 0n || md <= 0n)
|
|
6869
|
+
err('no inverse n=' + num + ' mod=' + md);
|
|
6870
|
+
let a = M(num, md), b = md, x = 0n, u = 1n;
|
|
6871
|
+
while (a !== 0n) {
|
|
6872
|
+
const q = b / a, r = b % a;
|
|
6873
|
+
const m = x - u * q;
|
|
6874
|
+
b = a, a = r, x = u, u = m;
|
|
6875
|
+
}
|
|
6876
|
+
return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
|
|
6877
|
+
};
|
|
6878
|
+
const apoint = (p) => (p instanceof Point ? p : err('Point expected'));
|
|
6879
|
+
// ## End of Helpers
|
|
6880
|
+
// -----------------
|
|
6881
|
+
const B256 = 2n ** 256n;
|
|
6882
|
+
/** Point in XYZT extended coordinates. */
|
|
6883
|
+
class Point {
|
|
6884
|
+
static BASE;
|
|
6885
|
+
static ZERO;
|
|
6886
|
+
ex;
|
|
6887
|
+
ey;
|
|
6888
|
+
ez;
|
|
6889
|
+
et;
|
|
6890
|
+
constructor(ex, ey, ez, et) {
|
|
6891
|
+
const max = B256;
|
|
6892
|
+
this.ex = arange(ex, 0n, max);
|
|
6893
|
+
this.ey = arange(ey, 0n, max);
|
|
6894
|
+
this.ez = arange(ez, 1n, max);
|
|
6895
|
+
this.et = arange(et, 0n, max);
|
|
6896
|
+
Object.freeze(this);
|
|
6897
|
+
}
|
|
6898
|
+
static fromAffine(p) {
|
|
6899
|
+
return new Point(p.x, p.y, 1n, M(p.x * p.y));
|
|
6900
|
+
}
|
|
6901
|
+
/** RFC8032 5.1.3: Uint8Array to Point. */
|
|
6902
|
+
static fromBytes(hex, zip215 = false) {
|
|
6903
|
+
const d = _d;
|
|
6904
|
+
// Copy array to not mess it up.
|
|
6905
|
+
const normed = u8fr(abytes(hex, L));
|
|
6906
|
+
// adjust first LE byte = last BE byte
|
|
6907
|
+
const lastByte = hex[31];
|
|
6908
|
+
normed[31] = lastByte & ~0x80;
|
|
6909
|
+
const y = bytesToNumLE(normed);
|
|
6910
|
+
// zip215=true: 0 <= y < 2^256
|
|
6911
|
+
// zip215=false, RFC8032: 0 <= y < 2^255-19
|
|
6912
|
+
const max = zip215 ? B256 : P;
|
|
6913
|
+
arange(y, 0n, max);
|
|
6914
|
+
const y2 = M(y * y); // y²
|
|
6915
|
+
const u = M(y2 - 1n); // u=y²-1
|
|
6916
|
+
const v = M(d * y2 + 1n); // v=dy²+1
|
|
6917
|
+
let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root
|
|
6918
|
+
if (!isValid)
|
|
6919
|
+
err('bad point: y not sqrt'); // not square root: bad point
|
|
6920
|
+
const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
|
|
6921
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
|
6922
|
+
if (!zip215 && x === 0n && isLastByteOdd)
|
|
6923
|
+
err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1
|
|
6924
|
+
if (isLastByteOdd !== isXOdd)
|
|
6925
|
+
x = M(-x);
|
|
6926
|
+
return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy
|
|
6927
|
+
}
|
|
6928
|
+
/** Checks if the point is valid and on-curve. */
|
|
6929
|
+
assertValidity() {
|
|
6930
|
+
const a = _a;
|
|
6931
|
+
const d = _d;
|
|
6932
|
+
const p = this;
|
|
6933
|
+
if (p.is0())
|
|
6934
|
+
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
6935
|
+
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
6936
|
+
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
6937
|
+
const { ex: X, ey: Y, ez: Z, et: T } = p;
|
|
6938
|
+
const X2 = M(X * X); // X²
|
|
6939
|
+
const Y2 = M(Y * Y); // Y²
|
|
6940
|
+
const Z2 = M(Z * Z); // Z²
|
|
6941
|
+
const Z4 = M(Z2 * Z2); // Z⁴
|
|
6942
|
+
const aX2 = M(X2 * a); // aX²
|
|
6943
|
+
const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²
|
|
6944
|
+
const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²
|
|
6945
|
+
if (left !== right)
|
|
6946
|
+
throw new Error('bad point: equation left != right (1)');
|
|
6947
|
+
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
6948
|
+
const XY = M(X * Y);
|
|
6949
|
+
const ZT = M(Z * T);
|
|
6950
|
+
if (XY !== ZT)
|
|
6951
|
+
throw new Error('bad point: equation left != right (2)');
|
|
6952
|
+
return this;
|
|
6953
|
+
}
|
|
6954
|
+
/** Equality check: compare points P&Q. */
|
|
6955
|
+
equals(other) {
|
|
6956
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
6957
|
+
const { ex: X2, ey: Y2, ez: Z2 } = apoint(other); // checks class equality
|
|
6958
|
+
const X1Z2 = M(X1 * Z2);
|
|
6959
|
+
const X2Z1 = M(X2 * Z1);
|
|
6960
|
+
const Y1Z2 = M(Y1 * Z2);
|
|
6961
|
+
const Y2Z1 = M(Y2 * Z1);
|
|
6962
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
6963
|
+
}
|
|
6964
|
+
is0() {
|
|
6965
|
+
return this.equals(I);
|
|
6966
|
+
}
|
|
6967
|
+
/** Flip point over y coordinate. */
|
|
6968
|
+
negate() {
|
|
6969
|
+
return new Point(M(-this.ex), this.ey, this.ez, M(-this.et));
|
|
6970
|
+
}
|
|
6971
|
+
/** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
|
|
6972
|
+
double() {
|
|
6973
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
6974
|
+
const a = _a;
|
|
6975
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
6976
|
+
const A = M(X1 * X1);
|
|
6977
|
+
const B = M(Y1 * Y1);
|
|
6978
|
+
const C = M(2n * M(Z1 * Z1));
|
|
6979
|
+
const D = M(a * A);
|
|
6980
|
+
const x1y1 = X1 + Y1;
|
|
6981
|
+
const E = M(M(x1y1 * x1y1) - A - B);
|
|
6982
|
+
const G = D + B;
|
|
6983
|
+
const F = G - C;
|
|
6984
|
+
const H = D - B;
|
|
6985
|
+
const X3 = M(E * F);
|
|
6986
|
+
const Y3 = M(G * H);
|
|
6987
|
+
const T3 = M(E * H);
|
|
6988
|
+
const Z3 = M(F * G);
|
|
6989
|
+
return new Point(X3, Y3, Z3, T3);
|
|
6990
|
+
}
|
|
6991
|
+
/** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
|
|
6992
|
+
add(other) {
|
|
6993
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
|
6994
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other); // doesn't check if other on-curve
|
|
6995
|
+
const a = _a;
|
|
6996
|
+
const d = _d;
|
|
6997
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
|
6998
|
+
const A = M(X1 * X2);
|
|
6999
|
+
const B = M(Y1 * Y2);
|
|
7000
|
+
const C = M(T1 * d * T2);
|
|
7001
|
+
const D = M(Z1 * Z2);
|
|
7002
|
+
const E = M((X1 + Y1) * (X2 + Y2) - A - B);
|
|
7003
|
+
const F = M(D - C);
|
|
7004
|
+
const G = M(D + C);
|
|
7005
|
+
const H = M(B - a * A);
|
|
7006
|
+
const X3 = M(E * F);
|
|
7007
|
+
const Y3 = M(G * H);
|
|
7008
|
+
const T3 = M(E * H);
|
|
7009
|
+
const Z3 = M(F * G);
|
|
7010
|
+
return new Point(X3, Y3, Z3, T3);
|
|
7011
|
+
}
|
|
7012
|
+
/**
|
|
7013
|
+
* Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
|
|
7014
|
+
* Uses {@link wNAF} for base point.
|
|
7015
|
+
* Uses fake point to mitigate side-channel leakage.
|
|
7016
|
+
* @param n scalar by which point is multiplied
|
|
7017
|
+
* @param safe safe mode guards against timing attacks; unsafe mode is faster
|
|
7018
|
+
*/
|
|
7019
|
+
multiply(n, safe = true) {
|
|
7020
|
+
if (!safe && (n === 0n || this.is0()))
|
|
7021
|
+
return I;
|
|
7022
|
+
arange(n, 1n, N);
|
|
7023
|
+
if (n === 1n)
|
|
7024
|
+
return this;
|
|
7025
|
+
if (this.equals(G))
|
|
7026
|
+
return wNAF(n).p;
|
|
7027
|
+
// init result point & fake point
|
|
7028
|
+
let p = I;
|
|
7029
|
+
let f = G;
|
|
7030
|
+
for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
|
|
7031
|
+
// if bit is present, add to point
|
|
7032
|
+
// if not present, add to fake, for timing safety
|
|
7033
|
+
if (n & 1n)
|
|
7034
|
+
p = p.add(d);
|
|
7035
|
+
else if (safe)
|
|
7036
|
+
f = f.add(d);
|
|
7037
|
+
}
|
|
7038
|
+
return p;
|
|
7039
|
+
}
|
|
7040
|
+
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
|
|
7041
|
+
toAffine() {
|
|
7042
|
+
const { ex: x, ey: y, ez: z } = this;
|
|
7043
|
+
// fast-paths for ZERO point OR Z=1
|
|
7044
|
+
if (this.equals(I))
|
|
7045
|
+
return { x: 0n, y: 1n };
|
|
7046
|
+
const iz = invert(z, P);
|
|
7047
|
+
// (Z * Z^-1) must be 1, otherwise bad math
|
|
7048
|
+
if (M(z * iz) !== 1n)
|
|
7049
|
+
err('invalid inverse');
|
|
7050
|
+
// x = X*Z^-1; y = Y*Z^-1
|
|
7051
|
+
return { x: M(x * iz), y: M(y * iz) };
|
|
7052
|
+
}
|
|
7053
|
+
toBytes() {
|
|
7054
|
+
const { x, y } = this.assertValidity().toAffine();
|
|
7055
|
+
const b = numTo32bLE(y);
|
|
7056
|
+
// store sign in first LE byte
|
|
7057
|
+
b[31] |= x & 1n ? 0x80 : 0;
|
|
7058
|
+
return b;
|
|
7059
|
+
}
|
|
7060
|
+
toHex() {
|
|
7061
|
+
return bytesToHex(this.toBytes());
|
|
7062
|
+
} // encode to hex string
|
|
7063
|
+
clearCofactor() {
|
|
7064
|
+
return this.multiply(big(h), false);
|
|
7065
|
+
}
|
|
7066
|
+
isSmallOrder() {
|
|
7067
|
+
return this.clearCofactor().is0();
|
|
7068
|
+
}
|
|
7069
|
+
isTorsionFree() {
|
|
7070
|
+
// multiply by big number CURVE.n
|
|
7071
|
+
let p = this.multiply(N / 2n, false).double(); // ensures the point is not "bad".
|
|
7072
|
+
if (N % 2n)
|
|
7073
|
+
p = p.add(this); // P^(N+1) // P*N == (P*(N/2))*2+P
|
|
7074
|
+
return p.is0();
|
|
7075
|
+
}
|
|
7076
|
+
static fromHex(hex, zip215) {
|
|
7077
|
+
return Point.fromBytes(toU8(hex), zip215);
|
|
7078
|
+
}
|
|
7079
|
+
get x() {
|
|
7080
|
+
return this.toAffine().x;
|
|
7081
|
+
}
|
|
7082
|
+
get y() {
|
|
7083
|
+
return this.toAffine().y;
|
|
7084
|
+
}
|
|
7085
|
+
toRawBytes() {
|
|
7086
|
+
return this.toBytes();
|
|
7087
|
+
}
|
|
7088
|
+
}
|
|
7089
|
+
/** Generator / base point */
|
|
7090
|
+
const G = new Point(Gx, Gy, 1n, M(Gx * Gy));
|
|
7091
|
+
/** Identity / zero point */
|
|
7092
|
+
const I = new Point(0n, 1n, 1n, 0n);
|
|
7093
|
+
// Static aliases
|
|
7094
|
+
Point.BASE = G;
|
|
7095
|
+
Point.ZERO = I;
|
|
7096
|
+
const numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
|
|
7097
|
+
const bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));
|
|
7098
|
+
const pow2 = (x, power) => {
|
|
7099
|
+
// pow2(x, 4) == x^(2^4)
|
|
7100
|
+
let r = x;
|
|
7101
|
+
while (power-- > 0n) {
|
|
7102
|
+
r *= r;
|
|
7103
|
+
r %= P;
|
|
7104
|
+
}
|
|
7105
|
+
return r;
|
|
7106
|
+
};
|
|
7107
|
+
// prettier-ignore
|
|
7108
|
+
const pow_2_252_3 = (x) => {
|
|
7109
|
+
const x2 = (x * x) % P; // x^2, bits 1
|
|
7110
|
+
const b2 = (x2 * x) % P; // x^3, bits 11
|
|
7111
|
+
const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111
|
|
7112
|
+
const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111
|
|
7113
|
+
const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)
|
|
7114
|
+
const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)
|
|
7115
|
+
const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)
|
|
7116
|
+
const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)
|
|
7117
|
+
const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)
|
|
7118
|
+
const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)
|
|
7119
|
+
const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)
|
|
7120
|
+
const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.
|
|
7121
|
+
return { pow_p_5_8, b2 };
|
|
7122
|
+
};
|
|
7123
|
+
const RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1
|
|
7124
|
+
// for sqrt comp
|
|
7125
|
+
// prettier-ignore
|
|
7126
|
+
const uvRatio = (u, v) => {
|
|
7127
|
+
const v3 = M(v * v * v); // v³
|
|
7128
|
+
const v7 = M(v3 * v3 * v); // v⁷
|
|
7129
|
+
const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8
|
|
7130
|
+
let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8
|
|
7131
|
+
const vx2 = M(v * x * x); // vx²
|
|
7132
|
+
const root1 = x; // First root candidate
|
|
7133
|
+
const root2 = M(x * RM1); // Second root candidate; RM1 is √-1
|
|
7134
|
+
const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
|
|
7135
|
+
const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)
|
|
7136
|
+
const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1
|
|
7137
|
+
if (useRoot1)
|
|
7138
|
+
x = root1;
|
|
7139
|
+
if (useRoot2 || noRoot)
|
|
7140
|
+
x = root2; // We return root2 anyway, for const-time
|
|
7141
|
+
if ((M(x) & 1n) === 1n)
|
|
7142
|
+
x = M(-x); // edIsNegative
|
|
7143
|
+
return { isValid: useRoot1 || useRoot2, value: x };
|
|
7144
|
+
};
|
|
7145
|
+
// ## Precomputes
|
|
7146
|
+
// --------------
|
|
7147
|
+
const W = 8; // W is window size
|
|
7148
|
+
const scalarBits = 256;
|
|
7149
|
+
const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8
|
|
7150
|
+
const pwindowSize = 2 ** (W - 1); // 128 for W=8
|
|
7151
|
+
const precompute = () => {
|
|
7152
|
+
const points = [];
|
|
7153
|
+
let p = G;
|
|
7154
|
+
let b = p;
|
|
7155
|
+
for (let w = 0; w < pwindows; w++) {
|
|
7156
|
+
b = p;
|
|
7157
|
+
points.push(b);
|
|
7158
|
+
for (let i = 1; i < pwindowSize; i++) {
|
|
7159
|
+
b = b.add(p);
|
|
7160
|
+
points.push(b);
|
|
7161
|
+
} // i=1, bc we skip 0
|
|
7162
|
+
p = b.double();
|
|
7163
|
+
}
|
|
7164
|
+
return points;
|
|
7165
|
+
};
|
|
7166
|
+
let Gpows = undefined; // precomputes for base point G
|
|
7167
|
+
// const-time negate
|
|
7168
|
+
const ctneg = (cnd, p) => {
|
|
7169
|
+
const n = p.negate();
|
|
7170
|
+
return cnd ? n : p;
|
|
7171
|
+
};
|
|
7172
|
+
/**
|
|
7173
|
+
* Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by
|
|
7174
|
+
* caching multiples of G (base point). Cache is stored in 32MB of RAM.
|
|
7175
|
+
* Any time `G.multiply` is done, precomputes are used.
|
|
7176
|
+
* Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.
|
|
7177
|
+
*
|
|
7178
|
+
* w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,
|
|
7179
|
+
* but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.
|
|
7180
|
+
*
|
|
7181
|
+
* !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
|
|
7182
|
+
*/
|
|
7183
|
+
const wNAF = (n) => {
|
|
7184
|
+
const comp = Gpows || (Gpows = precompute());
|
|
7185
|
+
let p = I;
|
|
7186
|
+
let f = G; // f must be G, or could become I in the end
|
|
7187
|
+
const pow_2_w = 2 ** W; // 256 for W=8
|
|
7188
|
+
const maxNum = pow_2_w; // 256 for W=8
|
|
7189
|
+
const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111
|
|
7190
|
+
const shiftBy = big(W); // 8 for W=8
|
|
7191
|
+
for (let w = 0; w < pwindows; w++) {
|
|
7192
|
+
let wbits = Number(n & mask); // extract W bits.
|
|
7193
|
+
n >>= shiftBy; // shift number by W bits.
|
|
7194
|
+
if (wbits > pwindowSize) {
|
|
7195
|
+
wbits -= maxNum;
|
|
7196
|
+
n += 1n;
|
|
7197
|
+
} // split if bits > max: +224 => 256-32
|
|
7198
|
+
const off = w * pwindowSize;
|
|
7199
|
+
const offF = off; // offsets, evaluate both
|
|
7200
|
+
const offP = off + Math.abs(wbits) - 1;
|
|
7201
|
+
const isEven = w % 2 !== 0; // conditions, evaluate both
|
|
7202
|
+
const isNeg = wbits < 0;
|
|
7203
|
+
if (wbits === 0) {
|
|
7204
|
+
// off == I: can't add it. Adding random offF instead.
|
|
7205
|
+
f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point
|
|
7206
|
+
}
|
|
7207
|
+
else {
|
|
7208
|
+
p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
return { p, f }; // return both real and fake points for JIT
|
|
7212
|
+
};
|
|
7213
|
+
|
|
6767
7214
|
const DEFAULT_MAX_ENTRIES_PER_VIEW = 10000;
|
|
6768
7215
|
const DEFAULT_CONFIG = {
|
|
6769
7216
|
reconnectIntervals: [1000, 2000, 4000, 8000, 16000],
|
|
@@ -6946,6 +7393,11 @@ function isHostedAreteWebsocketUrl(websocketUrl) {
|
|
|
6946
7393
|
return false;
|
|
6947
7394
|
}
|
|
6948
7395
|
}
|
|
7396
|
+
/**
|
|
7397
|
+
* Historical sentinel accepted for back-compat: connecting with this URL is
|
|
7398
|
+
* treated the same as passing no WebSocket URL at all (HTTP-only mode).
|
|
7399
|
+
*/
|
|
7400
|
+
const DISABLED_WEBSOCKET_URL = 'ws://127.0.0.1/__arete_disabled__';
|
|
6949
7401
|
class ConnectionManager {
|
|
6950
7402
|
constructor(config) {
|
|
6951
7403
|
this.ws = null;
|
|
@@ -6961,11 +7413,11 @@ class ConnectionManager {
|
|
|
6961
7413
|
this.stateHandlers = new Set();
|
|
6962
7414
|
this.socketIssueHandlers = new Set();
|
|
6963
7415
|
this.reconnectForTokenRefresh = false;
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
this.websocketUrl =
|
|
6968
|
-
this.hostedAreteUrl = isHostedAreteWebsocketUrl(
|
|
7416
|
+
const websocketUrl = config.websocketUrl && config.websocketUrl !== DISABLED_WEBSOCKET_URL
|
|
7417
|
+
? config.websocketUrl
|
|
7418
|
+
: null;
|
|
7419
|
+
this.websocketUrl = websocketUrl;
|
|
7420
|
+
this.hostedAreteUrl = websocketUrl !== null && isHostedAreteWebsocketUrl(websocketUrl);
|
|
6969
7421
|
this.reconnectIntervals = config.reconnectIntervals ?? DEFAULT_CONFIG.reconnectIntervals;
|
|
6970
7422
|
this.maxReconnectAttempts =
|
|
6971
7423
|
config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts;
|
|
@@ -7057,7 +7509,7 @@ class ConnectionManager {
|
|
|
7057
7509
|
}
|
|
7058
7510
|
createTokenEndpointRequestBody() {
|
|
7059
7511
|
return {
|
|
7060
|
-
websocket_url: this.websocketUrl,
|
|
7512
|
+
websocket_url: this.websocketUrl ?? '',
|
|
7061
7513
|
};
|
|
7062
7514
|
}
|
|
7063
7515
|
async fetchTokenFromEndpoint(tokenEndpoint) {
|
|
@@ -7211,14 +7663,21 @@ class ConnectionManager {
|
|
|
7211
7663
|
this.ws.close(1000, 'token refresh');
|
|
7212
7664
|
}
|
|
7213
7665
|
buildAuthUrl(token) {
|
|
7666
|
+
const websocketUrl = this.requireWebsocketUrl();
|
|
7214
7667
|
if (this.authConfig?.tokenTransport === 'bearer') {
|
|
7215
|
-
return
|
|
7668
|
+
return websocketUrl;
|
|
7216
7669
|
}
|
|
7217
7670
|
if (!token) {
|
|
7218
|
-
return
|
|
7671
|
+
return websocketUrl;
|
|
7672
|
+
}
|
|
7673
|
+
const separator = websocketUrl.includes('?') ? '&' : '?';
|
|
7674
|
+
return `${websocketUrl}${separator}${DEFAULT_QUERY_PARAMETER}=${encodeURIComponent(token)}`;
|
|
7675
|
+
}
|
|
7676
|
+
requireWebsocketUrl() {
|
|
7677
|
+
if (this.websocketUrl === null) {
|
|
7678
|
+
throw new AreteError('WebSocket transport is disabled (client was connected with transport: "http"); views and subscriptions are unavailable', 'WEBSOCKET_DISABLED');
|
|
7219
7679
|
}
|
|
7220
|
-
|
|
7221
|
-
return `${this.websocketUrl}${separator}${DEFAULT_QUERY_PARAMETER}=${encodeURIComponent(token)}`;
|
|
7680
|
+
return this.websocketUrl;
|
|
7222
7681
|
}
|
|
7223
7682
|
createWebSocket(url, token) {
|
|
7224
7683
|
if (this.authConfig?.tokenTransport === 'bearer') {
|
|
@@ -7238,6 +7697,12 @@ class ConnectionManager {
|
|
|
7238
7697
|
getState() {
|
|
7239
7698
|
return this.currentState;
|
|
7240
7699
|
}
|
|
7700
|
+
async getHttpAuthToken(forceRefresh = false) {
|
|
7701
|
+
return this.getOrRefreshToken(forceRefresh);
|
|
7702
|
+
}
|
|
7703
|
+
clearHttpAuthToken() {
|
|
7704
|
+
this.clearTokenState();
|
|
7705
|
+
}
|
|
7241
7706
|
onFrame(handler) {
|
|
7242
7707
|
this.frameHandlers.add(handler);
|
|
7243
7708
|
return () => {
|
|
@@ -7273,6 +7738,7 @@ class ConnectionManager {
|
|
|
7273
7738
|
return issue;
|
|
7274
7739
|
}
|
|
7275
7740
|
async connect() {
|
|
7741
|
+
this.requireWebsocketUrl();
|
|
7276
7742
|
if (this.ws?.readyState === WebSocket.OPEN ||
|
|
7277
7743
|
this.ws?.readyState === WebSocket.CONNECTING ||
|
|
7278
7744
|
this.currentState === 'connecting') {
|
|
@@ -7417,6 +7883,7 @@ class ConnectionManager {
|
|
|
7417
7883
|
}
|
|
7418
7884
|
}
|
|
7419
7885
|
subscribe(subscription) {
|
|
7886
|
+
this.requireWebsocketUrl();
|
|
7420
7887
|
const subKey = this.makeSubKey(subscription);
|
|
7421
7888
|
if (this.currentState === 'connected' && this.ws?.readyState === WebSocket.OPEN) {
|
|
7422
7889
|
if (this.activeSubscriptions.has(subKey)) {
|
|
@@ -7520,16 +7987,20 @@ class ConnectionManager {
|
|
|
7520
7987
|
}
|
|
7521
7988
|
}
|
|
7522
7989
|
|
|
7523
|
-
|
|
7990
|
+
const INTERNAL_SEQ_FIELD = '__seq';
|
|
7991
|
+
function isObject(item) {
|
|
7524
7992
|
return item !== null && typeof item === 'object' && !Array.isArray(item);
|
|
7525
7993
|
}
|
|
7526
|
-
function deepMergeWithAppend
|
|
7527
|
-
if (!isObject
|
|
7994
|
+
function deepMergeWithAppend(target, source, appendPaths, currentPath = '') {
|
|
7995
|
+
if (!isObject(target) || !isObject(source)) {
|
|
7528
7996
|
return source;
|
|
7529
7997
|
}
|
|
7530
7998
|
const result = { ...target };
|
|
7531
7999
|
for (const key in source) {
|
|
7532
8000
|
const sourceValue = source[key];
|
|
8001
|
+
if (sourceValue === undefined) {
|
|
8002
|
+
continue;
|
|
8003
|
+
}
|
|
7533
8004
|
const targetValue = result[key];
|
|
7534
8005
|
const fieldPath = currentPath ? `${currentPath}.${key}` : key;
|
|
7535
8006
|
if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
|
|
@@ -7540,8 +8011,8 @@ function deepMergeWithAppend$1(target, source, appendPaths, currentPath = '') {
|
|
|
7540
8011
|
result[key] = sourceValue;
|
|
7541
8012
|
}
|
|
7542
8013
|
}
|
|
7543
|
-
else if (isObject
|
|
7544
|
-
result[key] = deepMergeWithAppend
|
|
8014
|
+
else if (isObject(sourceValue) && isObject(targetValue)) {
|
|
8015
|
+
result[key] = deepMergeWithAppend(targetValue, sourceValue, appendPaths, fieldPath);
|
|
7545
8016
|
}
|
|
7546
8017
|
else {
|
|
7547
8018
|
result[key] = sourceValue;
|
|
@@ -7549,6 +8020,36 @@ function deepMergeWithAppend$1(target, source, appendPaths, currentPath = '') {
|
|
|
7549
8020
|
}
|
|
7550
8021
|
return result;
|
|
7551
8022
|
}
|
|
8023
|
+
function stripUndefinedProperties(value) {
|
|
8024
|
+
if (Array.isArray(value)) {
|
|
8025
|
+
return value.map(item => stripUndefinedProperties(item));
|
|
8026
|
+
}
|
|
8027
|
+
if (!isObject(value)) {
|
|
8028
|
+
return value;
|
|
8029
|
+
}
|
|
8030
|
+
const result = {};
|
|
8031
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
8032
|
+
if (nestedValue === undefined) {
|
|
8033
|
+
continue;
|
|
8034
|
+
}
|
|
8035
|
+
result[key] = stripUndefinedProperties(nestedValue);
|
|
8036
|
+
}
|
|
8037
|
+
return result;
|
|
8038
|
+
}
|
|
8039
|
+
function toCamelCaseSegment(value) {
|
|
8040
|
+
if (value === '_seq') {
|
|
8041
|
+
return INTERNAL_SEQ_FIELD;
|
|
8042
|
+
}
|
|
8043
|
+
const pascal = value
|
|
8044
|
+
.split(/[_.-]/)
|
|
8045
|
+
.filter(segment => segment.length > 0)
|
|
8046
|
+
.map(segment => segment[0].toUpperCase() + segment.slice(1))
|
|
8047
|
+
.join('');
|
|
8048
|
+
if (pascal.length === 0) {
|
|
8049
|
+
return value;
|
|
8050
|
+
}
|
|
8051
|
+
return pascal[0].toLowerCase() + pascal.slice(1);
|
|
8052
|
+
}
|
|
7552
8053
|
class FrameProcessor {
|
|
7553
8054
|
constructor(storage, config = {}) {
|
|
7554
8055
|
this.pendingUpdates = [];
|
|
@@ -7560,9 +8061,10 @@ class FrameProcessor {
|
|
|
7560
8061
|
: config.maxEntriesPerView;
|
|
7561
8062
|
this.flushIntervalMs = config.flushIntervalMs ?? 0;
|
|
7562
8063
|
this.schemas = config.schemas;
|
|
8064
|
+
this.patchSchemas = config.patchSchemas;
|
|
7563
8065
|
}
|
|
7564
|
-
getSchema(viewPath) {
|
|
7565
|
-
const schemas = this.schemas;
|
|
8066
|
+
getSchema(viewPath, patch = false) {
|
|
8067
|
+
const schemas = patch ? this.patchSchemas : this.schemas;
|
|
7566
8068
|
if (!schemas)
|
|
7567
8069
|
return null;
|
|
7568
8070
|
const entityName = viewPath.split('/')[0];
|
|
@@ -7571,19 +8073,86 @@ class FrameProcessor {
|
|
|
7571
8073
|
const entityKey = entityName;
|
|
7572
8074
|
return schemas[entityKey] ?? null;
|
|
7573
8075
|
}
|
|
7574
|
-
|
|
7575
|
-
const schema = this.getSchema(viewPath)
|
|
8076
|
+
normalizeEntity(viewPath, data, patch = false) {
|
|
8077
|
+
const schema = this.getSchema(viewPath, patch)
|
|
8078
|
+
?? (!patch ? null : this.getSchema(viewPath));
|
|
7576
8079
|
if (!schema)
|
|
7577
|
-
return
|
|
8080
|
+
return data;
|
|
7578
8081
|
const result = schema.safeParse(data);
|
|
7579
8082
|
if (!result.success) {
|
|
7580
8083
|
console.warn('[Arete] Frame validation failed:', {
|
|
7581
8084
|
view: viewPath,
|
|
7582
8085
|
error: result.error,
|
|
7583
8086
|
});
|
|
7584
|
-
return
|
|
8087
|
+
return null;
|
|
7585
8088
|
}
|
|
7586
|
-
return
|
|
8089
|
+
return stripUndefinedProperties(result.data);
|
|
8090
|
+
}
|
|
8091
|
+
hasSchema(viewPath) {
|
|
8092
|
+
return this.getSchema(viewPath) !== null;
|
|
8093
|
+
}
|
|
8094
|
+
normalizePathSegment(viewPath, segment) {
|
|
8095
|
+
if (!this.hasSchema(viewPath)) {
|
|
8096
|
+
return segment;
|
|
8097
|
+
}
|
|
8098
|
+
return toCamelCaseSegment(segment);
|
|
8099
|
+
}
|
|
8100
|
+
normalizePath(viewPath, path) {
|
|
8101
|
+
if (!this.hasSchema(viewPath)) {
|
|
8102
|
+
return path;
|
|
8103
|
+
}
|
|
8104
|
+
return path
|
|
8105
|
+
.split('.')
|
|
8106
|
+
.filter(segment => segment.length > 0)
|
|
8107
|
+
.map(segment => this.normalizePathSegment(viewPath, segment))
|
|
8108
|
+
.join('.');
|
|
8109
|
+
}
|
|
8110
|
+
normalizeSortConfig(viewPath, sort) {
|
|
8111
|
+
if (!this.hasSchema(viewPath)) {
|
|
8112
|
+
return sort;
|
|
8113
|
+
}
|
|
8114
|
+
return {
|
|
8115
|
+
...sort,
|
|
8116
|
+
field: sort.field.map(segment => this.normalizePathSegment(viewPath, segment)),
|
|
8117
|
+
};
|
|
8118
|
+
}
|
|
8119
|
+
normalizeAppendPaths(viewPath, append) {
|
|
8120
|
+
if (!append || append.length === 0 || !this.hasSchema(viewPath)) {
|
|
8121
|
+
return append ?? [];
|
|
8122
|
+
}
|
|
8123
|
+
return append.map(path => this.normalizePath(viewPath, path));
|
|
8124
|
+
}
|
|
8125
|
+
extractSeq(data) {
|
|
8126
|
+
if (!isObject(data)) {
|
|
8127
|
+
return undefined;
|
|
8128
|
+
}
|
|
8129
|
+
const seq = data._seq;
|
|
8130
|
+
if (typeof seq === 'string') {
|
|
8131
|
+
return seq;
|
|
8132
|
+
}
|
|
8133
|
+
if (typeof seq === 'number' && Number.isFinite(seq)) {
|
|
8134
|
+
return String(seq);
|
|
8135
|
+
}
|
|
8136
|
+
return undefined;
|
|
8137
|
+
}
|
|
8138
|
+
getInternalSeq(data) {
|
|
8139
|
+
if (!isObject(data)) {
|
|
8140
|
+
return undefined;
|
|
8141
|
+
}
|
|
8142
|
+
const seq = data[INTERNAL_SEQ_FIELD];
|
|
8143
|
+
return typeof seq === 'string' ? seq : undefined;
|
|
8144
|
+
}
|
|
8145
|
+
attachInternalSeq(viewPath, data, seq) {
|
|
8146
|
+
if (!seq || !this.hasSchema(viewPath) || !isObject(data)) {
|
|
8147
|
+
return data;
|
|
8148
|
+
}
|
|
8149
|
+
Object.defineProperty(data, INTERNAL_SEQ_FIELD, {
|
|
8150
|
+
value: seq,
|
|
8151
|
+
enumerable: false,
|
|
8152
|
+
configurable: true,
|
|
8153
|
+
writable: true,
|
|
8154
|
+
});
|
|
8155
|
+
return data;
|
|
7587
8156
|
}
|
|
7588
8157
|
handleFrame(frame) {
|
|
7589
8158
|
if (this.flushIntervalMs === 0) {
|
|
@@ -7669,7 +8238,9 @@ class FrameProcessor {
|
|
|
7669
8238
|
}
|
|
7670
8239
|
handleSubscribedFrame(frame) {
|
|
7671
8240
|
if (this.storage.setViewConfig && frame.sort) {
|
|
7672
|
-
this.storage.setViewConfig(frame.view, {
|
|
8241
|
+
this.storage.setViewConfig(frame.view, {
|
|
8242
|
+
sort: this.normalizeSortConfig(frame.view, frame.sort),
|
|
8243
|
+
});
|
|
7673
8244
|
}
|
|
7674
8245
|
}
|
|
7675
8246
|
handleSnapshotFrame(frame) {
|
|
@@ -7679,17 +8250,19 @@ class FrameProcessor {
|
|
|
7679
8250
|
handleSnapshotFrameWithoutEnforce(frame) {
|
|
7680
8251
|
const viewPath = frame.entity;
|
|
7681
8252
|
for (const entity of frame.data) {
|
|
7682
|
-
|
|
8253
|
+
const normalized = this.normalizeEntity(viewPath, entity.data);
|
|
8254
|
+
if (normalized === null) {
|
|
7683
8255
|
continue;
|
|
7684
8256
|
}
|
|
8257
|
+
const nextValue = this.attachInternalSeq(viewPath, normalized, this.extractSeq(entity.data));
|
|
7685
8258
|
const previousValue = this.storage.get(viewPath, entity.key);
|
|
7686
|
-
this.storage.set(viewPath, entity.key,
|
|
8259
|
+
this.storage.set(viewPath, entity.key, nextValue);
|
|
7687
8260
|
this.storage.notifyUpdate(viewPath, entity.key, {
|
|
7688
8261
|
type: 'upsert',
|
|
7689
8262
|
key: entity.key,
|
|
7690
|
-
data:
|
|
8263
|
+
data: nextValue,
|
|
7691
8264
|
});
|
|
7692
|
-
this.emitRichUpdate(viewPath, entity.key, previousValue,
|
|
8265
|
+
this.emitRichUpdate(viewPath, entity.key, previousValue, nextValue, 'upsert');
|
|
7693
8266
|
}
|
|
7694
8267
|
}
|
|
7695
8268
|
handleEntityFrame(frame) {
|
|
@@ -7702,33 +8275,39 @@ class FrameProcessor {
|
|
|
7702
8275
|
switch (frame.op) {
|
|
7703
8276
|
case 'create':
|
|
7704
8277
|
case 'upsert':
|
|
7705
|
-
|
|
8278
|
+
{
|
|
8279
|
+
const normalized = this.normalizeEntity(viewPath, frame.data);
|
|
8280
|
+
if (normalized === null) {
|
|
8281
|
+
break;
|
|
8282
|
+
}
|
|
8283
|
+
const nextValue = this.attachInternalSeq(viewPath, normalized, frame.seq ?? this.extractSeq(frame.data));
|
|
8284
|
+
this.storage.set(viewPath, frame.key, nextValue);
|
|
8285
|
+
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
8286
|
+
type: 'upsert',
|
|
8287
|
+
key: frame.key,
|
|
8288
|
+
data: nextValue,
|
|
8289
|
+
});
|
|
8290
|
+
this.emitRichUpdate(viewPath, frame.key, previousValue, nextValue, frame.op);
|
|
7706
8291
|
break;
|
|
7707
8292
|
}
|
|
7708
|
-
this.storage.set(viewPath, frame.key, frame.data);
|
|
7709
|
-
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
7710
|
-
type: 'upsert',
|
|
7711
|
-
key: frame.key,
|
|
7712
|
-
data: frame.data,
|
|
7713
|
-
});
|
|
7714
|
-
this.emitRichUpdate(viewPath, frame.key, previousValue, frame.data, frame.op);
|
|
7715
|
-
break;
|
|
7716
8293
|
case 'patch': {
|
|
7717
8294
|
const existing = this.storage.get(viewPath, frame.key);
|
|
7718
|
-
const
|
|
7719
|
-
|
|
7720
|
-
? deepMergeWithAppend$1(existing, frame.data, appendPaths)
|
|
7721
|
-
: frame.data;
|
|
7722
|
-
if (!this.validateEntity(viewPath, merged)) {
|
|
8295
|
+
const normalizedPatch = this.normalizeEntity(viewPath, frame.data, true);
|
|
8296
|
+
if (normalizedPatch === null) {
|
|
7723
8297
|
break;
|
|
7724
8298
|
}
|
|
7725
|
-
this.
|
|
8299
|
+
const appendPaths = this.normalizeAppendPaths(viewPath, frame.append);
|
|
8300
|
+
const merged = existing
|
|
8301
|
+
? deepMergeWithAppend(existing, normalizedPatch, appendPaths)
|
|
8302
|
+
: normalizedPatch;
|
|
8303
|
+
const nextValue = this.attachInternalSeq(viewPath, merged, frame.seq ?? this.extractSeq(frame.data) ?? this.getInternalSeq(existing));
|
|
8304
|
+
this.storage.set(viewPath, frame.key, nextValue);
|
|
7726
8305
|
this.storage.notifyUpdate(viewPath, frame.key, {
|
|
7727
8306
|
type: 'patch',
|
|
7728
8307
|
key: frame.key,
|
|
7729
|
-
data:
|
|
8308
|
+
data: normalizedPatch,
|
|
7730
8309
|
});
|
|
7731
|
-
this.emitRichUpdate(viewPath, frame.key, previousValue,
|
|
8310
|
+
this.emitRichUpdate(viewPath, frame.key, previousValue, nextValue, 'patch', normalizedPatch);
|
|
7732
8311
|
break;
|
|
7733
8312
|
}
|
|
7734
8313
|
case 'delete':
|
|
@@ -7761,7 +8340,7 @@ class FrameProcessor {
|
|
|
7761
8340
|
}
|
|
7762
8341
|
}
|
|
7763
8342
|
|
|
7764
|
-
|
|
8343
|
+
class ViewData {
|
|
7765
8344
|
constructor() {
|
|
7766
8345
|
this.entities = new Map();
|
|
7767
8346
|
this.accessOrder = [];
|
|
@@ -7815,7 +8394,7 @@ let ViewData$1 = class ViewData {
|
|
|
7815
8394
|
this.entities.clear();
|
|
7816
8395
|
this.accessOrder = [];
|
|
7817
8396
|
}
|
|
7818
|
-
}
|
|
8397
|
+
}
|
|
7819
8398
|
class MemoryAdapter {
|
|
7820
8399
|
constructor(_config = {}) {
|
|
7821
8400
|
this.views = new Map();
|
|
@@ -7863,7 +8442,7 @@ class MemoryAdapter {
|
|
|
7863
8442
|
set(viewPath, key, data) {
|
|
7864
8443
|
let view = this.views.get(viewPath);
|
|
7865
8444
|
if (!view) {
|
|
7866
|
-
view = new ViewData
|
|
8445
|
+
view = new ViewData();
|
|
7867
8446
|
this.views.set(viewPath, view);
|
|
7868
8447
|
}
|
|
7869
8448
|
view.set(key, data);
|
|
@@ -7903,7 +8482,7 @@ class MemoryAdapter {
|
|
|
7903
8482
|
}
|
|
7904
8483
|
}
|
|
7905
8484
|
|
|
7906
|
-
function getNestedValue
|
|
8485
|
+
function getNestedValue(obj, path) {
|
|
7907
8486
|
let current = obj;
|
|
7908
8487
|
for (const segment of path) {
|
|
7909
8488
|
if (current === null || current === undefined)
|
|
@@ -7914,16 +8493,25 @@ function getNestedValue$1(obj, path) {
|
|
|
7914
8493
|
}
|
|
7915
8494
|
return current;
|
|
7916
8495
|
}
|
|
7917
|
-
function compareSortValues
|
|
8496
|
+
function compareSortValues(a, b) {
|
|
7918
8497
|
if (a === b)
|
|
7919
8498
|
return 0;
|
|
7920
8499
|
if (a === undefined || a === null)
|
|
7921
8500
|
return -1;
|
|
7922
8501
|
if (b === undefined || b === null)
|
|
7923
8502
|
return 1;
|
|
8503
|
+
if (typeof a === 'bigint' && typeof b === 'bigint') {
|
|
8504
|
+
return a < b ? -1 : 1;
|
|
8505
|
+
}
|
|
7924
8506
|
if (typeof a === 'number' && typeof b === 'number') {
|
|
7925
8507
|
return a - b;
|
|
7926
8508
|
}
|
|
8509
|
+
if (typeof a === 'bigint' && typeof b === 'number' && Number.isInteger(b)) {
|
|
8510
|
+
return a < BigInt(b) ? -1 : 1;
|
|
8511
|
+
}
|
|
8512
|
+
if (typeof a === 'number' && typeof b === 'bigint' && Number.isInteger(a)) {
|
|
8513
|
+
return BigInt(a) < b ? -1 : 1;
|
|
8514
|
+
}
|
|
7927
8515
|
if (typeof a === 'string' && typeof b === 'string') {
|
|
7928
8516
|
return a.localeCompare(b);
|
|
7929
8517
|
}
|
|
@@ -8050,7 +8638,7 @@ class SortedStorageDecorator {
|
|
|
8050
8638
|
sortedKeys.splice(insertIdx, 0, key);
|
|
8051
8639
|
}
|
|
8052
8640
|
binarySearchInsertPosition(viewPath, sortedKeys, sortConfig, newKey, newValue) {
|
|
8053
|
-
const newSortValue = getNestedValue
|
|
8641
|
+
const newSortValue = getNestedValue(newValue, sortConfig.field);
|
|
8054
8642
|
const isDesc = sortConfig.order === 'desc';
|
|
8055
8643
|
let low = 0;
|
|
8056
8644
|
let high = sortedKeys.length;
|
|
@@ -8058,8 +8646,8 @@ class SortedStorageDecorator {
|
|
|
8058
8646
|
const mid = Math.floor((low + high) / 2);
|
|
8059
8647
|
const midKey = sortedKeys[mid];
|
|
8060
8648
|
const midEntity = this.inner.get(viewPath, midKey);
|
|
8061
|
-
const midValue = getNestedValue
|
|
8062
|
-
let cmp = compareSortValues
|
|
8649
|
+
const midValue = getNestedValue(midEntity, sortConfig.field);
|
|
8650
|
+
let cmp = compareSortValues(newSortValue, midValue);
|
|
8063
8651
|
if (isDesc)
|
|
8064
8652
|
cmp = -cmp;
|
|
8065
8653
|
if (cmp === 0) {
|
|
@@ -8081,9 +8669,9 @@ class SortedStorageDecorator {
|
|
|
8081
8669
|
const isDesc = sortConfig.order === 'desc';
|
|
8082
8670
|
const entries = allKeys.map(k => [k, this.inner.get(viewPath, k)]);
|
|
8083
8671
|
entries.sort((a, b) => {
|
|
8084
|
-
const aValue = getNestedValue
|
|
8085
|
-
const bValue = getNestedValue
|
|
8086
|
-
let cmp = compareSortValues
|
|
8672
|
+
const aValue = getNestedValue(a[1], sortConfig.field);
|
|
8673
|
+
const bValue = getNestedValue(b[1], sortConfig.field);
|
|
8674
|
+
let cmp = compareSortValues(aValue, bValue);
|
|
8087
8675
|
if (isDesc)
|
|
8088
8676
|
cmp = -cmp;
|
|
8089
8677
|
if (cmp === 0) {
|
|
@@ -8413,6 +9001,137 @@ function createTypedViews(stack, storage, subscriptionRegistry) {
|
|
|
8413
9001
|
return views;
|
|
8414
9002
|
}
|
|
8415
9003
|
|
|
9004
|
+
class ReadRequestError extends Error {
|
|
9005
|
+
constructor(input) {
|
|
9006
|
+
super(`Read request to '${input.path}' failed (${input.status}): ${input.body}`);
|
|
9007
|
+
this.name = 'ReadRequestError';
|
|
9008
|
+
this.status = input.status;
|
|
9009
|
+
this.path = input.path;
|
|
9010
|
+
this.body = input.body;
|
|
9011
|
+
this.serverErrorCode = input.serverErrorCode;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
function getServerErrorCode(response, body) {
|
|
9015
|
+
const headerCode = response.headers.get('X-Error-Code');
|
|
9016
|
+
if (headerCode) {
|
|
9017
|
+
return headerCode;
|
|
9018
|
+
}
|
|
9019
|
+
try {
|
|
9020
|
+
const parsed = JSON.parse(body);
|
|
9021
|
+
return typeof parsed.code === 'string' ? parsed.code : undefined;
|
|
9022
|
+
}
|
|
9023
|
+
catch {
|
|
9024
|
+
return undefined;
|
|
9025
|
+
}
|
|
9026
|
+
}
|
|
9027
|
+
async function parseReadResponse(response, path) {
|
|
9028
|
+
if (!response.ok) {
|
|
9029
|
+
const body = await response.text();
|
|
9030
|
+
throw new ReadRequestError({
|
|
9031
|
+
status: response.status,
|
|
9032
|
+
path,
|
|
9033
|
+
body,
|
|
9034
|
+
serverErrorCode: getServerErrorCode(response, body),
|
|
9035
|
+
});
|
|
9036
|
+
}
|
|
9037
|
+
return response.json();
|
|
9038
|
+
}
|
|
9039
|
+
|
|
9040
|
+
function joinUrl(baseUrl, path) {
|
|
9041
|
+
return `${baseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
|
|
9042
|
+
}
|
|
9043
|
+
function decodeBase64(encoded) {
|
|
9044
|
+
if (typeof atob === 'function') {
|
|
9045
|
+
const binary = atob(encoded);
|
|
9046
|
+
const bytes = new Uint8Array(binary.length);
|
|
9047
|
+
for (let i = 0; i < binary.length; i++) {
|
|
9048
|
+
bytes[i] = binary.charCodeAt(i);
|
|
9049
|
+
}
|
|
9050
|
+
return bytes;
|
|
9051
|
+
}
|
|
9052
|
+
const bufferCtor = globalThis.Buffer;
|
|
9053
|
+
if (bufferCtor) {
|
|
9054
|
+
return new Uint8Array(bufferCtor.from(encoded, 'base64'));
|
|
9055
|
+
}
|
|
9056
|
+
throw new Error('No base64 decoder available in this environment');
|
|
9057
|
+
}
|
|
9058
|
+
function deriveHttpEndpoint(wsUrl) {
|
|
9059
|
+
try {
|
|
9060
|
+
const parsed = new URL(wsUrl);
|
|
9061
|
+
if (parsed.protocol === 'ws:') {
|
|
9062
|
+
parsed.protocol = 'http:';
|
|
9063
|
+
}
|
|
9064
|
+
else if (parsed.protocol === 'wss:') {
|
|
9065
|
+
parsed.protocol = 'https:';
|
|
9066
|
+
}
|
|
9067
|
+
return parsed.toString().replace(/\/$/, '');
|
|
9068
|
+
}
|
|
9069
|
+
catch {
|
|
9070
|
+
return wsUrl.replace(/^wss?:/i, (protocol) => protocol.toLowerCase() === 'wss:' ? 'https:' : 'http:');
|
|
9071
|
+
}
|
|
9072
|
+
}
|
|
9073
|
+
function createChainClient(httpBaseUrl, fetchImpl) {
|
|
9074
|
+
return {
|
|
9075
|
+
async exists(address) {
|
|
9076
|
+
const path = `/chain/exists/${encodeURIComponent(address)}`;
|
|
9077
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9078
|
+
const body = await parseReadResponse(response, path);
|
|
9079
|
+
return body.exists;
|
|
9080
|
+
},
|
|
9081
|
+
async lamports(address) {
|
|
9082
|
+
const path = `/chain/lamports/${encodeURIComponent(address)}`;
|
|
9083
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9084
|
+
const body = await parseReadResponse(response, path);
|
|
9085
|
+
return body.lamports;
|
|
9086
|
+
},
|
|
9087
|
+
async minimumBalanceForRentExemption(space) {
|
|
9088
|
+
const path = `/chain/rent-exemption/${encodeURIComponent(String(space))}`;
|
|
9089
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9090
|
+
const body = await parseReadResponse(response, path);
|
|
9091
|
+
return body.lamports;
|
|
9092
|
+
},
|
|
9093
|
+
async clock() {
|
|
9094
|
+
const path = '/chain/clock';
|
|
9095
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9096
|
+
return parseReadResponse(response, path);
|
|
9097
|
+
},
|
|
9098
|
+
async account(address) {
|
|
9099
|
+
const path = `/chain/accounts/${encodeURIComponent(address)}`;
|
|
9100
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9101
|
+
const body = await parseReadResponse(response, path);
|
|
9102
|
+
if (!body) {
|
|
9103
|
+
return null;
|
|
9104
|
+
}
|
|
9105
|
+
return {
|
|
9106
|
+
address: body.address,
|
|
9107
|
+
ownerProgram: body.ownerProgram,
|
|
9108
|
+
lamports: body.lamports,
|
|
9109
|
+
executable: body.executable,
|
|
9110
|
+
data: decodeBase64(body.data),
|
|
9111
|
+
};
|
|
9112
|
+
},
|
|
9113
|
+
async mint(address) {
|
|
9114
|
+
const path = `/chain/mints/${encodeURIComponent(address)}`;
|
|
9115
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9116
|
+
return parseReadResponse(response, path);
|
|
9117
|
+
},
|
|
9118
|
+
async tokenAccount(address) {
|
|
9119
|
+
const path = `/chain/token-accounts/${encodeURIComponent(address)}`;
|
|
9120
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path));
|
|
9121
|
+
return parseReadResponse(response, path);
|
|
9122
|
+
},
|
|
9123
|
+
async balance(input) {
|
|
9124
|
+
const path = '/chain/balances';
|
|
9125
|
+
const response = await fetchImpl(joinUrl(httpBaseUrl, path), {
|
|
9126
|
+
method: 'POST',
|
|
9127
|
+
headers: { 'content-type': 'application/json' },
|
|
9128
|
+
body: JSON.stringify(input),
|
|
9129
|
+
});
|
|
9130
|
+
return parseReadResponse(response, path);
|
|
9131
|
+
},
|
|
9132
|
+
};
|
|
9133
|
+
}
|
|
9134
|
+
|
|
8416
9135
|
/**
|
|
8417
9136
|
* PDA (Program Derived Address) derivation utilities.
|
|
8418
9137
|
*
|
|
@@ -8427,7 +9146,10 @@ function decodeBase58(str) {
|
|
|
8427
9146
|
if (str.length === 0) {
|
|
8428
9147
|
return new Uint8Array(0);
|
|
8429
9148
|
}
|
|
8430
|
-
|
|
9149
|
+
// Big-endian byte accumulator (stored little-endian here, reversed at the
|
|
9150
|
+
// end). Must start empty: a leading `[0]` produces a spurious extra byte for
|
|
9151
|
+
// all-zero values such as the System Program ("111...1").
|
|
9152
|
+
const bytes = [];
|
|
8431
9153
|
for (const char of str) {
|
|
8432
9154
|
const value = BASE58_ALPHABET.indexOf(char);
|
|
8433
9155
|
if (value === -1) {
|
|
@@ -8459,7 +9181,9 @@ function encodeBase58(bytes) {
|
|
|
8459
9181
|
if (bytes.length === 0) {
|
|
8460
9182
|
return '';
|
|
8461
9183
|
}
|
|
8462
|
-
|
|
9184
|
+
// Must start empty for the same reason as `decodeBase58`: a leading `[0]`
|
|
9185
|
+
// yields an extra '1' character when encoding all-zero inputs.
|
|
9186
|
+
const digits = [];
|
|
8463
9187
|
for (const byte of bytes) {
|
|
8464
9188
|
let carry = byte;
|
|
8465
9189
|
for (let i = 0; i < digits.length; i++) {
|
|
@@ -8480,25 +9204,125 @@ function encodeBase58(bytes) {
|
|
|
8480
9204
|
}
|
|
8481
9205
|
return digits.reverse().map(d => BASE58_ALPHABET[d]).join('');
|
|
8482
9206
|
}
|
|
9207
|
+
// SHA-256 round constants (first 32 bits of the fractional parts of the cube
|
|
9208
|
+
// roots of the first 64 primes).
|
|
9209
|
+
const SHA256_K = new Uint32Array([
|
|
9210
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
9211
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
9212
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
9213
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
9214
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
9215
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
9216
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
9217
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
9218
|
+
]);
|
|
9219
|
+
/**
|
|
9220
|
+
* Dependency-free, synchronous SHA-256.
|
|
9221
|
+
*
|
|
9222
|
+
* Used so PDA derivation works identically in browsers, Node (both CJS and
|
|
9223
|
+
* ESM), and bundlers without relying on `require('crypto')` or async WebCrypto.
|
|
9224
|
+
*/
|
|
9225
|
+
function sha256Pure(data) {
|
|
9226
|
+
const h = new Uint32Array([
|
|
9227
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
|
9228
|
+
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
9229
|
+
]);
|
|
9230
|
+
const bitLen = data.length * 8;
|
|
9231
|
+
// Pad: append 0x80, then zeros, until length ≡ 56 (mod 64), then 8-byte length.
|
|
9232
|
+
const paddedLen = ((data.length + 8) >> 6) * 64 + 64;
|
|
9233
|
+
const msg = new Uint8Array(paddedLen);
|
|
9234
|
+
msg.set(data);
|
|
9235
|
+
msg[data.length] = 0x80;
|
|
9236
|
+
// 64-bit big-endian bit length (high 32 bits assumed 0 for our seed sizes).
|
|
9237
|
+
const dv = new DataView(msg.buffer);
|
|
9238
|
+
dv.setUint32(paddedLen - 4, bitLen >>> 0, false);
|
|
9239
|
+
dv.setUint32(paddedLen - 8, Math.floor(bitLen / 0x100000000), false);
|
|
9240
|
+
const w = new Uint32Array(64);
|
|
9241
|
+
for (let offset = 0; offset < paddedLen; offset += 64) {
|
|
9242
|
+
for (let i = 0; i < 16; i++) {
|
|
9243
|
+
w[i] = dv.getUint32(offset + i * 4, false);
|
|
9244
|
+
}
|
|
9245
|
+
for (let i = 16; i < 64; i++) {
|
|
9246
|
+
const w15 = w[i - 15];
|
|
9247
|
+
const w2 = w[i - 2];
|
|
9248
|
+
const s0 = ((w15 >>> 7) | (w15 << 25)) ^ ((w15 >>> 18) | (w15 << 14)) ^ (w15 >>> 3);
|
|
9249
|
+
const s1 = ((w2 >>> 17) | (w2 << 15)) ^ ((w2 >>> 19) | (w2 << 13)) ^ (w2 >>> 10);
|
|
9250
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
|
9251
|
+
}
|
|
9252
|
+
let a = h[0], b = h[1], c = h[2], d = h[3];
|
|
9253
|
+
let e = h[4], f = h[5], g = h[6], hh = h[7];
|
|
9254
|
+
for (let i = 0; i < 64; i++) {
|
|
9255
|
+
const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
|
9256
|
+
const ch = (e & f) ^ (~e & g);
|
|
9257
|
+
const t1 = (hh + S1 + ch + SHA256_K[i] + w[i]) >>> 0;
|
|
9258
|
+
const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
|
9259
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
9260
|
+
const t2 = (S0 + maj) >>> 0;
|
|
9261
|
+
hh = g;
|
|
9262
|
+
g = f;
|
|
9263
|
+
f = e;
|
|
9264
|
+
e = (d + t1) >>> 0;
|
|
9265
|
+
d = c;
|
|
9266
|
+
c = b;
|
|
9267
|
+
b = a;
|
|
9268
|
+
a = (t1 + t2) >>> 0;
|
|
9269
|
+
}
|
|
9270
|
+
h[0] = (h[0] + a) >>> 0;
|
|
9271
|
+
h[1] = (h[1] + b) >>> 0;
|
|
9272
|
+
h[2] = (h[2] + c) >>> 0;
|
|
9273
|
+
h[3] = (h[3] + d) >>> 0;
|
|
9274
|
+
h[4] = (h[4] + e) >>> 0;
|
|
9275
|
+
h[5] = (h[5] + f) >>> 0;
|
|
9276
|
+
h[6] = (h[6] + g) >>> 0;
|
|
9277
|
+
h[7] = (h[7] + hh) >>> 0;
|
|
9278
|
+
}
|
|
9279
|
+
const out = new Uint8Array(32);
|
|
9280
|
+
const outView = new DataView(out.buffer);
|
|
9281
|
+
for (let i = 0; i < 8; i++) {
|
|
9282
|
+
outView.setUint32(i * 4, h[i], false);
|
|
9283
|
+
}
|
|
9284
|
+
return out;
|
|
9285
|
+
}
|
|
8483
9286
|
/**
|
|
8484
|
-
* SHA-256 hash function (synchronous
|
|
9287
|
+
* SHA-256 hash function (synchronous).
|
|
8485
9288
|
*/
|
|
8486
9289
|
function sha256Sync(data) {
|
|
8487
|
-
|
|
8488
|
-
const { createHash } = require('crypto');
|
|
8489
|
-
return new Uint8Array(createHash('sha256').update(Buffer.from(data)).digest());
|
|
9290
|
+
return sha256Pure(data);
|
|
8490
9291
|
}
|
|
8491
9292
|
/**
|
|
8492
|
-
* SHA-256 hash function (async
|
|
9293
|
+
* SHA-256 hash function (async). Uses WebCrypto when available for speed,
|
|
9294
|
+
* otherwise falls back to the pure implementation.
|
|
8493
9295
|
*/
|
|
8494
9296
|
async function sha256Async(data) {
|
|
8495
9297
|
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle) {
|
|
8496
|
-
// Create a copy of the data to ensure we have an ArrayBuffer
|
|
8497
9298
|
const copy = new Uint8Array(data);
|
|
8498
9299
|
const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', copy);
|
|
8499
9300
|
return new Uint8Array(hashBuffer);
|
|
8500
9301
|
}
|
|
8501
|
-
return
|
|
9302
|
+
return sha256Pure(data);
|
|
9303
|
+
}
|
|
9304
|
+
/**
|
|
9305
|
+
* Check if a 32-byte value is a valid point on the ed25519 curve.
|
|
9306
|
+
*
|
|
9307
|
+
* A valid PDA must be OFF the curve (it must NOT correspond to a real
|
|
9308
|
+
* ed25519 public key, so that no private key can ever sign for it).
|
|
9309
|
+
*
|
|
9310
|
+
* We determine on-curve status by attempting to decompress the candidate
|
|
9311
|
+
* as a compressed Edwards point. If decompression succeeds the value lies
|
|
9312
|
+
* on the curve; if it throws, the value is off-curve and is a valid PDA.
|
|
9313
|
+
* This matches the behaviour of `PublicKey.isOnCurve` in @solana/web3.js.
|
|
9314
|
+
*/
|
|
9315
|
+
function isOnCurve(publicKey) {
|
|
9316
|
+
if (publicKey.length !== 32) {
|
|
9317
|
+
return false;
|
|
9318
|
+
}
|
|
9319
|
+
try {
|
|
9320
|
+
Point.fromHex(publicKey);
|
|
9321
|
+
return true;
|
|
9322
|
+
}
|
|
9323
|
+
catch {
|
|
9324
|
+
return false;
|
|
9325
|
+
}
|
|
8502
9326
|
}
|
|
8503
9327
|
/**
|
|
8504
9328
|
* PDA marker bytes appended to seeds before hashing.
|
|
@@ -8566,7 +9390,7 @@ async function findProgramAddress(seeds, programId) {
|
|
|
8566
9390
|
for (let bump = 255; bump >= 0; bump--) {
|
|
8567
9391
|
const buffer = buildPdaBuffer(seeds, programIdBytes, bump);
|
|
8568
9392
|
const hash = await sha256Async(buffer);
|
|
8569
|
-
{
|
|
9393
|
+
if (!isOnCurve(hash)) {
|
|
8570
9394
|
return [encodeBase58(hash), bump];
|
|
8571
9395
|
}
|
|
8572
9396
|
}
|
|
@@ -8586,7 +9410,7 @@ function findProgramAddressSync(seeds, programId) {
|
|
|
8586
9410
|
for (let bump = 255; bump >= 0; bump--) {
|
|
8587
9411
|
const buffer = buildPdaBuffer(seeds, programIdBytes, bump);
|
|
8588
9412
|
const hash = sha256Sync(buffer);
|
|
8589
|
-
{
|
|
9413
|
+
if (!isOnCurve(hash)) {
|
|
8590
9414
|
return [encodeBase58(hash), bump];
|
|
8591
9415
|
}
|
|
8592
9416
|
}
|
|
@@ -8635,6 +9459,134 @@ function createPublicKeySeed(address) {
|
|
|
8635
9459
|
return decoded;
|
|
8636
9460
|
}
|
|
8637
9461
|
|
|
9462
|
+
/**
|
|
9463
|
+
* Typed serialization of PDA seed values.
|
|
9464
|
+
*
|
|
9465
|
+
* Shared by the standalone PDA DSL (`pda-dsl.ts`) and instruction account
|
|
9466
|
+
* resolution (`account-resolver.ts`). When a seed carries a declared type
|
|
9467
|
+
* (from the IDL or the `pdas!` registry), encoding is exact: pubkeys are
|
|
9468
|
+
* base58-decoded to 32 bytes, integers are little-endian at the declared
|
|
9469
|
+
* width. Without a type, legacy heuristics apply for backward compatibility.
|
|
9470
|
+
*/
|
|
9471
|
+
/**
|
|
9472
|
+
* Normalizes the type-name variants that IDLs and codegen produce
|
|
9473
|
+
* ("Pubkey", "publicKey", "solana_pubkey::Pubkey", "String", ...) to a
|
|
9474
|
+
* canonical seed type. Returns undefined for types that cannot be a seed.
|
|
9475
|
+
*/
|
|
9476
|
+
function normalizeSeedType(argType) {
|
|
9477
|
+
if (!argType)
|
|
9478
|
+
return undefined;
|
|
9479
|
+
// Strip any path qualifier (e.g. solana_pubkey::Pubkey).
|
|
9480
|
+
const parts = argType.split('::');
|
|
9481
|
+
const t = (parts[parts.length - 1] ?? argType).trim();
|
|
9482
|
+
if (/^[ui](8|16|32|64|128)$/.test(t)) {
|
|
9483
|
+
return t;
|
|
9484
|
+
}
|
|
9485
|
+
switch (t) {
|
|
9486
|
+
case 'pubkey':
|
|
9487
|
+
case 'Pubkey':
|
|
9488
|
+
case 'publicKey':
|
|
9489
|
+
case 'PublicKey':
|
|
9490
|
+
return 'pubkey';
|
|
9491
|
+
case 'string':
|
|
9492
|
+
case 'String':
|
|
9493
|
+
case 'str':
|
|
9494
|
+
return 'string';
|
|
9495
|
+
default:
|
|
9496
|
+
return undefined;
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
/**
|
|
9500
|
+
* Serializes a PDA seed value.
|
|
9501
|
+
*
|
|
9502
|
+
* With a recognized `argType`, encoding is strict and width-exact; an
|
|
9503
|
+
* incompatible value throws rather than deriving a wrong address. Without
|
|
9504
|
+
* one, the legacy heuristics apply: raw bytes pass through, 43/44-character
|
|
9505
|
+
* strings are tried as base58, other strings are utf-8, numbers are 8-byte
|
|
9506
|
+
* little-endian u64.
|
|
9507
|
+
*/
|
|
9508
|
+
function serializeSeedValue(value, argType) {
|
|
9509
|
+
if (value instanceof Uint8Array) {
|
|
9510
|
+
return value;
|
|
9511
|
+
}
|
|
9512
|
+
const t = normalizeSeedType(argType);
|
|
9513
|
+
if (t === 'pubkey') {
|
|
9514
|
+
if (typeof value !== 'string') {
|
|
9515
|
+
throw new Error(`Pubkey seed requires a base58 string, got ${typeof value}`);
|
|
9516
|
+
}
|
|
9517
|
+
const decoded = decodeBase58(value);
|
|
9518
|
+
if (decoded.length !== 32) {
|
|
9519
|
+
throw new Error(`Pubkey seed '${value}' decoded to ${decoded.length} bytes, expected 32`);
|
|
9520
|
+
}
|
|
9521
|
+
return decoded;
|
|
9522
|
+
}
|
|
9523
|
+
if (t === 'string') {
|
|
9524
|
+
if (typeof value !== 'string') {
|
|
9525
|
+
throw new Error(`String seed requires a string value, got ${typeof value}`);
|
|
9526
|
+
}
|
|
9527
|
+
return new TextEncoder().encode(value);
|
|
9528
|
+
}
|
|
9529
|
+
if (t !== undefined) {
|
|
9530
|
+
// Numeric type at a declared width.
|
|
9531
|
+
if (typeof value !== 'bigint' && typeof value !== 'number') {
|
|
9532
|
+
throw new Error(`Numeric seed of type ${t} requires a number/bigint, got ${typeof value}`);
|
|
9533
|
+
}
|
|
9534
|
+
const bits = parseInt(t.slice(1), 10);
|
|
9535
|
+
return serializeNumber(value, bits / 8, t.startsWith('i'));
|
|
9536
|
+
}
|
|
9537
|
+
// --- Untyped: legacy heuristics. ---
|
|
9538
|
+
if (typeof value === 'string') {
|
|
9539
|
+
if (value.length === 43 || value.length === 44) {
|
|
9540
|
+
try {
|
|
9541
|
+
return decodeBase58(value);
|
|
9542
|
+
}
|
|
9543
|
+
catch {
|
|
9544
|
+
return new TextEncoder().encode(value);
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
return new TextEncoder().encode(value);
|
|
9548
|
+
}
|
|
9549
|
+
if (typeof value === 'bigint' || typeof value === 'number') {
|
|
9550
|
+
return serializeNumber(value, 8, true);
|
|
9551
|
+
}
|
|
9552
|
+
throw new Error(`Cannot serialize value for PDA seed: ${typeof value}`);
|
|
9553
|
+
}
|
|
9554
|
+
/**
|
|
9555
|
+
* Little-endian two's-complement encoding at a fixed byte width, with an
|
|
9556
|
+
* overflow check so out-of-range values fail instead of silently truncating.
|
|
9557
|
+
*/
|
|
9558
|
+
function serializeNumber(value, size, signed) {
|
|
9559
|
+
const buffer = new Uint8Array(size);
|
|
9560
|
+
let n = typeof value === 'bigint' ? value : BigInt(value);
|
|
9561
|
+
const original = n;
|
|
9562
|
+
for (let i = 0; i < size; i++) {
|
|
9563
|
+
buffer[i] = Number(n & BigInt(0xff));
|
|
9564
|
+
n >>= BigInt(8);
|
|
9565
|
+
}
|
|
9566
|
+
const fits = signed ? n === BigInt(0) || n === BigInt(-1) : n === BigInt(0);
|
|
9567
|
+
if (!fits) {
|
|
9568
|
+
throw new Error(`Seed value ${original} does not fit in ${size * 8} bits`);
|
|
9569
|
+
}
|
|
9570
|
+
return buffer;
|
|
9571
|
+
}
|
|
9572
|
+
|
|
9573
|
+
function getValueByPath(source, path) {
|
|
9574
|
+
if (!source) {
|
|
9575
|
+
return undefined;
|
|
9576
|
+
}
|
|
9577
|
+
if (Object.prototype.hasOwnProperty.call(source, path)) {
|
|
9578
|
+
return source[path];
|
|
9579
|
+
}
|
|
9580
|
+
let current = source;
|
|
9581
|
+
for (const segment of path.split('.')) {
|
|
9582
|
+
if (current === null || typeof current !== 'object') {
|
|
9583
|
+
return undefined;
|
|
9584
|
+
}
|
|
9585
|
+
current = current[segment];
|
|
9586
|
+
}
|
|
9587
|
+
return current;
|
|
9588
|
+
}
|
|
9589
|
+
|
|
8638
9590
|
/**
|
|
8639
9591
|
* Topologically sort accounts so that dependencies (accountRef) are resolved first.
|
|
8640
9592
|
* Non-PDA accounts come first, then PDAs in dependency order.
|
|
@@ -8721,13 +9673,32 @@ function resolveAccounts(accountMetas, args, options) {
|
|
|
8721
9673
|
missing.push(meta.name);
|
|
8722
9674
|
}
|
|
8723
9675
|
}
|
|
8724
|
-
// Return accounts in original order (as defined in accountMetas)
|
|
9676
|
+
// Return accounts in original order (as defined in accountMetas).
|
|
9677
|
+
//
|
|
9678
|
+
// Omitted optional accounts that precede a resolved account cannot simply
|
|
9679
|
+
// be dropped — that would shift every later account into the wrong slot.
|
|
9680
|
+
// Anchor's convention is to pass the program ID as a placeholder; trailing
|
|
9681
|
+
// omitted optionals are dropped as usual.
|
|
9682
|
+
const lastResolvedIndex = accountMetas.reduce((last, meta, index) => (resolvedMap[meta.name] ? index : last), -1);
|
|
8725
9683
|
const orderedAccounts = [];
|
|
8726
|
-
for (
|
|
9684
|
+
for (let i = 0; i < accountMetas.length; i++) {
|
|
9685
|
+
const meta = accountMetas[i];
|
|
8727
9686
|
const resolved = resolvedMap[meta.name];
|
|
8728
9687
|
if (resolved) {
|
|
8729
9688
|
orderedAccounts.push(resolved);
|
|
8730
9689
|
}
|
|
9690
|
+
else if (meta.isOptional && i < lastResolvedIndex) {
|
|
9691
|
+
if (!options.programId) {
|
|
9692
|
+
throw new Error('Omitted optional account "' + meta.name + '" precedes other accounts and needs ' +
|
|
9693
|
+
'the program ID as a placeholder, but no programId was provided in options.');
|
|
9694
|
+
}
|
|
9695
|
+
orderedAccounts.push({
|
|
9696
|
+
name: meta.name,
|
|
9697
|
+
address: options.programId,
|
|
9698
|
+
isSigner: false,
|
|
9699
|
+
isWritable: false,
|
|
9700
|
+
});
|
|
9701
|
+
}
|
|
8731
9702
|
}
|
|
8732
9703
|
return {
|
|
8733
9704
|
accounts: orderedAccounts,
|
|
@@ -8737,24 +9708,25 @@ function resolveAccounts(accountMetas, args, options) {
|
|
|
8737
9708
|
function resolveSingleAccount(meta, args, options, resolvedMap) {
|
|
8738
9709
|
switch (meta.category) {
|
|
8739
9710
|
case 'signer':
|
|
8740
|
-
return resolveSignerAccount(meta, options.wallet);
|
|
9711
|
+
return resolveSignerAccount(meta, options.accounts, options.wallet);
|
|
8741
9712
|
case 'known':
|
|
8742
9713
|
return resolveKnownAccount(meta);
|
|
8743
9714
|
case 'pda':
|
|
8744
|
-
return resolvePdaAccount(meta, args, resolvedMap, options.programId);
|
|
9715
|
+
return resolvePdaAccount(meta, args, resolvedMap, options.programId, options.resolve);
|
|
8745
9716
|
case 'userProvided':
|
|
8746
9717
|
return resolveUserProvidedAccount(meta, options.accounts);
|
|
8747
9718
|
default:
|
|
8748
9719
|
return null;
|
|
8749
9720
|
}
|
|
8750
9721
|
}
|
|
8751
|
-
function resolveSignerAccount(meta, wallet) {
|
|
8752
|
-
|
|
9722
|
+
function resolveSignerAccount(meta, accounts, wallet) {
|
|
9723
|
+
const address = accounts?.[meta.name] ?? wallet?.publicKey;
|
|
9724
|
+
if (!address) {
|
|
8753
9725
|
return null;
|
|
8754
9726
|
}
|
|
8755
9727
|
return {
|
|
8756
9728
|
name: meta.name,
|
|
8757
|
-
address
|
|
9729
|
+
address,
|
|
8758
9730
|
isSigner: true,
|
|
8759
9731
|
isWritable: meta.isWritable,
|
|
8760
9732
|
};
|
|
@@ -8770,7 +9742,7 @@ function resolveKnownAccount(meta) {
|
|
|
8770
9742
|
isWritable: meta.isWritable,
|
|
8771
9743
|
};
|
|
8772
9744
|
}
|
|
8773
|
-
function resolvePdaAccount(meta, args, resolvedMap, programId) {
|
|
9745
|
+
function resolvePdaAccount(meta, args, resolvedMap, programId, resolve) {
|
|
8774
9746
|
if (!meta.pdaConfig) {
|
|
8775
9747
|
return null;
|
|
8776
9748
|
}
|
|
@@ -8787,13 +9759,16 @@ function resolvePdaAccount(meta, args, resolvedMap, programId) {
|
|
|
8787
9759
|
case 'literal':
|
|
8788
9760
|
seeds.push(createSeed(seed.value));
|
|
8789
9761
|
break;
|
|
9762
|
+
case 'bytes':
|
|
9763
|
+
seeds.push(Uint8Array.from(seed.value));
|
|
9764
|
+
break;
|
|
8790
9765
|
case 'argRef': {
|
|
8791
|
-
const argValue = args
|
|
9766
|
+
const argValue = getValueByPath(args, seed.argName) ?? getValueByPath(resolve, seed.argName);
|
|
8792
9767
|
if (argValue === undefined) {
|
|
8793
9768
|
throw new Error('PDA seed references missing argument: ' + seed.argName +
|
|
8794
9769
|
' (for account "' + meta.name + '")');
|
|
8795
9770
|
}
|
|
8796
|
-
seeds.push(
|
|
9771
|
+
seeds.push(serializeSeedValue(argValue, seed.argType));
|
|
8797
9772
|
break;
|
|
8798
9773
|
}
|
|
8799
9774
|
case 'accountRef': {
|
|
@@ -8861,6 +9836,11 @@ function serializeInstructionData(discriminator, args, schema) {
|
|
|
8861
9836
|
const buffers = [Buffer.from(discriminator)];
|
|
8862
9837
|
for (const field of schema) {
|
|
8863
9838
|
const value = args[field.name];
|
|
9839
|
+
// Option fields treat undefined as None; everything else must be present
|
|
9840
|
+
// (silently encoding zeros for a missing arg corrupts instruction data).
|
|
9841
|
+
if (value === undefined && !(typeof field.type === 'object' && 'option' in field.type)) {
|
|
9842
|
+
throw new Error(`Missing required argument "${field.name}" (type ${JSON.stringify(field.type)})`);
|
|
9843
|
+
}
|
|
8864
9844
|
const serialized = serializeValue(value, field.type);
|
|
8865
9845
|
buffers.push(serialized);
|
|
8866
9846
|
}
|
|
@@ -8879,8 +9859,108 @@ function serializeValue(value, type) {
|
|
|
8879
9859
|
if ('array' in type) {
|
|
8880
9860
|
return serializeArray(value, type.array[0], type.array[1]);
|
|
8881
9861
|
}
|
|
9862
|
+
if ('hashMap' in type) {
|
|
9863
|
+
return serializeHashMap(value, type.hashMap[0], type.hashMap[1]);
|
|
9864
|
+
}
|
|
9865
|
+
if ('struct' in type) {
|
|
9866
|
+
return serializeStruct(value, type.struct);
|
|
9867
|
+
}
|
|
9868
|
+
if ('enum' in type) {
|
|
9869
|
+
return serializeEnum(value, type.enum);
|
|
9870
|
+
}
|
|
8882
9871
|
throw new Error(`Unknown type: ${JSON.stringify(type)}`);
|
|
8883
9872
|
}
|
|
9873
|
+
function serializeStruct(value, fields) {
|
|
9874
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
9875
|
+
throw new Error(`Struct value must be a plain object, got ${typeof value}`);
|
|
9876
|
+
}
|
|
9877
|
+
const obj = value;
|
|
9878
|
+
const buffers = [];
|
|
9879
|
+
for (const field of fields) {
|
|
9880
|
+
const fieldValue = obj[field.name];
|
|
9881
|
+
if (fieldValue === undefined &&
|
|
9882
|
+
!(typeof field.type === 'object' && 'option' in field.type)) {
|
|
9883
|
+
throw new Error(`Missing required struct field "${field.name}"`);
|
|
9884
|
+
}
|
|
9885
|
+
buffers.push(serializeValue(fieldValue, field.type));
|
|
9886
|
+
}
|
|
9887
|
+
return Buffer.concat(buffers);
|
|
9888
|
+
}
|
|
9889
|
+
function serializeHashMap(value, keyType, valueType) {
|
|
9890
|
+
if (keyType !== 'string') {
|
|
9891
|
+
throw new Error(`Instruction hashMap keys must use the 'string' schema, got ${JSON.stringify(keyType)}`);
|
|
9892
|
+
}
|
|
9893
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
9894
|
+
throw new Error(`HashMap value must be a plain object, got ${typeof value}`);
|
|
9895
|
+
}
|
|
9896
|
+
const entries = Object.entries(value).sort(([left], [right]) =>
|
|
9897
|
+
// Rust/Borsh sorts String keys by their UTF-8 bytes before serializing.
|
|
9898
|
+
Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8')));
|
|
9899
|
+
const len = Buffer.alloc(4);
|
|
9900
|
+
len.writeUInt32LE(entries.length, 0);
|
|
9901
|
+
const entryBuffers = [];
|
|
9902
|
+
for (const [key, entryValue] of entries) {
|
|
9903
|
+
entryBuffers.push(serializePrimitive(key, 'string'));
|
|
9904
|
+
entryBuffers.push(serializeValue(entryValue, valueType));
|
|
9905
|
+
}
|
|
9906
|
+
return Buffer.concat([len, ...entryBuffers]);
|
|
9907
|
+
}
|
|
9908
|
+
function variantName(variant) {
|
|
9909
|
+
return typeof variant === 'string' ? variant : variant.name;
|
|
9910
|
+
}
|
|
9911
|
+
function serializeEnum(value, variants) {
|
|
9912
|
+
// Numeric value: a bare variant index (fieldless variants only).
|
|
9913
|
+
if (typeof value === 'number') {
|
|
9914
|
+
if (!Number.isInteger(value) || value < 0 || value >= variants.length) {
|
|
9915
|
+
throw new Error(`Enum variant index ${value} out of range (0..${variants.length - 1})`);
|
|
9916
|
+
}
|
|
9917
|
+
const variant = variants[value];
|
|
9918
|
+
if (typeof variant !== 'string') {
|
|
9919
|
+
throw new Error(`Enum variant "${variantName(variant)}" carries data; pass { ${variantName(variant)}: ... } instead of an index`);
|
|
9920
|
+
}
|
|
9921
|
+
return Buffer.from([value]);
|
|
9922
|
+
}
|
|
9923
|
+
// String value: a fieldless variant by name.
|
|
9924
|
+
if (typeof value === 'string') {
|
|
9925
|
+
const index = variants.findIndex((v) => variantName(v) === value);
|
|
9926
|
+
if (index === -1) {
|
|
9927
|
+
throw new Error(`Unknown enum variant "${value}". Expected one of: ${variants.map(variantName).join(', ')}`);
|
|
9928
|
+
}
|
|
9929
|
+
const variant = variants[index];
|
|
9930
|
+
if (typeof variant !== 'string') {
|
|
9931
|
+
throw new Error(`Enum variant "${value}" carries data; pass { ${value}: ... } instead of a bare name`);
|
|
9932
|
+
}
|
|
9933
|
+
return Buffer.from([index]);
|
|
9934
|
+
}
|
|
9935
|
+
// Object value: { variantName: payload } for data-carrying variants.
|
|
9936
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
9937
|
+
const keys = Object.keys(value);
|
|
9938
|
+
if (keys.length !== 1) {
|
|
9939
|
+
throw new Error(`Enum value must be a single-key object ({ variantName: payload }), got keys [${keys.join(', ')}]`);
|
|
9940
|
+
}
|
|
9941
|
+
const key = keys[0];
|
|
9942
|
+
const payload = value[key];
|
|
9943
|
+
const index = variants.findIndex((v) => variantName(v) === key);
|
|
9944
|
+
if (index === -1) {
|
|
9945
|
+
throw new Error(`Unknown enum variant "${key}". Expected one of: ${variants.map(variantName).join(', ')}`);
|
|
9946
|
+
}
|
|
9947
|
+
const variant = variants[index];
|
|
9948
|
+
if (typeof variant === 'string') {
|
|
9949
|
+
throw new Error(`Enum variant "${key}" is fieldless; pass '${key}' instead of an object`);
|
|
9950
|
+
}
|
|
9951
|
+
const prefix = Buffer.from([index]);
|
|
9952
|
+
if ('fields' in variant) {
|
|
9953
|
+
return Buffer.concat([prefix, serializeStruct(payload, variant.fields)]);
|
|
9954
|
+
}
|
|
9955
|
+
const tuple = payload;
|
|
9956
|
+
if (!Array.isArray(tuple) || tuple.length !== variant.tuple.length) {
|
|
9957
|
+
throw new Error(`Enum variant "${key}" expects a tuple of length ${variant.tuple.length}`);
|
|
9958
|
+
}
|
|
9959
|
+
const elements = variant.tuple.map((elementType, i) => serializeValue(tuple[i], elementType));
|
|
9960
|
+
return Buffer.concat([prefix, ...elements]);
|
|
9961
|
+
}
|
|
9962
|
+
throw new Error(`Cannot serialize enum from value of type ${typeof value}`);
|
|
9963
|
+
}
|
|
8884
9964
|
function serializePrimitive(value, type) {
|
|
8885
9965
|
switch (type) {
|
|
8886
9966
|
case 'u8':
|
|
@@ -8921,21 +10001,60 @@ function serializePrimitive(value, type) {
|
|
|
8921
10001
|
case 'i128':
|
|
8922
10002
|
const i128 = Buffer.alloc(16);
|
|
8923
10003
|
const bigI128 = BigInt(value);
|
|
8924
|
-
|
|
10004
|
+
// The masked low limb is always non-negative, so it must be written
|
|
10005
|
+
// unsigned; the arithmetic-shifted high limb carries the sign.
|
|
10006
|
+
i128.writeBigUInt64LE(bigI128 & BigInt('0xFFFFFFFFFFFFFFFF'), 0);
|
|
8925
10007
|
i128.writeBigInt64LE(bigI128 >> BigInt(64), 8);
|
|
8926
10008
|
return i128;
|
|
10009
|
+
case 'f32': {
|
|
10010
|
+
const f32 = Buffer.alloc(4);
|
|
10011
|
+
f32.writeFloatLE(value, 0);
|
|
10012
|
+
return f32;
|
|
10013
|
+
}
|
|
10014
|
+
case 'f64': {
|
|
10015
|
+
const f64 = Buffer.alloc(8);
|
|
10016
|
+
f64.writeDoubleLE(value, 0);
|
|
10017
|
+
return f64;
|
|
10018
|
+
}
|
|
8927
10019
|
case 'bool':
|
|
8928
10020
|
return Buffer.from([value ? 1 : 0]);
|
|
10021
|
+
case 'bytes': {
|
|
10022
|
+
// Borsh bytes: u32 LE length prefix + raw bytes.
|
|
10023
|
+
const raw = value instanceof Uint8Array
|
|
10024
|
+
? value
|
|
10025
|
+
: Array.isArray(value)
|
|
10026
|
+
? Uint8Array.from(value)
|
|
10027
|
+
: null;
|
|
10028
|
+
if (raw === null) {
|
|
10029
|
+
throw new Error(`Cannot serialize bytes from value of type ${typeof value}`);
|
|
10030
|
+
}
|
|
10031
|
+
const lenPrefix = Buffer.alloc(4);
|
|
10032
|
+
lenPrefix.writeUInt32LE(raw.length, 0);
|
|
10033
|
+
return Buffer.concat([lenPrefix, Buffer.from(raw)]);
|
|
10034
|
+
}
|
|
8929
10035
|
case 'string':
|
|
8930
10036
|
const str = value;
|
|
8931
10037
|
const strBytes = Buffer.from(str, 'utf-8');
|
|
8932
10038
|
const strLen = Buffer.alloc(4);
|
|
8933
10039
|
strLen.writeUInt32LE(strBytes.length, 0);
|
|
8934
10040
|
return Buffer.concat([strLen, strBytes]);
|
|
8935
|
-
case 'pubkey':
|
|
8936
|
-
// Public key is 32 bytes
|
|
8937
|
-
|
|
8938
|
-
|
|
10041
|
+
case 'pubkey': {
|
|
10042
|
+
// Public key is 32 bytes. Accept base58 strings or raw 32-byte buffers.
|
|
10043
|
+
if (value instanceof Uint8Array) {
|
|
10044
|
+
if (value.length !== 32) {
|
|
10045
|
+
throw new Error(`Invalid pubkey byte length: expected 32, got ${value.length}`);
|
|
10046
|
+
}
|
|
10047
|
+
return Buffer.from(value);
|
|
10048
|
+
}
|
|
10049
|
+
if (typeof value === 'string') {
|
|
10050
|
+
const decoded = decodeBase58(value);
|
|
10051
|
+
if (decoded.length !== 32) {
|
|
10052
|
+
throw new Error(`Invalid pubkey: '${value}' decoded to ${decoded.length} bytes, expected 32`);
|
|
10053
|
+
}
|
|
10054
|
+
return Buffer.from(decoded);
|
|
10055
|
+
}
|
|
10056
|
+
throw new Error(`Cannot serialize pubkey from value of type ${typeof value}`);
|
|
10057
|
+
}
|
|
8939
10058
|
default:
|
|
8940
10059
|
throw new Error(`Unknown primitive type: ${type}`);
|
|
8941
10060
|
}
|
|
@@ -8962,59 +10081,7 @@ function serializeArray(values, elementType, length) {
|
|
|
8962
10081
|
}
|
|
8963
10082
|
|
|
8964
10083
|
/**
|
|
8965
|
-
*
|
|
8966
|
-
*
|
|
8967
|
-
* @param signature - Transaction signature
|
|
8968
|
-
* @param level - Desired confirmation level
|
|
8969
|
-
* @param timeout - Maximum wait time in milliseconds
|
|
8970
|
-
* @returns Confirmation result
|
|
8971
|
-
*/
|
|
8972
|
-
async function waitForConfirmation(signature, level = 'confirmed', timeout = 60000) {
|
|
8973
|
-
const startTime = Date.now();
|
|
8974
|
-
while (Date.now() - startTime < timeout) {
|
|
8975
|
-
const status = await checkTransactionStatus();
|
|
8976
|
-
if (status.err) {
|
|
8977
|
-
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
|
|
8978
|
-
}
|
|
8979
|
-
if (isConfirmationLevelSufficient(status.confirmations, level)) {
|
|
8980
|
-
return {
|
|
8981
|
-
level,
|
|
8982
|
-
slot: status.slot,
|
|
8983
|
-
};
|
|
8984
|
-
}
|
|
8985
|
-
await sleep(1000);
|
|
8986
|
-
}
|
|
8987
|
-
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
|
|
8988
|
-
}
|
|
8989
|
-
async function checkTransactionStatus(_signature) {
|
|
8990
|
-
// In production, query the Solana RPC
|
|
8991
|
-
return {
|
|
8992
|
-
err: null,
|
|
8993
|
-
confirmations: 32,
|
|
8994
|
-
slot: 123456789,
|
|
8995
|
-
};
|
|
8996
|
-
}
|
|
8997
|
-
function isConfirmationLevelSufficient(confirmations, level) {
|
|
8998
|
-
if (confirmations === null) {
|
|
8999
|
-
return false;
|
|
9000
|
-
}
|
|
9001
|
-
switch (level) {
|
|
9002
|
-
case 'processed':
|
|
9003
|
-
return confirmations >= 0;
|
|
9004
|
-
case 'confirmed':
|
|
9005
|
-
return confirmations >= 1;
|
|
9006
|
-
case 'finalized':
|
|
9007
|
-
return confirmations >= 32;
|
|
9008
|
-
default:
|
|
9009
|
-
return false;
|
|
9010
|
-
}
|
|
9011
|
-
}
|
|
9012
|
-
function sleep(ms) {
|
|
9013
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9014
|
-
}
|
|
9015
|
-
|
|
9016
|
-
/**
|
|
9017
|
-
* Parses and handles instruction errors.
|
|
10084
|
+
* Parses and handles instruction errors.
|
|
9018
10085
|
*/
|
|
9019
10086
|
/**
|
|
9020
10087
|
* Parses an error returned from a Solana transaction.
|
|
@@ -9072,103 +10139,369 @@ function extractErrorCode(error) {
|
|
|
9072
10139
|
function formatProgramError(error) {
|
|
9073
10140
|
return `${error.name} (${error.code}): ${error.message}`;
|
|
9074
10141
|
}
|
|
9075
|
-
|
|
9076
10142
|
/**
|
|
9077
|
-
*
|
|
10143
|
+
* Error thrown when an instruction fails to send and the underlying failure
|
|
10144
|
+
* could be parsed against the handler's IDL error definitions.
|
|
9078
10145
|
*/
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
10146
|
+
class InstructionError extends Error {
|
|
10147
|
+
constructor(message, programError, cause) {
|
|
10148
|
+
super(message);
|
|
10149
|
+
this.name = 'InstructionError';
|
|
10150
|
+
this.programError = programError;
|
|
10151
|
+
this.cause = cause;
|
|
9083
10152
|
}
|
|
9084
|
-
|
|
10153
|
+
}
|
|
10154
|
+
|
|
10155
|
+
/**
|
|
10156
|
+
* Creates a data-driven instruction handler.
|
|
10157
|
+
*
|
|
10158
|
+
* The returned handler implements `build()` generically: it serializes args
|
|
10159
|
+
* via the schema-driven serializer and constructs the account key list from
|
|
10160
|
+
* the resolved, ordered accounts. No imperative per-instruction code is
|
|
10161
|
+
* required, which keeps generated SDKs tiny and puts all serialization logic
|
|
10162
|
+
* in one tested place.
|
|
10163
|
+
*/
|
|
10164
|
+
function createInstructionHandler(config) {
|
|
10165
|
+
const discriminator = config.discriminator instanceof Uint8Array
|
|
10166
|
+
? config.discriminator
|
|
10167
|
+
: Uint8Array.from(config.discriminator);
|
|
10168
|
+
const argNames = config.args.map((a) => a.name);
|
|
10169
|
+
const errors = config.errors ?? [];
|
|
10170
|
+
return {
|
|
10171
|
+
programId: config.programId,
|
|
10172
|
+
accounts: config.accounts,
|
|
10173
|
+
errors,
|
|
10174
|
+
argNames,
|
|
10175
|
+
build(args, resolved) {
|
|
10176
|
+
const data = serializeInstructionData(discriminator, args, config.args);
|
|
10177
|
+
return {
|
|
10178
|
+
programId: config.programId,
|
|
10179
|
+
keys: resolved.map((r) => ({
|
|
10180
|
+
pubkey: r.address,
|
|
10181
|
+
isSigner: r.isSigner,
|
|
10182
|
+
isWritable: r.isWritable,
|
|
10183
|
+
})),
|
|
10184
|
+
data,
|
|
10185
|
+
};
|
|
10186
|
+
},
|
|
10187
|
+
};
|
|
9085
10188
|
}
|
|
9086
10189
|
/**
|
|
9087
|
-
*
|
|
10190
|
+
* Splits a merged params object into serialized args and account overrides.
|
|
9088
10191
|
*
|
|
9089
|
-
*
|
|
9090
|
-
*
|
|
9091
|
-
*
|
|
9092
|
-
*
|
|
9093
|
-
*
|
|
10192
|
+
* Keys matching a declared argument name are args; keys matching a declared
|
|
10193
|
+
* account name (with a string value) are account address overrides. This
|
|
10194
|
+
* applies to signer slots too, allowing explicit signer addresses to override
|
|
10195
|
+
* the wallet fallback. Anything else throws — a typo'd key silently dropped
|
|
10196
|
+
* here would otherwise change the built instruction. `options.accounts` on
|
|
10197
|
+
* {@link BuildOptions} remains an unvalidated escape hatch for advanced callers.
|
|
10198
|
+
*/
|
|
10199
|
+
function splitParams(handler, params) {
|
|
10200
|
+
const argNameSet = new Set(handler.argNames);
|
|
10201
|
+
const accountNameSet = new Set(handler.accounts.map((a) => a.name));
|
|
10202
|
+
const args = {};
|
|
10203
|
+
const accountOverrides = {};
|
|
10204
|
+
let resolve;
|
|
10205
|
+
for (const [key, value] of Object.entries(params)) {
|
|
10206
|
+
if (argNameSet.has(key)) {
|
|
10207
|
+
args[key] = value;
|
|
10208
|
+
}
|
|
10209
|
+
else if (key === 'resolve' && !accountNameSet.has(key)) {
|
|
10210
|
+
if (value === undefined) {
|
|
10211
|
+
continue;
|
|
10212
|
+
}
|
|
10213
|
+
if (value === null || Array.isArray(value) || typeof value !== 'object') {
|
|
10214
|
+
throw new Error('Parameter "resolve" must be an object when provided');
|
|
10215
|
+
}
|
|
10216
|
+
resolve = value;
|
|
10217
|
+
}
|
|
10218
|
+
else if (accountNameSet.has(key)) {
|
|
10219
|
+
if (typeof value !== 'string') {
|
|
10220
|
+
// Non-string values are not valid account addresses.
|
|
10221
|
+
throw new Error(`Parameter "${key}" is not a known argument and is not a base58 account address`);
|
|
10222
|
+
}
|
|
10223
|
+
accountOverrides[key] = value;
|
|
10224
|
+
}
|
|
10225
|
+
else {
|
|
10226
|
+
throw new Error(`Unknown parameter "${key}". Expected one of args [${[...argNameSet].join(', ')}] ` +
|
|
10227
|
+
`or accounts [${[...accountNameSet].join(', ')}]`);
|
|
10228
|
+
}
|
|
10229
|
+
}
|
|
10230
|
+
return { args, accountOverrides, resolve };
|
|
10231
|
+
}
|
|
10232
|
+
/**
|
|
10233
|
+
* Builds a {@link BuiltInstruction} from a handler and a merged params object.
|
|
9094
10234
|
*
|
|
9095
|
-
*
|
|
9096
|
-
*
|
|
9097
|
-
* @param options - Execution options
|
|
9098
|
-
* @returns Execution result with signature
|
|
10235
|
+
* This is a pure function: it performs no network access. It is the unit of
|
|
10236
|
+
* composition for batching (`wallet.signAndSend([a, b, c])`).
|
|
9099
10237
|
*/
|
|
9100
|
-
|
|
9101
|
-
|
|
10238
|
+
function buildInstruction(handler, params, options = {}) {
|
|
10239
|
+
const { args, accountOverrides, resolve } = splitParams(handler, params);
|
|
9102
10240
|
const resolutionOptions = {
|
|
9103
|
-
accounts: options.accounts,
|
|
10241
|
+
accounts: { ...accountOverrides, ...options.accounts },
|
|
10242
|
+
resolve,
|
|
9104
10243
|
wallet: options.wallet,
|
|
9105
|
-
programId: handler.programId,
|
|
10244
|
+
programId: handler.programId,
|
|
9106
10245
|
};
|
|
9107
10246
|
const resolution = resolveAccounts(handler.accounts, args, resolutionOptions);
|
|
9108
10247
|
validateAccountResolution(resolution);
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
// Step 3: Build transaction from the built instruction
|
|
9113
|
-
const transaction = buildTransaction(instruction);
|
|
9114
|
-
// Step 4: Sign and send
|
|
9115
|
-
if (!options.wallet) {
|
|
9116
|
-
throw new Error('Wallet required to sign transaction');
|
|
10248
|
+
const instruction = handler.build(args, resolution.accounts);
|
|
10249
|
+
if (options.remainingAccounts?.length) {
|
|
10250
|
+
instruction.keys.push(...options.remainingAccounts);
|
|
9117
10251
|
}
|
|
9118
|
-
|
|
9119
|
-
// Step 5: Wait for confirmation
|
|
9120
|
-
const confirmationLevel = options.confirmationLevel ?? 'confirmed';
|
|
9121
|
-
const timeout = options.timeout ?? 60000;
|
|
9122
|
-
const confirmation = await waitForConfirmation(signature, confirmationLevel, timeout);
|
|
9123
|
-
return {
|
|
9124
|
-
signature,
|
|
9125
|
-
confirmationLevel: confirmation.level,
|
|
9126
|
-
slot: confirmation.slot,
|
|
9127
|
-
};
|
|
10252
|
+
return instruction;
|
|
9128
10253
|
}
|
|
9129
10254
|
/**
|
|
9130
|
-
*
|
|
10255
|
+
* Builds, signs, and sends an instruction via the wallet adapter.
|
|
9131
10256
|
*
|
|
9132
|
-
*
|
|
9133
|
-
*
|
|
10257
|
+
* The core SDK does not touch RPC: the adapter owns blockhash, compilation,
|
|
10258
|
+
* signing, sending, and confirmation. On failure, program errors are parsed
|
|
10259
|
+
* against the handler's IDL error definitions and surfaced as an
|
|
10260
|
+
* {@link InstructionError}.
|
|
9134
10261
|
*/
|
|
9135
|
-
function
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
keys: instruction.keys,
|
|
9143
|
-
data: Array.from(instruction.data),
|
|
9144
|
-
}],
|
|
10262
|
+
async function executeInstruction(handler, params, options = {}) {
|
|
10263
|
+
const instruction = buildInstruction(handler, params, options);
|
|
10264
|
+
if (!options.wallet) {
|
|
10265
|
+
throw new Error('Wallet required to sign and send transaction');
|
|
10266
|
+
}
|
|
10267
|
+
const sendOptions = {
|
|
10268
|
+
...options.send,
|
|
9145
10269
|
};
|
|
10270
|
+
if (options.confirmationLevel !== undefined) {
|
|
10271
|
+
sendOptions.confirmationLevel = options.confirmationLevel;
|
|
10272
|
+
}
|
|
10273
|
+
try {
|
|
10274
|
+
const result = await options.wallet.signAndSend([instruction], sendOptions);
|
|
10275
|
+
return { signature: result.signature, slot: result.slot };
|
|
10276
|
+
}
|
|
10277
|
+
catch (err) {
|
|
10278
|
+
const programError = parseInstructionError(err, handler.errors);
|
|
10279
|
+
if (programError) {
|
|
10280
|
+
throw new InstructionError(`${programError.name} (${programError.code}): ${programError.message}`, programError, err);
|
|
10281
|
+
}
|
|
10282
|
+
throw err;
|
|
10283
|
+
}
|
|
9146
10284
|
}
|
|
9147
10285
|
/**
|
|
9148
10286
|
* Creates an instruction executor bound to a specific wallet.
|
|
9149
|
-
*
|
|
9150
|
-
* @param wallet - Wallet adapter
|
|
9151
|
-
* @returns Bound executor function
|
|
9152
10287
|
*/
|
|
9153
10288
|
function createInstructionExecutor(wallet) {
|
|
9154
10289
|
return {
|
|
9155
|
-
execute: async (handler,
|
|
9156
|
-
return executeInstruction(handler,
|
|
9157
|
-
...options,
|
|
9158
|
-
wallet,
|
|
9159
|
-
});
|
|
10290
|
+
execute: async (handler, params, options) => {
|
|
10291
|
+
return executeInstruction(handler, params, { ...options, wallet });
|
|
9160
10292
|
},
|
|
10293
|
+
build: (handler, params, options) => {
|
|
10294
|
+
return buildInstruction(handler, params, { ...options, wallet });
|
|
10295
|
+
},
|
|
10296
|
+
};
|
|
10297
|
+
}
|
|
10298
|
+
|
|
10299
|
+
const STACK_RUNTIME_EXTENSIONS = '__areteStackRuntimeExtensions';
|
|
10300
|
+
const PROGRAM_OPERATION_EXTENSIONS = '__areteProgramOperationExtensions';
|
|
10301
|
+
function getProgramRuntimeExtensions(program) {
|
|
10302
|
+
return program[PROGRAM_OPERATION_EXTENSIONS];
|
|
10303
|
+
}
|
|
10304
|
+
function getStackRuntimeExtensions(stack) {
|
|
10305
|
+
return stack[STACK_RUNTIME_EXTENSIONS];
|
|
10306
|
+
}
|
|
10307
|
+
function defineClientField(target, key, value) {
|
|
10308
|
+
if (value === undefined || key in target) {
|
|
10309
|
+
return;
|
|
10310
|
+
}
|
|
10311
|
+
Object.defineProperty(target, key, {
|
|
10312
|
+
value,
|
|
10313
|
+
enumerable: true,
|
|
10314
|
+
configurable: true,
|
|
10315
|
+
writable: false,
|
|
10316
|
+
});
|
|
10317
|
+
}
|
|
10318
|
+
function applyConnectedStackExtensions(client, stack) {
|
|
10319
|
+
const extendedStack = stack;
|
|
10320
|
+
defineClientField(client, 'addresses', extendedStack.addresses);
|
|
10321
|
+
defineClientField(client, 'constants', extendedStack.constants);
|
|
10322
|
+
defineClientField(client, 'defaults', extendedStack.defaults);
|
|
10323
|
+
defineClientField(client, 'math', extendedStack.math);
|
|
10324
|
+
const runtime = getStackRuntimeExtensions(stack);
|
|
10325
|
+
defineClientField(client, 'flows', runtime?.createFlows?.(client));
|
|
10326
|
+
defineClientField(client, 'read', runtime?.createRead?.(client));
|
|
10327
|
+
return client;
|
|
10328
|
+
}
|
|
10329
|
+
function nonEmpty(values, label) {
|
|
10330
|
+
if (values.length === 0) {
|
|
10331
|
+
throw new Error(`${label} must contain at least one item`);
|
|
10332
|
+
}
|
|
10333
|
+
return [...values];
|
|
10334
|
+
}
|
|
10335
|
+
function inferSignerAddress(value) {
|
|
10336
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
10337
|
+
return value;
|
|
10338
|
+
}
|
|
10339
|
+
if (!value || typeof value !== 'object') {
|
|
10340
|
+
return null;
|
|
10341
|
+
}
|
|
10342
|
+
const candidate = value;
|
|
10343
|
+
if (typeof candidate.address === 'string' && candidate.address.length > 0) {
|
|
10344
|
+
return candidate.address;
|
|
10345
|
+
}
|
|
10346
|
+
if (typeof candidate.publicKey === 'string' && candidate.publicKey.length > 0) {
|
|
10347
|
+
return candidate.publicKey;
|
|
10348
|
+
}
|
|
10349
|
+
const publicKey = candidate.publicKey;
|
|
10350
|
+
return typeof publicKey?.toBase58 === 'function' ? publicKey.toBase58() : null;
|
|
10351
|
+
}
|
|
10352
|
+
function validateTransactionSigners(transaction, host, options) {
|
|
10353
|
+
const signerAddresses = (options.signers ?? []).map(inferSignerAddress);
|
|
10354
|
+
const available = new Set(options.availableSignerAddresses ?? []);
|
|
10355
|
+
for (const address of options.signerRegistry?.addresses() ?? []) {
|
|
10356
|
+
available.add(address);
|
|
10357
|
+
}
|
|
10358
|
+
const wallet = options.wallet ?? host.wallet;
|
|
10359
|
+
for (const address of wallet?.signerAddresses ?? []) {
|
|
10360
|
+
available.add(address);
|
|
10361
|
+
}
|
|
10362
|
+
const walletAddress = wallet?.publicKey ?? host.publicKey;
|
|
10363
|
+
if (walletAddress) {
|
|
10364
|
+
available.add(walletAddress);
|
|
10365
|
+
}
|
|
10366
|
+
for (const address of signerAddresses) {
|
|
10367
|
+
if (address) {
|
|
10368
|
+
available.add(address);
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10371
|
+
const missing = transaction.requiredSignerAddresses.filter((address) => !available.has(address));
|
|
10372
|
+
if (missing.length > 0) {
|
|
10373
|
+
throw new Error(`Missing signer(s) for ${transaction.name}: ${missing.join(', ')}`);
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
10376
|
+
class OperationExecutionError extends Error {
|
|
10377
|
+
constructor(input) {
|
|
10378
|
+
super(`Operation '${input.operation.name}' failed at transaction ${input.failedTransactionIndex + 1} (${input.failedTransaction.name})`);
|
|
10379
|
+
this.name = 'OperationExecutionError';
|
|
10380
|
+
this.operation = input.operation;
|
|
10381
|
+
this.failedTransaction = input.failedTransaction;
|
|
10382
|
+
this.failedTransactionIndex = input.failedTransactionIndex;
|
|
10383
|
+
this.completedReceipts = [...input.completedReceipts];
|
|
10384
|
+
this.cause = input.cause;
|
|
10385
|
+
}
|
|
10386
|
+
}
|
|
10387
|
+
async function executePreparedOperation(host, operation, options = {}) {
|
|
10388
|
+
const receipts = [];
|
|
10389
|
+
const signers = [
|
|
10390
|
+
...new Set([
|
|
10391
|
+
...(options.signerRegistry?.values() ?? []),
|
|
10392
|
+
...(options.signers ?? []),
|
|
10393
|
+
]),
|
|
10394
|
+
];
|
|
10395
|
+
for (const [transactionIndex, transaction] of operation.plan.transactions.entries()) {
|
|
10396
|
+
try {
|
|
10397
|
+
validateTransactionSigners(transaction, host, options);
|
|
10398
|
+
await options.onTransactionStart?.({ operation, transaction, transactionIndex });
|
|
10399
|
+
const result = await host.transaction(transaction.instructions, {
|
|
10400
|
+
wallet: options.wallet,
|
|
10401
|
+
send: options.send,
|
|
10402
|
+
errors: [...transaction.errors],
|
|
10403
|
+
signers: signers.length > 0 ? signers : undefined,
|
|
10404
|
+
});
|
|
10405
|
+
const receipt = {
|
|
10406
|
+
transactionIndex,
|
|
10407
|
+
transactionName: transaction.name,
|
|
10408
|
+
signature: result.signature,
|
|
10409
|
+
slot: result.slot,
|
|
10410
|
+
};
|
|
10411
|
+
receipts.push(receipt);
|
|
10412
|
+
await options.onTransactionSuccess?.({
|
|
10413
|
+
operation,
|
|
10414
|
+
transaction,
|
|
10415
|
+
transactionIndex,
|
|
10416
|
+
receipt,
|
|
10417
|
+
});
|
|
10418
|
+
}
|
|
10419
|
+
catch (cause) {
|
|
10420
|
+
throw new OperationExecutionError({
|
|
10421
|
+
operation,
|
|
10422
|
+
failedTransaction: transaction,
|
|
10423
|
+
failedTransactionIndex: transactionIndex,
|
|
10424
|
+
completedReceipts: receipts,
|
|
10425
|
+
cause,
|
|
10426
|
+
});
|
|
10427
|
+
}
|
|
10428
|
+
}
|
|
10429
|
+
if (operation.kind === 'flow') {
|
|
10430
|
+
return {
|
|
10431
|
+
kind: 'flow',
|
|
10432
|
+
operationName: operation.name,
|
|
10433
|
+
artifacts: operation.artifacts,
|
|
10434
|
+
signatures: nonEmpty(receipts.map((receipt) => receipt.signature), `Operation '${operation.name}' signatures`),
|
|
10435
|
+
transactions: nonEmpty(receipts, `Operation '${operation.name}' receipts`),
|
|
10436
|
+
};
|
|
10437
|
+
}
|
|
10438
|
+
return {
|
|
10439
|
+
kind: operation.kind,
|
|
10440
|
+
operationName: operation.name,
|
|
10441
|
+
artifacts: operation.artifacts,
|
|
10442
|
+
signatures: [receipts[0].signature],
|
|
10443
|
+
transaction: receipts[0],
|
|
9161
10444
|
};
|
|
9162
10445
|
}
|
|
9163
10446
|
|
|
9164
|
-
|
|
9165
|
-
|
|
10447
|
+
function mergeAttachedPrograms(stack, attachedPrograms) {
|
|
10448
|
+
const merged = { ...(attachedPrograms ?? {}) };
|
|
10449
|
+
for (const [name, definition] of Object.entries(stack.programs ?? {})) {
|
|
10450
|
+
if (name in merged) {
|
|
10451
|
+
console.warn(`Ignoring attached program '${name}' for stack '${stack.name}' because the stack already defines that key`);
|
|
10452
|
+
}
|
|
10453
|
+
merged[name] = definition;
|
|
10454
|
+
}
|
|
10455
|
+
return merged;
|
|
10456
|
+
}
|
|
10457
|
+
function normalizeProgramAccountWireKeys(value) {
|
|
10458
|
+
if (Array.isArray(value)) {
|
|
10459
|
+
return value.map(normalizeProgramAccountWireKeys);
|
|
10460
|
+
}
|
|
10461
|
+
if (!value || typeof value !== 'object') {
|
|
10462
|
+
return value;
|
|
10463
|
+
}
|
|
10464
|
+
return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
|
|
10465
|
+
key.replace(/[A-Z]/g, (letter, index) => `${index === 0 ? '' : '_'}${letter.toLowerCase()}`),
|
|
10466
|
+
normalizeProgramAccountWireKeys(nestedValue),
|
|
10467
|
+
]));
|
|
10468
|
+
}
|
|
10469
|
+
function parseProgramAccountValue(definition, value) {
|
|
10470
|
+
const schema = definition.schema;
|
|
10471
|
+
if (!schema) {
|
|
10472
|
+
return value;
|
|
10473
|
+
}
|
|
10474
|
+
const parsed = schema.safeParse(value);
|
|
10475
|
+
if (parsed.success) {
|
|
10476
|
+
return parsed.data;
|
|
10477
|
+
}
|
|
10478
|
+
const normalized = schema.safeParse(normalizeProgramAccountWireKeys(value));
|
|
10479
|
+
if (normalized.success) {
|
|
10480
|
+
return normalized.data;
|
|
10481
|
+
}
|
|
10482
|
+
throw new Error(`Program account read '${definition.account}' failed schema validation`);
|
|
10483
|
+
}
|
|
10484
|
+
function cloneStackWithPrograms(stack, programs) {
|
|
10485
|
+
const cloned = Object.create(Object.getPrototypeOf(stack), Object.getOwnPropertyDescriptors(stack));
|
|
10486
|
+
cloned.programs = programs;
|
|
10487
|
+
return cloned;
|
|
10488
|
+
}
|
|
10489
|
+
function withPrograms(stack, attachedPrograms) {
|
|
10490
|
+
return cloneStackWithPrograms(stack, mergeAttachedPrograms(stack, attachedPrograms));
|
|
10491
|
+
}
|
|
10492
|
+
let Arete$1 = class Arete {
|
|
10493
|
+
constructor(url, httpBaseUrl, options) {
|
|
9166
10494
|
this.stack = options.stack;
|
|
10495
|
+
this._wallet = options.wallet;
|
|
10496
|
+
this.executionDefaults = options.execution;
|
|
10497
|
+
this.httpBaseUrl = httpBaseUrl;
|
|
10498
|
+
this.fetchImpl = options.fetch ?? this.resolveFetchImpl();
|
|
9167
10499
|
this.storage = new SortedStorageDecorator(options.storage ?? new MemoryAdapter());
|
|
9168
10500
|
this.processor = new FrameProcessor(this.storage, {
|
|
9169
10501
|
maxEntriesPerView: options.maxEntriesPerView,
|
|
9170
10502
|
flushIntervalMs: options.flushIntervalMs,
|
|
9171
|
-
schemas:
|
|
10503
|
+
schemas: this.stack.schemas,
|
|
10504
|
+
patchSchemas: this.stack.patchSchemas,
|
|
9172
10505
|
});
|
|
9173
10506
|
this.connection = new ConnectionManager({
|
|
9174
10507
|
websocketUrl: url,
|
|
@@ -9181,26 +10514,178 @@ class Arete {
|
|
|
9181
10514
|
this.processor.handleFrame(frame);
|
|
9182
10515
|
});
|
|
9183
10516
|
this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
|
|
9184
|
-
this.
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
10517
|
+
this._queries = this.buildQueries();
|
|
10518
|
+
this._chain = createChainClient(this.httpBaseUrl, this.authenticatedFetch.bind(this));
|
|
10519
|
+
this._programs = this.buildPrograms();
|
|
10520
|
+
}
|
|
10521
|
+
resolveFetchImpl() {
|
|
10522
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
10523
|
+
throw new AreteError('A fetch implementation is required for HTTP point reads (provide ConnectOptions.fetch or use an environment with global fetch)', 'INVALID_CONFIG');
|
|
10524
|
+
}
|
|
10525
|
+
return globalThis.fetch.bind(globalThis);
|
|
10526
|
+
}
|
|
10527
|
+
buildQueries() {
|
|
10528
|
+
const queries = {};
|
|
10529
|
+
for (const [name, definition] of Object.entries(this.stack.queries ?? {})) {
|
|
10530
|
+
queries[name] = this.createQueryExecutor(definition);
|
|
10531
|
+
}
|
|
10532
|
+
return queries;
|
|
10533
|
+
}
|
|
10534
|
+
buildPrograms() {
|
|
10535
|
+
const bases = {};
|
|
10536
|
+
for (const [name, definition] of Object.entries(this.stack.programs ?? {})) {
|
|
10537
|
+
const instructions = {};
|
|
10538
|
+
for (const [instructionName, handler] of Object.entries(definition.rawInstructions ?? {})) {
|
|
10539
|
+
instructions[instructionName] = this.createTypedInstruction(handler);
|
|
9193
10540
|
}
|
|
10541
|
+
const accounts = {};
|
|
10542
|
+
for (const [accountName, accountDefinition] of Object.entries(definition.accounts ?? {})) {
|
|
10543
|
+
accounts[accountName] = this.createAccountReader(accountDefinition);
|
|
10544
|
+
}
|
|
10545
|
+
const queries = {};
|
|
10546
|
+
for (const [queryName, queryDefinition] of Object.entries(definition.queries ?? {})) {
|
|
10547
|
+
queries[queryName] = this.createQueryExecutor(queryDefinition);
|
|
10548
|
+
}
|
|
10549
|
+
bases[name] = {
|
|
10550
|
+
name: definition.name,
|
|
10551
|
+
programId: definition.programId,
|
|
10552
|
+
schemas: definition.schemas,
|
|
10553
|
+
pdas: definition.pdas ?? {},
|
|
10554
|
+
accounts,
|
|
10555
|
+
queries,
|
|
10556
|
+
raw: instructions,
|
|
10557
|
+
addresses: definition.addresses ?? {},
|
|
10558
|
+
constants: definition.constants ?? {},
|
|
10559
|
+
defaults: definition.defaults ?? {},
|
|
10560
|
+
math: definition.math ?? {},
|
|
10561
|
+
};
|
|
10562
|
+
}
|
|
10563
|
+
const programs = {};
|
|
10564
|
+
const client = this;
|
|
10565
|
+
for (const [name, definition] of Object.entries(this.stack.programs ?? {})) {
|
|
10566
|
+
const base = bases[name];
|
|
10567
|
+
const connectedProgram = {
|
|
10568
|
+
...base,
|
|
10569
|
+
instructions: {},
|
|
10570
|
+
transactions: {},
|
|
10571
|
+
flows: {},
|
|
10572
|
+
};
|
|
10573
|
+
const runtime = getProgramRuntimeExtensions(definition);
|
|
10574
|
+
const operations = runtime?.createOperations({
|
|
10575
|
+
chain: this._chain,
|
|
10576
|
+
get wallet() {
|
|
10577
|
+
return client._wallet;
|
|
10578
|
+
},
|
|
10579
|
+
program: connectedProgram,
|
|
10580
|
+
});
|
|
10581
|
+
connectedProgram.instructions = operations?.instructions ?? {};
|
|
10582
|
+
connectedProgram.transactions = operations?.transactions ?? {};
|
|
10583
|
+
connectedProgram.flows = operations?.flows ?? {};
|
|
10584
|
+
programs[name] = connectedProgram;
|
|
9194
10585
|
}
|
|
9195
|
-
return
|
|
10586
|
+
return programs;
|
|
9196
10587
|
}
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
10588
|
+
createTypedInstruction(handler) {
|
|
10589
|
+
return {
|
|
10590
|
+
build: (params, options) => buildInstruction(handler, params, this.withWallet(options)),
|
|
10591
|
+
};
|
|
10592
|
+
}
|
|
10593
|
+
createAccountReader(definition) {
|
|
10594
|
+
return {
|
|
10595
|
+
fetch: async (address) => {
|
|
10596
|
+
const result = await this.readJson(`${definition.path}/${encodeURIComponent(address)}`);
|
|
10597
|
+
return result === null ? null : parseProgramAccountValue(definition, result);
|
|
10598
|
+
},
|
|
10599
|
+
fetchMany: async (addresses) => {
|
|
10600
|
+
const result = await this.readJson(definition.path, {
|
|
10601
|
+
method: 'POST',
|
|
10602
|
+
body: { addresses },
|
|
10603
|
+
});
|
|
10604
|
+
return result.map((value) => value === null ? null : parseProgramAccountValue(definition, value));
|
|
10605
|
+
},
|
|
10606
|
+
exists: async (address) => {
|
|
10607
|
+
const result = await this.readJson(`${definition.path}/${encodeURIComponent(address)}/exists`);
|
|
10608
|
+
return result.exists;
|
|
10609
|
+
},
|
|
10610
|
+
};
|
|
10611
|
+
}
|
|
10612
|
+
createQueryExecutor(definition) {
|
|
10613
|
+
return async (params) => {
|
|
10614
|
+
const result = await this.readJson(definition.path, {
|
|
10615
|
+
method: definition.method ?? 'POST',
|
|
10616
|
+
body: params,
|
|
10617
|
+
});
|
|
10618
|
+
if (!definition.schema) {
|
|
10619
|
+
return result;
|
|
10620
|
+
}
|
|
10621
|
+
const parsed = definition.schema.safeParse(result);
|
|
10622
|
+
if (!parsed.success) {
|
|
10623
|
+
throw new Error(`Query '${definition.name}' failed schema validation`);
|
|
10624
|
+
}
|
|
10625
|
+
return parsed.data;
|
|
10626
|
+
};
|
|
10627
|
+
}
|
|
10628
|
+
async readJson(path, options) {
|
|
10629
|
+
const response = await this.authenticatedFetch(this.resolveReadUrl(path), {
|
|
10630
|
+
method: options?.method ?? 'GET',
|
|
10631
|
+
headers: options?.body === undefined ? undefined : { 'content-type': 'application/json' },
|
|
10632
|
+
body: options?.body === undefined ? undefined : JSON.stringify(options.body),
|
|
10633
|
+
});
|
|
10634
|
+
return parseReadResponse(response, path);
|
|
10635
|
+
}
|
|
10636
|
+
async authenticatedFetch(input, init) {
|
|
10637
|
+
const attempt = async (forceRefresh = false) => {
|
|
10638
|
+
const token = await this.connection.getHttpAuthToken(forceRefresh);
|
|
10639
|
+
const headers = new Headers(init?.headers ?? undefined);
|
|
10640
|
+
if (token) {
|
|
10641
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
10642
|
+
}
|
|
10643
|
+
return this.fetchImpl(input, {
|
|
10644
|
+
...init,
|
|
10645
|
+
headers,
|
|
10646
|
+
});
|
|
10647
|
+
};
|
|
10648
|
+
let response = await attempt(false);
|
|
10649
|
+
if (!response.ok) {
|
|
10650
|
+
const wireErrorCode = response.headers.get('X-Error-Code');
|
|
10651
|
+
const errorCode = wireErrorCode ? parseErrorCode(wireErrorCode) : undefined;
|
|
10652
|
+
if (errorCode && shouldRefreshToken(errorCode)) {
|
|
10653
|
+
this.connection.clearHttpAuthToken();
|
|
10654
|
+
response = await attempt(true);
|
|
10655
|
+
}
|
|
9201
10656
|
}
|
|
10657
|
+
return response;
|
|
10658
|
+
}
|
|
10659
|
+
resolveReadUrl(path) {
|
|
10660
|
+
return `${this.httpBaseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
|
|
10661
|
+
}
|
|
10662
|
+
/** Merge the client's default wallet into call options (call options win). */
|
|
10663
|
+
withWallet(options) {
|
|
10664
|
+
const merged = { ...(options ?? {}) };
|
|
10665
|
+
if (!merged.wallet && this._wallet) {
|
|
10666
|
+
merged.wallet = this._wallet;
|
|
10667
|
+
}
|
|
10668
|
+
return merged;
|
|
10669
|
+
}
|
|
10670
|
+
static async connect(stack, options) {
|
|
10671
|
+
const requestedUrl = options?.url ?? stack.endpoints.ws;
|
|
10672
|
+
const autoReconnect = options?.autoReconnect !== false;
|
|
10673
|
+
const httpOnly = options?.transport === 'http' || (!requestedUrl && !autoReconnect);
|
|
10674
|
+
const url = httpOnly ? null : requestedUrl;
|
|
10675
|
+
const httpUrl = options?.httpUrl
|
|
10676
|
+
?? stack.endpoints.http
|
|
10677
|
+
?? (requestedUrl ? deriveHttpEndpoint(requestedUrl) : undefined);
|
|
10678
|
+
if (!httpOnly && !url) {
|
|
10679
|
+
throw new AreteError('WebSocket URL is required (provide url option or define endpoints.ws in stack)', 'INVALID_CONFIG');
|
|
10680
|
+
}
|
|
10681
|
+
if (!httpUrl) {
|
|
10682
|
+
throw new AreteError('HTTP endpoint is required for transport: "http" (provide httpUrl option or define endpoints.http in stack)', 'INVALID_CONFIG');
|
|
10683
|
+
}
|
|
10684
|
+
const attachedPrograms = options?.programs;
|
|
10685
|
+
const effectiveStack = withPrograms(stack, attachedPrograms);
|
|
9202
10686
|
const internalOptions = {
|
|
9203
|
-
stack,
|
|
10687
|
+
stack: effectiveStack,
|
|
10688
|
+
httpUrl,
|
|
9204
10689
|
storage: options?.storage,
|
|
9205
10690
|
maxEntriesPerView: options?.maxEntriesPerView,
|
|
9206
10691
|
flushIntervalMs: options?.flushIntervalMs,
|
|
@@ -9209,18 +10694,113 @@ class Arete {
|
|
|
9209
10694
|
maxReconnectAttempts: options?.maxReconnectAttempts,
|
|
9210
10695
|
validateFrames: options?.validateFrames,
|
|
9211
10696
|
auth: options?.auth,
|
|
10697
|
+
wallet: options?.wallet,
|
|
10698
|
+
fetch: options?.fetch,
|
|
10699
|
+
execution: options?.execution,
|
|
9212
10700
|
};
|
|
9213
|
-
const client = new Arete(url, internalOptions);
|
|
9214
|
-
if (
|
|
10701
|
+
const client = new Arete(url, httpUrl, internalOptions);
|
|
10702
|
+
if (!httpOnly && autoReconnect) {
|
|
9215
10703
|
await client.connection.connect();
|
|
9216
10704
|
}
|
|
9217
|
-
return client;
|
|
10705
|
+
return applyConnectedStackExtensions(client, effectiveStack);
|
|
9218
10706
|
}
|
|
9219
10707
|
get views() {
|
|
9220
10708
|
return this._views;
|
|
9221
10709
|
}
|
|
9222
|
-
get
|
|
9223
|
-
return this.
|
|
10710
|
+
get queries() {
|
|
10711
|
+
return this._queries;
|
|
10712
|
+
}
|
|
10713
|
+
get programs() {
|
|
10714
|
+
return this._programs;
|
|
10715
|
+
}
|
|
10716
|
+
get chain() {
|
|
10717
|
+
return this._chain;
|
|
10718
|
+
}
|
|
10719
|
+
/** The default wallet adapter, if one was configured. */
|
|
10720
|
+
get wallet() {
|
|
10721
|
+
return this._wallet;
|
|
10722
|
+
}
|
|
10723
|
+
/** The connected wallet address, if a default wallet was configured. */
|
|
10724
|
+
get publicKey() {
|
|
10725
|
+
return this._wallet?.publicKey;
|
|
10726
|
+
}
|
|
10727
|
+
/**
|
|
10728
|
+
* Set (or clear) the default wallet adapter used for instruction execution.
|
|
10729
|
+
* Useful for connecting/disconnecting a wallet after the client is created.
|
|
10730
|
+
*/
|
|
10731
|
+
setWallet(wallet) {
|
|
10732
|
+
this._wallet = wallet;
|
|
10733
|
+
}
|
|
10734
|
+
/**
|
|
10735
|
+
* Sign and send a batch of pre-built instructions as a single transaction.
|
|
10736
|
+
*
|
|
10737
|
+
* Build instructions with `client.programs.<program>.raw.<name>.build(params)`
|
|
10738
|
+
* and compose them here. RPC/compilation/confirmation are owned by the adapter.
|
|
10739
|
+
*
|
|
10740
|
+
* On failure, the error is parsed against `options.errors` when given,
|
|
10741
|
+
* otherwise against error metadata aggregated from all the stack's handlers
|
|
10742
|
+
* (deduped by code, first-wins — if the stack bundles programs with
|
|
10743
|
+
* overlapping error codes, pass `options.errors` or use the per-instruction
|
|
10744
|
+
* call path for precise attribution).
|
|
10745
|
+
*/
|
|
10746
|
+
async transaction(instructions, options) {
|
|
10747
|
+
const wallet = options?.wallet ?? this._wallet;
|
|
10748
|
+
if (!wallet) {
|
|
10749
|
+
throw new Error('Wallet required to sign and send transaction');
|
|
10750
|
+
}
|
|
10751
|
+
try {
|
|
10752
|
+
const sendOptions = {
|
|
10753
|
+
...(options?.send ?? {}),
|
|
10754
|
+
};
|
|
10755
|
+
if (options?.signers !== undefined) {
|
|
10756
|
+
sendOptions.signers = options.signers;
|
|
10757
|
+
}
|
|
10758
|
+
const result = await wallet.signAndSend(instructions, sendOptions);
|
|
10759
|
+
return { signature: result.signature, slot: result.slot };
|
|
10760
|
+
}
|
|
10761
|
+
catch (err) {
|
|
10762
|
+
const programError = parseInstructionError(err, options?.errors ?? this.aggregateErrors());
|
|
10763
|
+
if (programError) {
|
|
10764
|
+
throw new InstructionError(`${programError.name} (${programError.code}): ${programError.message}`, programError, err);
|
|
10765
|
+
}
|
|
10766
|
+
throw err;
|
|
10767
|
+
}
|
|
10768
|
+
}
|
|
10769
|
+
execute(prepared, options) {
|
|
10770
|
+
const defaults = this.executionDefaults;
|
|
10771
|
+
return executePreparedOperation(this, prepared, {
|
|
10772
|
+
wallet: options?.wallet ?? defaults?.wallet,
|
|
10773
|
+
send: defaults?.send || options?.send
|
|
10774
|
+
? { ...(defaults?.send ?? {}), ...(options?.send ?? {}) }
|
|
10775
|
+
: undefined,
|
|
10776
|
+
signers: options?.signers ?? defaults?.signers,
|
|
10777
|
+
signerRegistry: options?.signerRegistry ?? defaults?.signerRegistry,
|
|
10778
|
+
availableSignerAddresses: options?.availableSignerAddresses ?? defaults?.availableSignerAddresses,
|
|
10779
|
+
onTransactionStart: options?.onTransactionStart ?? defaults?.onTransactionStart,
|
|
10780
|
+
onTransactionSuccess: options?.onTransactionSuccess ?? defaults?.onTransactionSuccess,
|
|
10781
|
+
});
|
|
10782
|
+
}
|
|
10783
|
+
/** Error metadata from every handler in the stack, deduped by code. */
|
|
10784
|
+
aggregateErrors() {
|
|
10785
|
+
if (!this._aggregatedErrors) {
|
|
10786
|
+
const all = [];
|
|
10787
|
+
const seen = new Set();
|
|
10788
|
+
const collect = (handler) => {
|
|
10789
|
+
for (const error of handler.errors ?? []) {
|
|
10790
|
+
if (!seen.has(error.code)) {
|
|
10791
|
+
seen.add(error.code);
|
|
10792
|
+
all.push(error);
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
};
|
|
10796
|
+
for (const program of Object.values(this.stack.programs ?? {})) {
|
|
10797
|
+
for (const handler of Object.values(program.rawInstructions ?? {})) {
|
|
10798
|
+
collect(handler);
|
|
10799
|
+
}
|
|
10800
|
+
}
|
|
10801
|
+
this._aggregatedErrors = all;
|
|
10802
|
+
}
|
|
10803
|
+
return this._aggregatedErrors;
|
|
9224
10804
|
}
|
|
9225
10805
|
get connectionState() {
|
|
9226
10806
|
return this.connection.getState();
|
|
@@ -9262,8 +10842,180 @@ class Arete {
|
|
|
9262
10842
|
getSubscriptionRegistry() {
|
|
9263
10843
|
return this.subscriptionRegistry;
|
|
9264
10844
|
}
|
|
10845
|
+
};
|
|
10846
|
+
|
|
10847
|
+
function createSignerRegistry(entries = []) {
|
|
10848
|
+
const signers = new Map();
|
|
10849
|
+
for (const [address, signer] of entries) {
|
|
10850
|
+
if (!address) {
|
|
10851
|
+
throw new Error('Signer registry addresses must not be empty');
|
|
10852
|
+
}
|
|
10853
|
+
signers.set(address, signer);
|
|
10854
|
+
}
|
|
10855
|
+
return {
|
|
10856
|
+
register(address, signer) {
|
|
10857
|
+
if (!address) {
|
|
10858
|
+
throw new Error('Signer registry addresses must not be empty');
|
|
10859
|
+
}
|
|
10860
|
+
signers.set(address, signer);
|
|
10861
|
+
},
|
|
10862
|
+
unregister(address) {
|
|
10863
|
+
return signers.delete(address);
|
|
10864
|
+
},
|
|
10865
|
+
get(address) {
|
|
10866
|
+
return signers.get(address);
|
|
10867
|
+
},
|
|
10868
|
+
has(address) {
|
|
10869
|
+
return signers.has(address);
|
|
10870
|
+
},
|
|
10871
|
+
addresses() {
|
|
10872
|
+
return [...signers.keys()];
|
|
10873
|
+
},
|
|
10874
|
+
values() {
|
|
10875
|
+
return [...signers.values()];
|
|
10876
|
+
},
|
|
10877
|
+
entries() {
|
|
10878
|
+
return [...signers.entries()];
|
|
10879
|
+
},
|
|
10880
|
+
clear() {
|
|
10881
|
+
signers.clear();
|
|
10882
|
+
},
|
|
10883
|
+
};
|
|
9265
10884
|
}
|
|
9266
10885
|
|
|
10886
|
+
function resolveMemberConnectOptions(stack, member, options, forceHttpOnly) {
|
|
10887
|
+
const transport = member?.transport ?? options?.transport ?? (forceHttpOnly ? 'http' : 'ws');
|
|
10888
|
+
// Resolution order: per-member override → the stack's own endpoints →
|
|
10889
|
+
// session-wide fallback endpoints.
|
|
10890
|
+
const url = member?.url ?? (stack.endpoints.ws || options?.endpoints?.ws);
|
|
10891
|
+
const httpUrl = member?.httpUrl ?? stack.endpoints.http ?? options?.endpoints?.http;
|
|
10892
|
+
return {
|
|
10893
|
+
url,
|
|
10894
|
+
httpUrl,
|
|
10895
|
+
transport,
|
|
10896
|
+
auth: member?.auth ?? options?.auth,
|
|
10897
|
+
storage: member?.storage,
|
|
10898
|
+
autoReconnect: member?.autoReconnect,
|
|
10899
|
+
wallet: options?.wallet,
|
|
10900
|
+
fetch: options?.fetch,
|
|
10901
|
+
};
|
|
10902
|
+
}
|
|
10903
|
+
function programAsStack(name, program) {
|
|
10904
|
+
return {
|
|
10905
|
+
name,
|
|
10906
|
+
endpoints: { ws: '' },
|
|
10907
|
+
views: {},
|
|
10908
|
+
programs: { [name]: program },
|
|
10909
|
+
};
|
|
10910
|
+
}
|
|
10911
|
+
async function createSession(definition, options) {
|
|
10912
|
+
const stackEntries = Object.entries(definition.stacks ?? {});
|
|
10913
|
+
const programEntries = Object.entries(definition.programs ?? {});
|
|
10914
|
+
if (stackEntries.length === 0 && programEntries.length === 0) {
|
|
10915
|
+
throw new AreteError('createSession requires at least one stack or program member', 'INVALID_CONFIG');
|
|
10916
|
+
}
|
|
10917
|
+
let wallet = options?.wallet;
|
|
10918
|
+
const signerRegistry = options?.signerRegistry ?? createSignerRegistry();
|
|
10919
|
+
const connectedStacks = await Promise.all(stackEntries.map(async ([key, stack]) => {
|
|
10920
|
+
const memberOptions = options?.stacks?.[key];
|
|
10921
|
+
const effectiveStack = withPrograms(stack, memberOptions?.programs);
|
|
10922
|
+
const connectOptions = resolveMemberConnectOptions(effectiveStack, memberOptions, options, false);
|
|
10923
|
+
const client = await Arete$1.connect(effectiveStack, connectOptions);
|
|
10924
|
+
return [key, client];
|
|
10925
|
+
}));
|
|
10926
|
+
const connectedPrograms = await Promise.all(programEntries.map(async ([key, program]) => {
|
|
10927
|
+
const syntheticStack = programAsStack(key, program);
|
|
10928
|
+
const connectOptions = resolveMemberConnectOptions(syntheticStack, options?.programs?.[key], options, true);
|
|
10929
|
+
const client = await Arete$1.connect(syntheticStack, connectOptions);
|
|
10930
|
+
return [key, client];
|
|
10931
|
+
}));
|
|
10932
|
+
const memberClients = [
|
|
10933
|
+
...connectedStacks.map(([, client]) => client),
|
|
10934
|
+
...connectedPrograms.map(([, client]) => client),
|
|
10935
|
+
];
|
|
10936
|
+
const executionHost = memberClients[0];
|
|
10937
|
+
const stacks = Object.fromEntries(connectedStacks);
|
|
10938
|
+
const explicitProgramKeys = new Set(connectedPrograms.map(([key]) => key));
|
|
10939
|
+
const programOwners = new Map();
|
|
10940
|
+
const connectedProgramEntries = connectedPrograms.map(([key, client]) => [
|
|
10941
|
+
key,
|
|
10942
|
+
client.programs[key],
|
|
10943
|
+
]);
|
|
10944
|
+
const promotedPrograms = Object.fromEntries(connectedProgramEntries);
|
|
10945
|
+
for (const [stackKey, client] of connectedStacks) {
|
|
10946
|
+
for (const [programKey, program] of Object.entries(client.programs)) {
|
|
10947
|
+
if (explicitProgramKeys.has(programKey)) {
|
|
10948
|
+
continue;
|
|
10949
|
+
}
|
|
10950
|
+
const existingOwner = programOwners.get(programKey);
|
|
10951
|
+
if (existingOwner) {
|
|
10952
|
+
console.warn(`Program '${programKey}' is bundled by stacks '${existingOwner.stack}'` +
|
|
10953
|
+
` (${existingOwner.programId ?? 'unknown program ID'}) and '${stackKey}'` +
|
|
10954
|
+
` (${program.programId ?? 'unknown program ID'}); session.programs.${programKey}` +
|
|
10955
|
+
` uses '${existingOwner.stack}' because it was connected first`);
|
|
10956
|
+
continue;
|
|
10957
|
+
}
|
|
10958
|
+
promotedPrograms[programKey] = program;
|
|
10959
|
+
programOwners.set(programKey, { stack: stackKey, programId: program.programId });
|
|
10960
|
+
}
|
|
10961
|
+
}
|
|
10962
|
+
const programs = promotedPrograms;
|
|
10963
|
+
const chain = options?.chain ?? (options?.endpoints?.http !== undefined
|
|
10964
|
+
? createChainClient(options.endpoints.http, (options?.fetch ?? globalThis.fetch?.bind(globalThis)))
|
|
10965
|
+
: executionHost.chain);
|
|
10966
|
+
const session = {
|
|
10967
|
+
stacks,
|
|
10968
|
+
programs,
|
|
10969
|
+
get wallet() {
|
|
10970
|
+
return wallet;
|
|
10971
|
+
},
|
|
10972
|
+
signerRegistry,
|
|
10973
|
+
chain,
|
|
10974
|
+
transaction(instructions, transactionOptions) {
|
|
10975
|
+
const defaults = options?.execution;
|
|
10976
|
+
const configuredSigners = transactionOptions?.signers ?? defaults?.signers;
|
|
10977
|
+
const signers = [...new Set([...signerRegistry.values(), ...(configuredSigners ?? [])])];
|
|
10978
|
+
return executionHost.transaction(instructions, {
|
|
10979
|
+
wallet: transactionOptions?.wallet ?? defaults?.wallet,
|
|
10980
|
+
send: defaults?.send || transactionOptions?.send
|
|
10981
|
+
? { ...(defaults?.send ?? {}), ...(transactionOptions?.send ?? {}) }
|
|
10982
|
+
: undefined,
|
|
10983
|
+
errors: transactionOptions?.errors,
|
|
10984
|
+
signers: signers.length > 0 ? signers : undefined,
|
|
10985
|
+
});
|
|
10986
|
+
},
|
|
10987
|
+
execute(prepared, executionOptions) {
|
|
10988
|
+
const defaults = options?.execution;
|
|
10989
|
+
const configuredSigners = executionOptions?.signers ?? defaults?.signers;
|
|
10990
|
+
return executionHost.execute(prepared, {
|
|
10991
|
+
wallet: executionOptions?.wallet ?? defaults?.wallet,
|
|
10992
|
+
send: defaults?.send || executionOptions?.send
|
|
10993
|
+
? { ...(defaults?.send ?? {}), ...(executionOptions?.send ?? {}) }
|
|
10994
|
+
: undefined,
|
|
10995
|
+
signers: configuredSigners,
|
|
10996
|
+
signerRegistry,
|
|
10997
|
+
availableSignerAddresses: executionOptions?.availableSignerAddresses ?? defaults?.availableSignerAddresses,
|
|
10998
|
+
onTransactionStart: executionOptions?.onTransactionStart ?? defaults?.onTransactionStart,
|
|
10999
|
+
onTransactionSuccess: executionOptions?.onTransactionSuccess ?? defaults?.onTransactionSuccess,
|
|
11000
|
+
});
|
|
11001
|
+
},
|
|
11002
|
+
setWallet(nextWallet) {
|
|
11003
|
+
wallet = nextWallet;
|
|
11004
|
+
for (const client of memberClients) {
|
|
11005
|
+
client.setWallet(nextWallet);
|
|
11006
|
+
}
|
|
11007
|
+
},
|
|
11008
|
+
close() {
|
|
11009
|
+
for (const client of memberClients) {
|
|
11010
|
+
client.disconnect();
|
|
11011
|
+
}
|
|
11012
|
+
},
|
|
11013
|
+
};
|
|
11014
|
+
return session;
|
|
11015
|
+
}
|
|
11016
|
+
|
|
11017
|
+
const Arete = Object.assign(Arete$1, { session: createSession });
|
|
11018
|
+
|
|
9267
11019
|
const DEFAULT_FLUSH_INTERVAL_MS = 16;
|
|
9268
11020
|
|
|
9269
11021
|
class ZustandAdapter {
|
|
@@ -9432,11 +11184,52 @@ class ZustandAdapter {
|
|
|
9432
11184
|
}
|
|
9433
11185
|
}
|
|
9434
11186
|
|
|
11187
|
+
function syncClientWallets(entries, wallet) {
|
|
11188
|
+
for (const entry of entries) {
|
|
11189
|
+
entry.client.setWallet(wallet);
|
|
11190
|
+
}
|
|
11191
|
+
}
|
|
11192
|
+
function initializeConnectedClient(client, adapter, wallet) {
|
|
11193
|
+
client.setWallet(wallet);
|
|
11194
|
+
client.onConnectionStateChange((state) => {
|
|
11195
|
+
adapter.setConnectionState(state);
|
|
11196
|
+
});
|
|
11197
|
+
adapter.setConnectionState(client.connectionState);
|
|
11198
|
+
}
|
|
11199
|
+
|
|
11200
|
+
const objectKeys = new WeakMap();
|
|
11201
|
+
let nextObjectKey = 0;
|
|
11202
|
+
function getObjectKey(value, prefix) {
|
|
11203
|
+
const existing = objectKeys.get(value);
|
|
11204
|
+
if (existing) {
|
|
11205
|
+
return existing;
|
|
11206
|
+
}
|
|
11207
|
+
const key = `${prefix}-${++nextObjectKey}`;
|
|
11208
|
+
objectKeys.set(value, key);
|
|
11209
|
+
return key;
|
|
11210
|
+
}
|
|
11211
|
+
function createClientCacheKey(stack, options) {
|
|
11212
|
+
if (!stack) {
|
|
11213
|
+
return null;
|
|
11214
|
+
}
|
|
11215
|
+
return [
|
|
11216
|
+
getObjectKey(stack, 'stack'),
|
|
11217
|
+
options?.transport ?? 'ws',
|
|
11218
|
+
options?.url ?? stack.endpoints.ws,
|
|
11219
|
+
options?.httpUrl ?? stack.endpoints.http ?? '',
|
|
11220
|
+
options?.programs
|
|
11221
|
+
? getObjectKey(options.programs, 'programs')
|
|
11222
|
+
: 'programs-none',
|
|
11223
|
+
].join(':');
|
|
11224
|
+
}
|
|
11225
|
+
|
|
9435
11226
|
const AreteContext = React.createContext(null);
|
|
9436
11227
|
function AreteProvider({ children, fallback = null, ...config }) {
|
|
9437
11228
|
const clientsRef = React.useRef(new Map());
|
|
9438
11229
|
const connectingRef = React.useRef(new Map());
|
|
9439
11230
|
const clientChangeListenersRef = React.useRef(new Set());
|
|
11231
|
+
const latestWalletRef = React.useRef(config.wallet);
|
|
11232
|
+
latestWalletRef.current = config.wallet;
|
|
9440
11233
|
const notifyClientChange = React.useCallback(() => {
|
|
9441
11234
|
clientChangeListenersRef.current.forEach(cb => { cb(); });
|
|
9442
11235
|
}, []);
|
|
@@ -9446,8 +11239,11 @@ function AreteProvider({ children, fallback = null, ...config }) {
|
|
|
9446
11239
|
clientChangeListenersRef.current.delete(callback);
|
|
9447
11240
|
};
|
|
9448
11241
|
}, []);
|
|
9449
|
-
const getOrCreateClient = React.useCallback(async (stack,
|
|
9450
|
-
const cacheKey =
|
|
11242
|
+
const getOrCreateClient = React.useCallback(async (stack, options) => {
|
|
11243
|
+
const cacheKey = createClientCacheKey(stack, options);
|
|
11244
|
+
if (!cacheKey) {
|
|
11245
|
+
throw new Error('Stack is required to create an Arete client');
|
|
11246
|
+
}
|
|
9451
11247
|
const existing = clientsRef.current.get(cacheKey);
|
|
9452
11248
|
if (existing) {
|
|
9453
11249
|
return existing.client;
|
|
@@ -9458,21 +11254,24 @@ function AreteProvider({ children, fallback = null, ...config }) {
|
|
|
9458
11254
|
}
|
|
9459
11255
|
const adapter = new ZustandAdapter();
|
|
9460
11256
|
const connectionPromise = Arete.connect(stack, {
|
|
9461
|
-
url:
|
|
11257
|
+
url: options?.url,
|
|
11258
|
+
httpUrl: options?.httpUrl,
|
|
11259
|
+
transport: options?.transport,
|
|
11260
|
+
programs: options?.programs,
|
|
9462
11261
|
storage: adapter,
|
|
9463
11262
|
autoReconnect: config.autoConnect,
|
|
9464
11263
|
reconnectIntervals: config.reconnectIntervals,
|
|
9465
11264
|
maxReconnectAttempts: config.maxReconnectAttempts,
|
|
9466
11265
|
maxEntriesPerView: config.maxEntriesPerView,
|
|
9467
11266
|
flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
|
|
11267
|
+
fetch: config.fetch,
|
|
11268
|
+
validateFrames: config.validateFrames,
|
|
9468
11269
|
auth: config.auth,
|
|
11270
|
+
wallet: latestWalletRef.current,
|
|
9469
11271
|
}).then((client) => {
|
|
9470
|
-
client
|
|
9471
|
-
adapter.setConnectionState(state, error);
|
|
9472
|
-
});
|
|
9473
|
-
adapter.setConnectionState(client.connectionState);
|
|
11272
|
+
initializeConnectedClient(client, adapter, latestWalletRef.current);
|
|
9474
11273
|
clientsRef.current.set(cacheKey, {
|
|
9475
|
-
client,
|
|
11274
|
+
client: client,
|
|
9476
11275
|
disconnect: () => client.disconnect()
|
|
9477
11276
|
});
|
|
9478
11277
|
connectingRef.current.delete(cacheKey);
|
|
@@ -9481,8 +11280,8 @@ function AreteProvider({ children, fallback = null, ...config }) {
|
|
|
9481
11280
|
});
|
|
9482
11281
|
connectingRef.current.set(cacheKey, connectionPromise);
|
|
9483
11282
|
return connectionPromise;
|
|
9484
|
-
}, [config.autoConnect, config.reconnectIntervals, config.maxReconnectAttempts, config.maxEntriesPerView, config.flushIntervalMs, config.auth, notifyClientChange]);
|
|
9485
|
-
const getClient = React.useCallback((stack) => {
|
|
11283
|
+
}, [config.autoConnect, config.reconnectIntervals, config.maxReconnectAttempts, config.maxEntriesPerView, config.flushIntervalMs, config.fetch, config.validateFrames, config.auth, notifyClientChange]);
|
|
11284
|
+
const getClient = React.useCallback((stack, options) => {
|
|
9486
11285
|
if (!stack) {
|
|
9487
11286
|
if (clientsRef.current.size === 1) {
|
|
9488
11287
|
const firstEntry = clientsRef.current.values().next().value;
|
|
@@ -9490,9 +11289,17 @@ function AreteProvider({ children, fallback = null, ...config }) {
|
|
|
9490
11289
|
}
|
|
9491
11290
|
return null;
|
|
9492
11291
|
}
|
|
9493
|
-
const
|
|
11292
|
+
const cacheKey = createClientCacheKey(stack, options);
|
|
11293
|
+
if (!cacheKey) {
|
|
11294
|
+
return null;
|
|
11295
|
+
}
|
|
11296
|
+
const entry = clientsRef.current.get(cacheKey);
|
|
9494
11297
|
return entry ? entry.client : null;
|
|
9495
11298
|
}, []);
|
|
11299
|
+
React.useEffect(() => {
|
|
11300
|
+
syncClientWallets(clientsRef.current.values(), config.wallet);
|
|
11301
|
+
notifyClientChange();
|
|
11302
|
+
}, [config.wallet, notifyClientChange]);
|
|
9496
11303
|
React.useEffect(() => {
|
|
9497
11304
|
return () => {
|
|
9498
11305
|
clientsRef.current.forEach((entry) => {
|
|
@@ -9517,10 +11324,10 @@ function useAreteContext() {
|
|
|
9517
11324
|
}
|
|
9518
11325
|
return context;
|
|
9519
11326
|
}
|
|
9520
|
-
function useConnectionState(stack) {
|
|
11327
|
+
function useConnectionState(stack, options) {
|
|
9521
11328
|
const { getClient, subscribeToClientChanges } = useAreteContext();
|
|
9522
11329
|
const [state, setState] = React.useState(() => {
|
|
9523
|
-
const client = getClient(stack);
|
|
11330
|
+
const client = getClient(stack, options);
|
|
9524
11331
|
return client?.connectionState ?? 'disconnected';
|
|
9525
11332
|
});
|
|
9526
11333
|
const unsubscribeRef = React.useRef(undefined);
|
|
@@ -9529,7 +11336,7 @@ function useConnectionState(stack) {
|
|
|
9529
11336
|
const setupClientSubscription = () => {
|
|
9530
11337
|
unsubscribeRef.current?.();
|
|
9531
11338
|
unsubscribeRef.current = undefined;
|
|
9532
|
-
const client = getClient(stack);
|
|
11339
|
+
const client = getClient(stack, options);
|
|
9533
11340
|
if (client && mounted) {
|
|
9534
11341
|
setState(client.connectionState);
|
|
9535
11342
|
unsubscribeRef.current = client.onConnectionStateChange((newState) => {
|
|
@@ -9548,12 +11355,12 @@ function useConnectionState(stack) {
|
|
|
9548
11355
|
unsubscribeFromClientChanges();
|
|
9549
11356
|
unsubscribeRef.current?.();
|
|
9550
11357
|
};
|
|
9551
|
-
}, [getClient, subscribeToClientChanges, stack]);
|
|
11358
|
+
}, [getClient, subscribeToClientChanges, stack, options]);
|
|
9552
11359
|
return state;
|
|
9553
11360
|
}
|
|
9554
|
-
function useView(stack, viewPath) {
|
|
11361
|
+
function useView(stack, viewPath, options) {
|
|
9555
11362
|
const { getClient } = useAreteContext();
|
|
9556
|
-
const client = getClient(stack);
|
|
11363
|
+
const client = getClient(stack, options);
|
|
9557
11364
|
return React.useSyncExternalStore((callback) => {
|
|
9558
11365
|
if (!client)
|
|
9559
11366
|
return () => { };
|
|
@@ -9565,9 +11372,9 @@ function useView(stack, viewPath) {
|
|
|
9565
11372
|
return data;
|
|
9566
11373
|
});
|
|
9567
11374
|
}
|
|
9568
|
-
function useEntity(stack, viewPath, key) {
|
|
11375
|
+
function useEntity(stack, viewPath, key, options) {
|
|
9569
11376
|
const { getClient } = useAreteContext();
|
|
9570
|
-
const client = getClient(stack);
|
|
11377
|
+
const client = getClient(stack, options);
|
|
9571
11378
|
return React.useSyncExternalStore((callback) => {
|
|
9572
11379
|
if (!client)
|
|
9573
11380
|
return () => { };
|
|
@@ -9580,6 +11387,84 @@ function useEntity(stack, viewPath, key) {
|
|
|
9580
11387
|
});
|
|
9581
11388
|
}
|
|
9582
11389
|
|
|
11390
|
+
function wrapRawInstruction(instruction, client, createMutationHook) {
|
|
11391
|
+
const execute = async (input, options) => {
|
|
11392
|
+
if (!client) {
|
|
11393
|
+
throw new Error('Arete client is not connected');
|
|
11394
|
+
}
|
|
11395
|
+
return client.transaction([instruction.build(input)], options);
|
|
11396
|
+
};
|
|
11397
|
+
return {
|
|
11398
|
+
execute,
|
|
11399
|
+
build: instruction.build,
|
|
11400
|
+
useMutation: () => createMutationHook(execute),
|
|
11401
|
+
};
|
|
11402
|
+
}
|
|
11403
|
+
function isConnectedOperation(value) {
|
|
11404
|
+
return Boolean(value && typeof value === 'object' && typeof value.prepare === 'function');
|
|
11405
|
+
}
|
|
11406
|
+
function wrapOperation(operation, client, createMutationHook) {
|
|
11407
|
+
const execute = async (input, options) => {
|
|
11408
|
+
if (!client) {
|
|
11409
|
+
throw new Error('Arete client is not connected');
|
|
11410
|
+
}
|
|
11411
|
+
const prepared = await operation.prepare(input);
|
|
11412
|
+
return client.execute(prepared, options);
|
|
11413
|
+
};
|
|
11414
|
+
return {
|
|
11415
|
+
prepare: operation.prepare.bind(operation),
|
|
11416
|
+
execute,
|
|
11417
|
+
useMutation: () => createMutationHook(execute),
|
|
11418
|
+
};
|
|
11419
|
+
}
|
|
11420
|
+
function wrapOperationNamespace(namespace, client, createMutationHook) {
|
|
11421
|
+
const wrapped = {};
|
|
11422
|
+
for (const [name, value] of Object.entries(namespace)) {
|
|
11423
|
+
if (isConnectedOperation(value)) {
|
|
11424
|
+
wrapped[name] = wrapOperation(value, client, createMutationHook);
|
|
11425
|
+
continue;
|
|
11426
|
+
}
|
|
11427
|
+
if (value && typeof value === 'object') {
|
|
11428
|
+
wrapped[name] = wrapOperationNamespace(value, client, createMutationHook);
|
|
11429
|
+
}
|
|
11430
|
+
}
|
|
11431
|
+
return wrapped;
|
|
11432
|
+
}
|
|
11433
|
+
function wrapMaybeOperationNamespace(namespace, client, createMutationHook) {
|
|
11434
|
+
return namespace
|
|
11435
|
+
? wrapOperationNamespace(namespace, client, createMutationHook)
|
|
11436
|
+
: {};
|
|
11437
|
+
}
|
|
11438
|
+
function buildProgramHookInterfaces(programs, client, createMutationHook) {
|
|
11439
|
+
const wrappedPrograms = {};
|
|
11440
|
+
if (!programs) {
|
|
11441
|
+
return wrappedPrograms;
|
|
11442
|
+
}
|
|
11443
|
+
for (const [name, program] of Object.entries(programs)) {
|
|
11444
|
+
const raw = {};
|
|
11445
|
+
for (const [instructionName, instruction] of Object.entries(program.raw)) {
|
|
11446
|
+
raw[instructionName] = wrapRawInstruction(instruction, client, createMutationHook);
|
|
11447
|
+
}
|
|
11448
|
+
wrappedPrograms[name] = {
|
|
11449
|
+
name: program.name,
|
|
11450
|
+
programId: program.programId,
|
|
11451
|
+
schemas: program.schemas,
|
|
11452
|
+
pdas: program.pdas,
|
|
11453
|
+
accounts: program.accounts,
|
|
11454
|
+
queries: program.queries,
|
|
11455
|
+
addresses: program.addresses,
|
|
11456
|
+
constants: program.constants,
|
|
11457
|
+
defaults: program.defaults,
|
|
11458
|
+
math: program.math,
|
|
11459
|
+
raw: raw,
|
|
11460
|
+
instructions: wrapMaybeOperationNamespace(program.instructions, client, createMutationHook),
|
|
11461
|
+
transactions: wrapMaybeOperationNamespace(program.transactions, client, createMutationHook),
|
|
11462
|
+
flows: wrapMaybeOperationNamespace(program.flows, client, createMutationHook),
|
|
11463
|
+
};
|
|
11464
|
+
}
|
|
11465
|
+
return wrappedPrograms;
|
|
11466
|
+
}
|
|
11467
|
+
|
|
9583
11468
|
function shallowArrayEqual(a, b) {
|
|
9584
11469
|
if (a === b)
|
|
9585
11470
|
return true;
|
|
@@ -9593,6 +11478,34 @@ function shallowArrayEqual(a, b) {
|
|
|
9593
11478
|
}
|
|
9594
11479
|
return true;
|
|
9595
11480
|
}
|
|
11481
|
+
function compareNumericValues(value, condition, op) {
|
|
11482
|
+
if (typeof value === 'bigint' || typeof condition === 'bigint') {
|
|
11483
|
+
const left = typeof value === 'bigint' ? value : BigInt(value);
|
|
11484
|
+
const right = typeof condition === 'bigint' ? condition : BigInt(condition);
|
|
11485
|
+
switch (op) {
|
|
11486
|
+
case 'gte':
|
|
11487
|
+
return left >= right;
|
|
11488
|
+
case 'lte':
|
|
11489
|
+
return left <= right;
|
|
11490
|
+
case 'gt':
|
|
11491
|
+
return left > right;
|
|
11492
|
+
case 'lt':
|
|
11493
|
+
return left < right;
|
|
11494
|
+
}
|
|
11495
|
+
}
|
|
11496
|
+
const left = value;
|
|
11497
|
+
const right = condition;
|
|
11498
|
+
switch (op) {
|
|
11499
|
+
case 'gte':
|
|
11500
|
+
return left >= right;
|
|
11501
|
+
case 'lte':
|
|
11502
|
+
return left <= right;
|
|
11503
|
+
case 'gt':
|
|
11504
|
+
return left > right;
|
|
11505
|
+
case 'lt':
|
|
11506
|
+
return left < right;
|
|
11507
|
+
}
|
|
11508
|
+
}
|
|
9596
11509
|
function useStateView(viewDef, client, key, options) {
|
|
9597
11510
|
const [isLoading, setIsLoading] = React.useState(!options?.initialData && options?.withSnapshot !== false);
|
|
9598
11511
|
const [error, setError] = React.useState();
|
|
@@ -9677,7 +11590,10 @@ function useStateView(viewDef, client, key, options) {
|
|
|
9677
11590
|
? clientRef.current.store.get(viewDef.view, keyString)
|
|
9678
11591
|
: clientRef.current.store.getAll(viewDef.view)[0];
|
|
9679
11592
|
const validated = entity && schema
|
|
9680
|
-
? (
|
|
11593
|
+
? (() => {
|
|
11594
|
+
const parsed = schema.safeParse(entity);
|
|
11595
|
+
return parsed.success ? parsed.data : undefined;
|
|
11596
|
+
})()
|
|
9681
11597
|
: entity;
|
|
9682
11598
|
if (validated !== cachedSnapshotRef.current) {
|
|
9683
11599
|
cachedSnapshotRef.current = validated;
|
|
@@ -9803,20 +11719,23 @@ function useListView(viewDef, client, params, options) {
|
|
|
9803
11719
|
if (typeof condition === 'object' && condition !== null) {
|
|
9804
11720
|
const cond = condition;
|
|
9805
11721
|
if ('gte' in cond)
|
|
9806
|
-
return value
|
|
11722
|
+
return compareNumericValues(value, cond.gte, 'gte');
|
|
9807
11723
|
if ('lte' in cond)
|
|
9808
|
-
return value
|
|
11724
|
+
return compareNumericValues(value, cond.lte, 'lte');
|
|
9809
11725
|
if ('gt' in cond)
|
|
9810
|
-
return value
|
|
11726
|
+
return compareNumericValues(value, cond.gt, 'gt');
|
|
9811
11727
|
if ('lt' in cond)
|
|
9812
|
-
return value
|
|
11728
|
+
return compareNumericValues(value, cond.lt, 'lt');
|
|
9813
11729
|
}
|
|
9814
11730
|
return value === condition;
|
|
9815
11731
|
});
|
|
9816
11732
|
});
|
|
9817
11733
|
}
|
|
9818
11734
|
if (schema) {
|
|
9819
|
-
items = items.
|
|
11735
|
+
items = items.flatMap((item) => {
|
|
11736
|
+
const parsed = schema.safeParse(item);
|
|
11737
|
+
return parsed.success ? [parsed.data] : [];
|
|
11738
|
+
});
|
|
9820
11739
|
}
|
|
9821
11740
|
if (limit) {
|
|
9822
11741
|
items = items.slice(0, limit);
|
|
@@ -9876,30 +11795,42 @@ function createListViewHook(viewDef, client) {
|
|
|
9876
11795
|
function useInstructionMutation(execute) {
|
|
9877
11796
|
const [status, setStatus] = React.useState('idle');
|
|
9878
11797
|
const [error, setError] = React.useState(null);
|
|
11798
|
+
const [signatures, setSignatures] = React.useState([]);
|
|
9879
11799
|
const [signature, setSignature] = React.useState(null);
|
|
9880
11800
|
const submit = React.useCallback(async (args, options) => {
|
|
9881
11801
|
setStatus('pending');
|
|
9882
11802
|
setError(null);
|
|
11803
|
+
setSignatures([]);
|
|
9883
11804
|
setSignature(null);
|
|
11805
|
+
const { onSuccess, onError, ...executionOptions } = options ?? {};
|
|
9884
11806
|
try {
|
|
9885
|
-
const result = await execute(args,
|
|
11807
|
+
const result = await execute(args, executionOptions);
|
|
11808
|
+
const resultSignatures = typeof result === 'object' && result !== null && 'signatures' in result
|
|
11809
|
+
&& Array.isArray(result.signatures)
|
|
11810
|
+
? result.signatures.map(String)
|
|
11811
|
+
: typeof result === 'object' && result !== null && 'signature' in result
|
|
11812
|
+
? [String(result.signature)]
|
|
11813
|
+
: [];
|
|
9886
11814
|
setStatus('success');
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
11815
|
+
setSignatures(resultSignatures);
|
|
11816
|
+
setSignature(resultSignatures.length === 1 ? resultSignatures[0] : null);
|
|
11817
|
+
if (onSuccess) {
|
|
11818
|
+
onSuccess(result);
|
|
9890
11819
|
}
|
|
9891
11820
|
return result;
|
|
9892
11821
|
}
|
|
9893
11822
|
catch (err) {
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
const displayError = programError
|
|
9897
|
-
? `${programError.name}: ${programError.message}`
|
|
9898
|
-
:
|
|
11823
|
+
// The core executor already parses program errors against the handler's
|
|
11824
|
+
// IDL error definitions and throws an InstructionError.
|
|
11825
|
+
const displayError = err instanceof InstructionError && err.programError
|
|
11826
|
+
? `${err.programError.name}: ${err.programError.message}`
|
|
11827
|
+
: err instanceof Error
|
|
11828
|
+
? err.message
|
|
11829
|
+
: String(err);
|
|
9899
11830
|
setStatus('error');
|
|
9900
11831
|
setError(displayError);
|
|
9901
|
-
if (
|
|
9902
|
-
|
|
11832
|
+
if (onError && err instanceof Error) {
|
|
11833
|
+
onError(err);
|
|
9903
11834
|
}
|
|
9904
11835
|
throw err;
|
|
9905
11836
|
}
|
|
@@ -9907,12 +11838,14 @@ function useInstructionMutation(execute) {
|
|
|
9907
11838
|
const reset = React.useCallback(() => {
|
|
9908
11839
|
setStatus('idle');
|
|
9909
11840
|
setError(null);
|
|
11841
|
+
setSignatures([]);
|
|
9910
11842
|
setSignature(null);
|
|
9911
11843
|
}, []);
|
|
9912
11844
|
return {
|
|
9913
11845
|
submit,
|
|
9914
11846
|
status,
|
|
9915
11847
|
error,
|
|
11848
|
+
signatures,
|
|
9916
11849
|
signature,
|
|
9917
11850
|
isLoading: status === 'pending',
|
|
9918
11851
|
reset,
|
|
@@ -9921,21 +11854,25 @@ function useInstructionMutation(execute) {
|
|
|
9921
11854
|
|
|
9922
11855
|
function useArete(stack, options) {
|
|
9923
11856
|
const { getOrCreateClient, getClient } = useAreteContext();
|
|
9924
|
-
const
|
|
9925
|
-
const
|
|
11857
|
+
const url = options?.url;
|
|
11858
|
+
const httpUrl = options?.httpUrl;
|
|
11859
|
+
const transport = options?.transport;
|
|
11860
|
+
const attachedPrograms = options?.programs;
|
|
11861
|
+
const lookupOptions = React.useMemo(() => ({ url, httpUrl, transport, programs: attachedPrograms }), [url, httpUrl, transport, attachedPrograms]);
|
|
11862
|
+
const [client, setClient] = React.useState(getClient(stack, lookupOptions));
|
|
9926
11863
|
const [isLoading, setIsLoading] = React.useState(!client);
|
|
9927
11864
|
const [error, setError] = React.useState(null);
|
|
9928
11865
|
const [connectionState, setConnectionState] = React.useState(() => client?.connectionState ?? 'disconnected');
|
|
9929
11866
|
React.useEffect(() => {
|
|
9930
|
-
const existingClient = getClient(stack);
|
|
9931
|
-
if (existingClient
|
|
11867
|
+
const existingClient = getClient(stack, lookupOptions);
|
|
11868
|
+
if (existingClient) {
|
|
9932
11869
|
setClient(existingClient);
|
|
9933
11870
|
setIsLoading(false);
|
|
9934
11871
|
return;
|
|
9935
11872
|
}
|
|
9936
11873
|
setIsLoading(true);
|
|
9937
11874
|
setError(null);
|
|
9938
|
-
getOrCreateClient(stack,
|
|
11875
|
+
getOrCreateClient(stack, lookupOptions)
|
|
9939
11876
|
.then((newClient) => {
|
|
9940
11877
|
setClient(newClient);
|
|
9941
11878
|
setIsLoading(false);
|
|
@@ -9944,7 +11881,7 @@ function useArete(stack, options) {
|
|
|
9944
11881
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
9945
11882
|
setIsLoading(false);
|
|
9946
11883
|
});
|
|
9947
|
-
}, [stack, getOrCreateClient, getClient,
|
|
11884
|
+
}, [stack, getOrCreateClient, getClient, lookupOptions]);
|
|
9948
11885
|
React.useEffect(() => {
|
|
9949
11886
|
if (!client) {
|
|
9950
11887
|
setConnectionState('disconnected');
|
|
@@ -9975,21 +11912,14 @@ function useArete(stack, options) {
|
|
|
9975
11912
|
}
|
|
9976
11913
|
return result;
|
|
9977
11914
|
}, [stack, client]);
|
|
9978
|
-
const
|
|
9979
|
-
|
|
9980
|
-
if (client?.instructions) {
|
|
9981
|
-
for (const [instructionName, executeFn] of Object.entries(client.instructions)) {
|
|
9982
|
-
result[instructionName] = {
|
|
9983
|
-
execute: executeFn,
|
|
9984
|
-
useMutation: () => useInstructionMutation(executeFn)
|
|
9985
|
-
};
|
|
9986
|
-
}
|
|
9987
|
-
}
|
|
9988
|
-
return result;
|
|
11915
|
+
const programs = React.useMemo(() => {
|
|
11916
|
+
return buildProgramHookInterfaces(client?.programs, client, useInstructionMutation);
|
|
9989
11917
|
}, [client]);
|
|
9990
11918
|
return {
|
|
9991
11919
|
views: views,
|
|
9992
|
-
|
|
11920
|
+
queries: (client?.queries ?? {}),
|
|
11921
|
+
programs: programs,
|
|
11922
|
+
chain: client?.chain,
|
|
9993
11923
|
zustandStore: client?.store?.store,
|
|
9994
11924
|
client: client,
|
|
9995
11925
|
connectionState,
|
|
@@ -10006,10 +11936,14 @@ exports.ConnectionManager = ConnectionManager;
|
|
|
10006
11936
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
10007
11937
|
exports.DEFAULT_MAX_ENTRIES_PER_VIEW = DEFAULT_MAX_ENTRIES_PER_VIEW;
|
|
10008
11938
|
exports.FrameProcessor = FrameProcessor;
|
|
11939
|
+
exports.InstructionError = InstructionError;
|
|
10009
11940
|
exports.MemoryAdapter = MemoryAdapter;
|
|
11941
|
+
exports.ReadRequestError = ReadRequestError;
|
|
10010
11942
|
exports.SubscriptionRegistry = SubscriptionRegistry;
|
|
10011
11943
|
exports.ZustandAdapter = ZustandAdapter;
|
|
11944
|
+
exports.buildInstruction = buildInstruction;
|
|
10012
11945
|
exports.createInstructionExecutor = createInstructionExecutor;
|
|
11946
|
+
exports.createInstructionHandler = createInstructionHandler;
|
|
10013
11947
|
exports.createPublicKeySeed = createPublicKeySeed;
|
|
10014
11948
|
exports.createSeed = createSeed;
|
|
10015
11949
|
exports.derivePda = findProgramAddress;
|
|
@@ -10029,5 +11963,5 @@ exports.useEntity = useEntity;
|
|
|
10029
11963
|
exports.useInstructionMutation = useInstructionMutation;
|
|
10030
11964
|
exports.useView = useView;
|
|
10031
11965
|
exports.validateAccountResolution = validateAccountResolution;
|
|
10032
|
-
exports.
|
|
11966
|
+
exports.withPrograms = withPrograms;
|
|
10033
11967
|
//# sourceMappingURL=index.js.map
|