@bounded-sh/core 0.0.68 → 0.0.70
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 +0 -1
- package/dist/client/config.d.ts +4 -8
- package/dist/client/operations.d.ts +0 -3
- package/dist/client/reconcile.d.ts +7 -16
- package/dist/{index-C9w7qQpj.js → index-CqTl6vHg.js} +76 -632
- package/dist/index-CqTl6vHg.js.map +1 -0
- package/dist/{index-C9h-Bnjk.mjs → index-Csf_2uZF.mjs} +77 -629
- package/dist/index-Csf_2uZF.mjs.map +1 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{realtime-store-B_Szs6F3.mjs → realtime-store-ByP5wYsa.mjs} +2 -2
- package/dist/{realtime-store-B_Szs6F3.mjs.map → realtime-store-ByP5wYsa.mjs.map} +1 -1
- package/dist/{realtime-store-CQiij3zc.js → realtime-store-DfeKV55-.js} +2 -2
- package/dist/{realtime-store-CQiij3zc.js.map → realtime-store-DfeKV55-.js.map} +1 -1
- package/dist/{solana-message-ngQQt80s.mjs → solana-message-BFqre_Oi.mjs} +2 -2
- package/dist/{solana-message-ngQQt80s.mjs.map → solana-message-BFqre_Oi.mjs.map} +1 -1
- package/dist/{solana-message-DibkzgkZ.js → solana-message-BecAT47y.js} +2 -2
- package/dist/{solana-message-DibkzgkZ.js.map → solana-message-BecAT47y.js.map} +1 -1
- package/dist/{solana-operations-B0Q9UOHR.mjs → solana-operations-ByMDnjog.mjs} +127 -8
- package/dist/solana-operations-ByMDnjog.mjs.map +1 -0
- package/dist/{solana-operations-XjBhPOnu.js → solana-operations-uN89eqO2.js} +127 -8
- package/dist/solana-operations-uN89eqO2.js.map +1 -0
- package/dist/types.d.ts +14 -182
- package/dist/utils/session-events.d.ts +2 -2
- package/package.json +2 -2
- package/dist/index-C9h-Bnjk.mjs.map +0 -1
- package/dist/index-C9w7qQpj.js.map +0 -1
- package/dist/solana-operations-B0Q9UOHR.mjs.map +0 -1
- package/dist/solana-operations-XjBhPOnu.js.map +0 -1
- package/dist/utils/evm/evm-transaction-validation.d.ts +0 -46
|
@@ -685,8 +685,8 @@ function refreshedTokenMayReplaceStored(newIdToken, storedAddress, decodeBase64U
|
|
|
685
685
|
* event bus, so a session dying inside a session manager used to be invisible to
|
|
686
686
|
* the SDK: `setCurrentUser(null)` was only ever called by `logout()` and `init()`.
|
|
687
687
|
* The result was a UI that kept showing a connected wallet long after the session
|
|
688
|
-
* it was showing had been wiped, which is exactly what let
|
|
689
|
-
* review against a wallet the server no longer knew about.
|
|
688
|
+
* it was showing had been wiped, which is exactly what let a wallet-review UI
|
|
689
|
+
* freeze a review against a wallet the server no longer knew about.
|
|
690
690
|
*
|
|
691
691
|
* `js-sdk` registers a handler at init; core fires it ONLY when storage was
|
|
692
692
|
* actually cleared, never on a failed-but-preserved refresh. Firing on a
|
|
@@ -4043,7 +4043,7 @@ class ServerSessionManager {
|
|
|
4043
4043
|
const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
|
|
4044
4044
|
/* fetch nonce from auth API (bound to the snapshotted app+issuer) */
|
|
4045
4045
|
const nonce = await genAuthNonce(sessionConfig);
|
|
4046
|
-
const { genSolanaMessage } = await import('./solana-message-
|
|
4046
|
+
const { genSolanaMessage } = await import('./solana-message-BFqre_Oi.mjs');
|
|
4047
4047
|
const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
|
|
4048
4048
|
/* sign the message */
|
|
4049
4049
|
const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
|
|
@@ -5404,6 +5404,35 @@ async function getWsAuthToken(isServer) {
|
|
|
5404
5404
|
throw rememberAmbientAuthFailure(normalizeAuthExpiredError(error, 'Authentication expired and refresh failed'));
|
|
5405
5405
|
}
|
|
5406
5406
|
}
|
|
5407
|
+
/**
|
|
5408
|
+
* The server refused the token sent on the auth frame: its error frame arrives
|
|
5409
|
+
* while the auth ack is still pending, just before the 1008 close. Local expiry
|
|
5410
|
+
* had vouched for that exact token (unexpired but invalid - e.g. a rotated
|
|
5411
|
+
* issuer key), so re-sending it would only be refused again. Force a genuinely
|
|
5412
|
+
* new one: a definitive issuer rejection clears the session inside the shared
|
|
5413
|
+
* helper (firing notifySessionCleared, which signs the UI out), a successful
|
|
5414
|
+
* refresh commits a new token - and the ordinary reconnect loop picks up either
|
|
5415
|
+
* outcome on its own. Fresh token: authenticates. No token: connects
|
|
5416
|
+
* anonymously and public reads replay instead of erroring forever.
|
|
5417
|
+
*/
|
|
5418
|
+
async function recoverFromServerAuthRejection(connection) {
|
|
5419
|
+
const rejected = connection.pendingAuthToken;
|
|
5420
|
+
if (!rejected)
|
|
5421
|
+
return;
|
|
5422
|
+
// One forced refresh per distinct rejected token value: a transiently
|
|
5423
|
+
// unreachable issuer must not turn the reconnect loop into a refresh storm.
|
|
5424
|
+
if (connection.serverRejectedAuthToken === rejected)
|
|
5425
|
+
return;
|
|
5426
|
+
connection.serverRejectedAuthToken = rejected;
|
|
5427
|
+
try {
|
|
5428
|
+
await getFreshAuthToken(connection.isServer, { force: true });
|
|
5429
|
+
}
|
|
5430
|
+
catch (_a) {
|
|
5431
|
+
// Every failure mode is already handled inside the helper: a dead
|
|
5432
|
+
// session was cleared (auth listeners notified), a transient refresh
|
|
5433
|
+
// failure keeps the session and the latch above stops the storm.
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5407
5436
|
async function getConnectionAuthToken(isServer, authTokenProvider, anonymous) {
|
|
5408
5437
|
// A read-only scoped instance connects anonymously: no auth frame, and it
|
|
5409
5438
|
// must never read (or refresh) the ambient global session - that session
|
|
@@ -5727,6 +5756,8 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
|
|
|
5727
5756
|
authFailure: null,
|
|
5728
5757
|
source,
|
|
5729
5758
|
anonymous,
|
|
5759
|
+
isServer,
|
|
5760
|
+
serverRejectedAuthToken: null,
|
|
5730
5761
|
};
|
|
5731
5762
|
connections.set(connKey, connection);
|
|
5732
5763
|
// URL provider for reconnection with fresh tokens
|
|
@@ -5992,6 +6023,16 @@ function handleServerMessage(connection, message) {
|
|
|
5992
6023
|
}
|
|
5993
6024
|
case 'error': {
|
|
5994
6025
|
console.error('[WS v2] Server error:', message.code, message.message);
|
|
6026
|
+
// The auth frame was just refused: while the auth ack is pending the
|
|
6027
|
+
// server sends no other error frame, and this one is immediately
|
|
6028
|
+
// followed by its 1008 close. Kick the forced-refresh recovery - the
|
|
6029
|
+
// ordinary reconnect loop then re-auths with a fresh token or, once a
|
|
6030
|
+
// definitively dead session is cleared, connects anonymously so
|
|
6031
|
+
// public reads still replay. Ambient connections only: wallet-scoped
|
|
6032
|
+
// connections refresh through their own provider.
|
|
6033
|
+
if (connection.isAuthenticating && connection.pendingAuthToken && !connection.authTokenProvider && !connection.anonymous) {
|
|
6034
|
+
void recoverFromServerAuthRejection(connection);
|
|
6035
|
+
}
|
|
5995
6036
|
// Build an Error that ALSO carries the stable `code` as a structured
|
|
5996
6037
|
// field (not just string-interpolated into the message) so callers can
|
|
5997
6038
|
// branch on it — e.g. `catch (e) { if (e.code === 'invariant_violation') }`
|
|
@@ -6775,7 +6816,7 @@ async function doReconnectWithNewAuth() {
|
|
|
6775
6816
|
console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
|
|
6776
6817
|
}
|
|
6777
6818
|
try {
|
|
6778
|
-
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-
|
|
6819
|
+
const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-ByP5wYsa.mjs');
|
|
6779
6820
|
await reconnectRealtimeStoreWithNewAuth();
|
|
6780
6821
|
}
|
|
6781
6822
|
catch (error) {
|
|
@@ -6886,7 +6927,7 @@ async function runNamedQueriesOverSocket(appId, queries, isServer) {
|
|
|
6886
6927
|
return null; // timeout / disconnect / server error frame -> HTTP re-runs authoritatively
|
|
6887
6928
|
}
|
|
6888
6929
|
if ((data === null || data === void 0 ? void 0 : data.useHttp) === true)
|
|
6889
|
-
return null; // server deferred (onchain
|
|
6930
|
+
return null; // server deferred (onchain, cross-room, malformed)
|
|
6890
6931
|
if (Array.isArray(data === null || data === void 0 ? void 0 : data.queries))
|
|
6891
6932
|
return data.queries;
|
|
6892
6933
|
return null; // unexpected shape -> HTTP
|
|
@@ -6999,456 +7040,6 @@ async function wsIntentReliable(appId, roomRoutePath, intent, isServer) {
|
|
|
6999
7040
|
});
|
|
7000
7041
|
}
|
|
7001
7042
|
|
|
7002
|
-
/**
|
|
7003
|
-
* Selectors derived from the canonical BoundedRuntime signatures (BoundedTypes.sol):
|
|
7004
|
-
*
|
|
7005
|
-
* cast sig 'setDocuments(string,(string[],bool,(string,uint8,uint8,bytes)[])[])'
|
|
7006
|
-
* => 0x0827b094
|
|
7007
|
-
* cast sig 'setDocumentsAttested(string,(string[],bool,(string,uint8,uint8,bytes)[])[],(bytes32,uint256,uint256,uint8,bytes32,bytes32))'
|
|
7008
|
-
* => 0x87b111c4
|
|
7009
|
-
*
|
|
7010
|
-
* Keep the signatures beside the constants: changing either Solidity tuple is an
|
|
7011
|
-
* ABI break and must deliberately update these values and the Foundry goldens.
|
|
7012
|
-
*/
|
|
7013
|
-
const BOUNDED_RUNTIME_SET_DOCUMENTS_SELECTOR = '0x0827b094';
|
|
7014
|
-
const BOUNDED_RUNTIME_SET_DOCUMENTS_ATTESTED_SELECTOR = '0x87b111c4';
|
|
7015
|
-
const SET_DOCUMENTS_SELECTOR = BOUNDED_RUNTIME_SET_DOCUMENTS_SELECTOR.slice(2);
|
|
7016
|
-
const SET_DOCUMENTS_ATTESTED_SELECTOR = BOUNDED_RUNTIME_SET_DOCUMENTS_ATTESTED_SELECTOR.slice(2);
|
|
7017
|
-
const MAX_CALLDATA_BYTES = 1024 * 1024;
|
|
7018
|
-
const MAX_ARRAY_ITEMS = 2048;
|
|
7019
|
-
const BIGINT_ZERO = BigInt(0);
|
|
7020
|
-
const BIGINT_ONE = BigInt(1);
|
|
7021
|
-
const BIGINT_EIGHT = BigInt(8);
|
|
7022
|
-
const SECP256K1_N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
|
7023
|
-
const SECP256K1_HALF_N = BigInt('0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0');
|
|
7024
|
-
function hexBytes(raw) {
|
|
7025
|
-
if (typeof raw !== 'string' || !/^0x[0-9a-fA-F]*$/.test(raw) || raw.length % 2 !== 0) {
|
|
7026
|
-
throw new Error('EVM transaction calldata must be even-length 0x hex');
|
|
7027
|
-
}
|
|
7028
|
-
const hex = raw.slice(2);
|
|
7029
|
-
if (hex.length / 2 > MAX_CALLDATA_BYTES)
|
|
7030
|
-
throw new Error('EVM transaction calldata exceeds the client safety limit');
|
|
7031
|
-
const out = new Uint8Array(hex.length / 2);
|
|
7032
|
-
for (let i = 0; i < out.length; i++)
|
|
7033
|
-
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
7034
|
-
return out;
|
|
7035
|
-
}
|
|
7036
|
-
class AbiReader {
|
|
7037
|
-
constructor(bytes) {
|
|
7038
|
-
this.bytes = bytes;
|
|
7039
|
-
this.maxRead = 0;
|
|
7040
|
-
this.dynamicPayloadBytes = 0;
|
|
7041
|
-
this.utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: false });
|
|
7042
|
-
}
|
|
7043
|
-
range(offset, length, label) {
|
|
7044
|
-
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0 || offset + length > this.bytes.length) {
|
|
7045
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label})`);
|
|
7046
|
-
}
|
|
7047
|
-
this.maxRead = Math.max(this.maxRead, offset + length);
|
|
7048
|
-
}
|
|
7049
|
-
word(offset, label) {
|
|
7050
|
-
this.range(offset, 32, label);
|
|
7051
|
-
let value = BIGINT_ZERO;
|
|
7052
|
-
for (let i = 0; i < 32; i++)
|
|
7053
|
-
value = (value << BIGINT_EIGHT) | BigInt(this.bytes[offset + i]);
|
|
7054
|
-
return value;
|
|
7055
|
-
}
|
|
7056
|
-
numberWord(offset, label, max = Number.MAX_SAFE_INTEGER) {
|
|
7057
|
-
const value = this.word(offset, label);
|
|
7058
|
-
if (value > BigInt(max))
|
|
7059
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label} too large)`);
|
|
7060
|
-
return Number(value);
|
|
7061
|
-
}
|
|
7062
|
-
offset(base, wordOffset, min, label) {
|
|
7063
|
-
const relative = this.numberWord(wordOffset, `${label} offset`, this.bytes.length);
|
|
7064
|
-
if (relative < min || relative % 32 !== 0)
|
|
7065
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label} offset)`);
|
|
7066
|
-
const absolute = base + relative;
|
|
7067
|
-
this.range(absolute, 0, label);
|
|
7068
|
-
return absolute;
|
|
7069
|
-
}
|
|
7070
|
-
dynamicBytes(at, label) {
|
|
7071
|
-
const length = this.numberWord(at, `${label} length`, MAX_CALLDATA_BYTES);
|
|
7072
|
-
this.dynamicPayloadBytes += length;
|
|
7073
|
-
// ABI offsets may alias. Cap the cumulative decoded payload so a malicious
|
|
7074
|
-
// 1 MB tail referenced by thousands of fields cannot amplify into a
|
|
7075
|
-
// multi-gigabyte allocation/UTF-8 decode in the wallet process.
|
|
7076
|
-
if (this.dynamicPayloadBytes > MAX_CALLDATA_BYTES) {
|
|
7077
|
-
throw new Error('Malformed EVM setDocuments calldata (dynamic payload amplification)');
|
|
7078
|
-
}
|
|
7079
|
-
const start = at + 32;
|
|
7080
|
-
const padded = Math.ceil(length / 32) * 32;
|
|
7081
|
-
this.range(start, padded, label);
|
|
7082
|
-
for (let i = start + length; i < start + padded; i++) {
|
|
7083
|
-
if (this.bytes[i] !== 0)
|
|
7084
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label} padding)`);
|
|
7085
|
-
}
|
|
7086
|
-
return this.bytes.slice(start, start + length);
|
|
7087
|
-
}
|
|
7088
|
-
dynamicEnd(at, label) {
|
|
7089
|
-
const length = this.numberWord(at, `${label} length`, MAX_CALLDATA_BYTES);
|
|
7090
|
-
const end = at + 32 + Math.ceil(length / 32) * 32;
|
|
7091
|
-
this.range(at + 32, Math.ceil(length / 32) * 32, label);
|
|
7092
|
-
return end;
|
|
7093
|
-
}
|
|
7094
|
-
string(at, label) {
|
|
7095
|
-
try {
|
|
7096
|
-
return this.utf8.decode(this.dynamicBytes(at, label));
|
|
7097
|
-
}
|
|
7098
|
-
catch (_a) {
|
|
7099
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label} utf8)`);
|
|
7100
|
-
}
|
|
7101
|
-
}
|
|
7102
|
-
finish() {
|
|
7103
|
-
if (this.maxRead !== this.bytes.length)
|
|
7104
|
-
throw new Error('Malformed EVM setDocuments calldata (unreferenced trailing bytes)');
|
|
7105
|
-
}
|
|
7106
|
-
}
|
|
7107
|
-
function assertCanonicalOffset(actual, expected, label) {
|
|
7108
|
-
if (actual !== expected)
|
|
7109
|
-
throw new Error(`Malformed EVM setDocuments calldata (${label} non-canonical offset)`);
|
|
7110
|
-
}
|
|
7111
|
-
function decodeStringArray(reader, at, label) {
|
|
7112
|
-
const count = reader.numberWord(at, `${label} count`, MAX_ARRAY_ITEMS);
|
|
7113
|
-
const head = at + 32;
|
|
7114
|
-
const min = count * 32;
|
|
7115
|
-
const out = [];
|
|
7116
|
-
let expectedTail = head + min;
|
|
7117
|
-
for (let i = 0; i < count; i++) {
|
|
7118
|
-
const itemAt = reader.offset(head, head + i * 32, min, `${label}[${i}]`);
|
|
7119
|
-
assertCanonicalOffset(itemAt, expectedTail, `${label}[${i}]`);
|
|
7120
|
-
out.push(reader.string(itemAt, `${label}[${i}]`));
|
|
7121
|
-
expectedTail = reader.dynamicEnd(itemAt, `${label}[${i}]`);
|
|
7122
|
-
}
|
|
7123
|
-
return { value: out, end: expectedTail };
|
|
7124
|
-
}
|
|
7125
|
-
function decodeFields(reader, at) {
|
|
7126
|
-
const count = reader.numberWord(at, 'fields count', MAX_ARRAY_ITEMS);
|
|
7127
|
-
const head = at + 32;
|
|
7128
|
-
const min = count * 32;
|
|
7129
|
-
const out = [];
|
|
7130
|
-
let expectedTail = head + min;
|
|
7131
|
-
for (let i = 0; i < count; i++) {
|
|
7132
|
-
const tuple = reader.offset(head, head + i * 32, min, `field[${i}]`);
|
|
7133
|
-
assertCanonicalOffset(tuple, expectedTail, `field[${i}]`);
|
|
7134
|
-
const nameAt = reader.offset(tuple, tuple, 128, `field[${i}].name`);
|
|
7135
|
-
assertCanonicalOffset(nameAt, tuple + 128, `field[${i}].name`);
|
|
7136
|
-
const name = reader.string(nameAt, `field[${i}].name`);
|
|
7137
|
-
const nameEnd = reader.dynamicEnd(nameAt, `field[${i}].name`);
|
|
7138
|
-
const ftype = reader.numberWord(tuple + 32, `field[${i}].ftype`, 255);
|
|
7139
|
-
const op = reader.numberWord(tuple + 64, `field[${i}].op`, 255);
|
|
7140
|
-
const valueAt = reader.offset(tuple, tuple + 96, 128, `field[${i}].value`);
|
|
7141
|
-
assertCanonicalOffset(valueAt, nameEnd, `field[${i}].value`);
|
|
7142
|
-
const value = reader.dynamicBytes(valueAt, `field[${i}].value`);
|
|
7143
|
-
expectedTail = reader.dynamicEnd(valueAt, `field[${i}].value`);
|
|
7144
|
-
out.push({ name, ftype, op, value });
|
|
7145
|
-
}
|
|
7146
|
-
return { value: out, end: expectedTail };
|
|
7147
|
-
}
|
|
7148
|
-
function decodeRuntimeWrite(rawData) {
|
|
7149
|
-
const bytes = hexBytes(rawData);
|
|
7150
|
-
if (bytes.length < 68)
|
|
7151
|
-
throw new Error('Malformed EVM setDocuments calldata');
|
|
7152
|
-
const selector = Array.from(bytes.slice(0, 4)).map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
7153
|
-
const kind = selector === SET_DOCUMENTS_SELECTOR
|
|
7154
|
-
? 'setDocuments'
|
|
7155
|
-
: selector === SET_DOCUMENTS_ATTESTED_SELECTOR
|
|
7156
|
-
? 'setDocumentsAttested'
|
|
7157
|
-
: null;
|
|
7158
|
-
if (!kind)
|
|
7159
|
-
throw new Error('EVM transaction is not a supported BoundedRuntime document write');
|
|
7160
|
-
const reader = new AbiReader(bytes);
|
|
7161
|
-
reader.maxRead = 4;
|
|
7162
|
-
const args = 4;
|
|
7163
|
-
// ExternalCallAttestation is a fully-static six-word tuple and is therefore
|
|
7164
|
-
// expanded inline into the top-level head: 2 dynamic offsets + 6 words.
|
|
7165
|
-
const headBytes = kind === 'setDocumentsAttested' ? 256 : 64;
|
|
7166
|
-
const appAt = reader.offset(args, args, headBytes, 'appId');
|
|
7167
|
-
assertCanonicalOffset(appAt, args + headBytes, 'appId');
|
|
7168
|
-
const opsAt = reader.offset(args, args + 32, headBytes, 'ops');
|
|
7169
|
-
const appId = reader.string(appAt, 'appId');
|
|
7170
|
-
const appEnd = reader.dynamicEnd(appAt, 'appId');
|
|
7171
|
-
assertCanonicalOffset(opsAt, appEnd, 'ops');
|
|
7172
|
-
const count = reader.numberWord(opsAt, 'ops count', MAX_ARRAY_ITEMS);
|
|
7173
|
-
const head = opsAt + 32;
|
|
7174
|
-
const min = count * 32;
|
|
7175
|
-
const ops = [];
|
|
7176
|
-
let expectedTail = head + min;
|
|
7177
|
-
for (let i = 0; i < count; i++) {
|
|
7178
|
-
const tuple = reader.offset(head, head + i * 32, min, `op[${i}]`);
|
|
7179
|
-
assertCanonicalOffset(tuple, expectedTail, `op[${i}]`);
|
|
7180
|
-
const pathAt = reader.offset(tuple, tuple, 96, `op[${i}].path`);
|
|
7181
|
-
assertCanonicalOffset(pathAt, tuple + 96, `op[${i}].path`);
|
|
7182
|
-
const deleteWord = reader.numberWord(tuple + 32, `op[${i}].isDelete`, 1);
|
|
7183
|
-
const fieldsAt = reader.offset(tuple, tuple + 64, 96, `op[${i}].fields`);
|
|
7184
|
-
const path = decodeStringArray(reader, pathAt, `op[${i}].path`);
|
|
7185
|
-
assertCanonicalOffset(fieldsAt, path.end, `op[${i}].fields`);
|
|
7186
|
-
const fields = decodeFields(reader, fieldsAt);
|
|
7187
|
-
expectedTail = fields.end;
|
|
7188
|
-
ops.push({
|
|
7189
|
-
pathSegments: path.value,
|
|
7190
|
-
isDelete: deleteWord === 1,
|
|
7191
|
-
fields: fields.value,
|
|
7192
|
-
});
|
|
7193
|
-
}
|
|
7194
|
-
let attestation;
|
|
7195
|
-
if (kind === 'setDocumentsAttested') {
|
|
7196
|
-
const callsHash = reader.word(args + 64, 'attestation.callsHash');
|
|
7197
|
-
const nonce = reader.word(args + 96, 'attestation.nonce');
|
|
7198
|
-
const deadline = reader.word(args + 128, 'attestation.deadline');
|
|
7199
|
-
const v = reader.numberWord(args + 160, 'attestation.v', 255);
|
|
7200
|
-
const r = reader.word(args + 192, 'attestation.r');
|
|
7201
|
-
const s = reader.word(args + 224, 'attestation.s');
|
|
7202
|
-
if (v !== 27 && v !== 28)
|
|
7203
|
-
throw new Error('Malformed EVM setDocuments calldata (attestation.v must be 27 or 28)');
|
|
7204
|
-
if (r === BIGINT_ZERO || r >= SECP256K1_N)
|
|
7205
|
-
throw new Error('Malformed EVM setDocuments calldata (attestation.r is not canonical)');
|
|
7206
|
-
if (s === BIGINT_ZERO || s > SECP256K1_HALF_N)
|
|
7207
|
-
throw new Error('Malformed EVM setDocuments calldata (attestation.s is not canonical low-s)');
|
|
7208
|
-
attestation = { callsHash, nonce, deadline, v, r, s };
|
|
7209
|
-
}
|
|
7210
|
-
if (expectedTail !== bytes.length)
|
|
7211
|
-
throw new Error('Malformed EVM setDocuments calldata (unreferenced trailing bytes)');
|
|
7212
|
-
reader.finish();
|
|
7213
|
-
return Object.assign({ kind, appId, ops }, (attestation ? { attestation } : {}));
|
|
7214
|
-
}
|
|
7215
|
-
/**
|
|
7216
|
-
* Fail closed unless calldata is a byte-for-byte canonical ABI encoding of one
|
|
7217
|
-
* supported BoundedRuntime write. This is the defense used by direct signer
|
|
7218
|
-
* surfaces; the higher-level write validator below additionally binds app/ops.
|
|
7219
|
-
*/
|
|
7220
|
-
function assertCanonicalBoundedRuntimeWriteCalldata(rawData) {
|
|
7221
|
-
decodeRuntimeWrite(rawData);
|
|
7222
|
-
}
|
|
7223
|
-
const MAX_EVM_DOCUMENT_PATH_UTF8_BYTES = 1024;
|
|
7224
|
-
function canonicalUtf8ByteLength(value) {
|
|
7225
|
-
let bytes = 0;
|
|
7226
|
-
for (let i = 0; i < value.length; i++) {
|
|
7227
|
-
const unit = value.charCodeAt(i);
|
|
7228
|
-
if (unit <= 0x7f)
|
|
7229
|
-
bytes += 1;
|
|
7230
|
-
else if (unit <= 0x7ff)
|
|
7231
|
-
bytes += 2;
|
|
7232
|
-
else if (unit >= 0xd800 && unit <= 0xdbff) {
|
|
7233
|
-
const low = value.charCodeAt(++i);
|
|
7234
|
-
if (!(low >= 0xdc00 && low <= 0xdfff))
|
|
7235
|
-
throw new Error(`Invalid EVM write path: ${value}`);
|
|
7236
|
-
bytes += 4;
|
|
7237
|
-
}
|
|
7238
|
-
else if (unit >= 0xdc00 && unit <= 0xdfff) {
|
|
7239
|
-
throw new Error(`Invalid EVM write path: ${value}`);
|
|
7240
|
-
}
|
|
7241
|
-
else
|
|
7242
|
-
bytes += 3;
|
|
7243
|
-
}
|
|
7244
|
-
return bytes;
|
|
7245
|
-
}
|
|
7246
|
-
function normalizedPathSegments(path) {
|
|
7247
|
-
if (typeof path !== 'string' || path.length === 0 ||
|
|
7248
|
-
canonicalUtf8ByteLength(path) > MAX_EVM_DOCUMENT_PATH_UTF8_BYTES) {
|
|
7249
|
-
throw new Error(`Invalid EVM write path: ${path}`);
|
|
7250
|
-
}
|
|
7251
|
-
const segments = path.split('/');
|
|
7252
|
-
if (segments.length < 2 || segments.length % 2 !== 0 ||
|
|
7253
|
-
segments[0].startsWith('__') ||
|
|
7254
|
-
segments.some((s) => !s || s === '.' || s === '..' || /[\\\u0000-\u001f\u007f]/.test(s))) {
|
|
7255
|
-
throw new Error(`Invalid EVM write path: ${path}`);
|
|
7256
|
-
}
|
|
7257
|
-
return segments;
|
|
7258
|
-
}
|
|
7259
|
-
function bytesBigInt(bytes, signed) {
|
|
7260
|
-
if (bytes.length !== 32)
|
|
7261
|
-
throw new Error('EVM field value is not one ABI word');
|
|
7262
|
-
let value = BIGINT_ZERO;
|
|
7263
|
-
for (const b of bytes)
|
|
7264
|
-
value = (value << BIGINT_EIGHT) | BigInt(b);
|
|
7265
|
-
if (signed && (value & (BIGINT_ONE << BigInt(255))) !== BIGINT_ZERO)
|
|
7266
|
-
value -= BIGINT_ONE << BigInt(256);
|
|
7267
|
-
return value;
|
|
7268
|
-
}
|
|
7269
|
-
function decodedAbiString(bytes) {
|
|
7270
|
-
const reader = new AbiReader(bytes);
|
|
7271
|
-
const at = reader.offset(0, 0, 32, 'field string');
|
|
7272
|
-
const value = reader.string(at, 'field string');
|
|
7273
|
-
reader.finish();
|
|
7274
|
-
return value;
|
|
7275
|
-
}
|
|
7276
|
-
function assertSetValue(field, expected) {
|
|
7277
|
-
if (field.op !== 0)
|
|
7278
|
-
throw new Error(`EVM field ${field.name} changes the requested SET action`);
|
|
7279
|
-
if (typeof expected === 'boolean') {
|
|
7280
|
-
if (field.ftype !== 3 || bytesBigInt(field.value, false) !== (expected ? BIGINT_ONE : BIGINT_ZERO))
|
|
7281
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7282
|
-
return;
|
|
7283
|
-
}
|
|
7284
|
-
if (typeof expected === 'number') {
|
|
7285
|
-
if (!Number.isFinite(expected))
|
|
7286
|
-
throw new Error(`EVM field ${field.name} is not finite`);
|
|
7287
|
-
if (Number.isInteger(expected)) {
|
|
7288
|
-
if (!Number.isSafeInteger(expected)) {
|
|
7289
|
-
throw new Error(`EVM field ${field.name} integer must be a safe number or canonical decimal string`);
|
|
7290
|
-
}
|
|
7291
|
-
const actual = field.ftype === 1 ? bytesBigInt(field.value, false) : field.ftype === 2 ? bytesBigInt(field.value, true) : null;
|
|
7292
|
-
if (actual === null || actual !== BigInt(expected))
|
|
7293
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7294
|
-
return;
|
|
7295
|
-
}
|
|
7296
|
-
if (field.ftype !== 4 || decodedAbiString(field.value) !== expected.toString())
|
|
7297
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7298
|
-
return;
|
|
7299
|
-
}
|
|
7300
|
-
if (typeof expected === 'bigint') {
|
|
7301
|
-
const actual = field.ftype === 1 ? bytesBigInt(field.value, false) : field.ftype === 2 ? bytesBigInt(field.value, true) : null;
|
|
7302
|
-
if (actual === null || actual !== expected)
|
|
7303
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7304
|
-
return;
|
|
7305
|
-
}
|
|
7306
|
-
if (typeof expected === 'string') {
|
|
7307
|
-
if (field.ftype === 4 && decodedAbiString(field.value) === expected)
|
|
7308
|
-
return;
|
|
7309
|
-
if ((field.ftype === 1 || field.ftype === 2) && expected.length <= 20 && /^-?(?:0|[1-9][0-9]*)$/.test(expected)) {
|
|
7310
|
-
const exact = BigInt(expected);
|
|
7311
|
-
if (exact >= BigInt(Number.MIN_SAFE_INTEGER) && exact <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
7312
|
-
throw new Error(`EVM field ${field.name} safe integer must be supplied as a number`);
|
|
7313
|
-
}
|
|
7314
|
-
const inRange = field.ftype === 1
|
|
7315
|
-
? exact >= BIGINT_ZERO && exact <= ((BIGINT_ONE << BigInt(64)) - BIGINT_ONE)
|
|
7316
|
-
: exact >= -(BIGINT_ONE << BigInt(63)) && exact <= ((BIGINT_ONE << BigInt(63)) - BIGINT_ONE);
|
|
7317
|
-
const actual = bytesBigInt(field.value, field.ftype === 2);
|
|
7318
|
-
if (inRange && actual === exact)
|
|
7319
|
-
return;
|
|
7320
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7321
|
-
}
|
|
7322
|
-
if (field.ftype === 5 && normalizeEvmAddress(expected)) {
|
|
7323
|
-
const word = field.value;
|
|
7324
|
-
if (word.length === 32 && word.slice(0, 12).every((b) => b === 0)) {
|
|
7325
|
-
const actual = `0x${Array.from(word.slice(12)).map((b) => b.toString(16).padStart(2, '0')).join('')}`;
|
|
7326
|
-
if (actual === normalizeEvmAddress(expected))
|
|
7327
|
-
return;
|
|
7328
|
-
}
|
|
7329
|
-
}
|
|
7330
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7331
|
-
}
|
|
7332
|
-
if (expected === undefined || expected === null) {
|
|
7333
|
-
throw new Error(`EVM field ${field.name} cannot be represented safely on EVM; use a document delete or omit the field`);
|
|
7334
|
-
}
|
|
7335
|
-
const json = JSON.stringify(expected);
|
|
7336
|
-
if (typeof json !== 'string' || field.ftype !== 4 || decodedAbiString(field.value) !== json) {
|
|
7337
|
-
throw new Error(`EVM field ${field.name} value mismatch`);
|
|
7338
|
-
}
|
|
7339
|
-
}
|
|
7340
|
-
function assertField(field, expected) {
|
|
7341
|
-
if (expected === null) {
|
|
7342
|
-
if (field.op !== 2 || field.ftype < 1 || field.ftype > 5 || field.value.length !== 0) {
|
|
7343
|
-
throw new Error(`EVM field ${field.name} deletion mismatch`);
|
|
7344
|
-
}
|
|
7345
|
-
return;
|
|
7346
|
-
}
|
|
7347
|
-
if (expected && typeof expected === 'object' && !Array.isArray(expected)) {
|
|
7348
|
-
const operation = expected.operation;
|
|
7349
|
-
if (operation === 'increment') {
|
|
7350
|
-
const delta = expected.value;
|
|
7351
|
-
const actual = field.ftype === 1 || field.ftype === 2
|
|
7352
|
-
? bytesBigInt(field.value, true)
|
|
7353
|
-
: null;
|
|
7354
|
-
if (!Number.isSafeInteger(delta) || field.op !== 3 || actual === null || actual !== BigInt(delta)) {
|
|
7355
|
-
throw new Error(`EVM field ${field.name} increment mismatch`);
|
|
7356
|
-
}
|
|
7357
|
-
return;
|
|
7358
|
-
}
|
|
7359
|
-
if (operation === 'time' && expected.value === 'now') {
|
|
7360
|
-
if (field.op !== 4 || (field.ftype !== 1 && field.ftype !== 2) || field.value.length !== 0) {
|
|
7361
|
-
throw new Error(`EVM field ${field.name} timestamp mismatch`);
|
|
7362
|
-
}
|
|
7363
|
-
return;
|
|
7364
|
-
}
|
|
7365
|
-
}
|
|
7366
|
-
assertSetValue(field, expected);
|
|
7367
|
-
}
|
|
7368
|
-
function assertOp(decoded, path, document) {
|
|
7369
|
-
const expectedSegments = normalizedPathSegments(path);
|
|
7370
|
-
if (decoded.pathSegments.length !== expectedSegments.length || decoded.pathSegments.some((s, i) => s !== expectedSegments[i])) {
|
|
7371
|
-
throw new Error(`EVM transaction path does not match requested write: ${path}`);
|
|
7372
|
-
}
|
|
7373
|
-
if (decoded.isDelete !== (document === null))
|
|
7374
|
-
throw new Error(`EVM transaction action does not match requested write: ${path}`);
|
|
7375
|
-
if (document === null) {
|
|
7376
|
-
if (decoded.fields.length !== 0)
|
|
7377
|
-
throw new Error(`EVM delete unexpectedly carries fields: ${path}`);
|
|
7378
|
-
return;
|
|
7379
|
-
}
|
|
7380
|
-
if (!document || typeof document !== 'object' || Array.isArray(document))
|
|
7381
|
-
throw new Error(`EVM document must be a plain object or null: ${path}`);
|
|
7382
|
-
const expectedEntries = Object.entries(document);
|
|
7383
|
-
if (expectedEntries.some(([name]) => name.startsWith('_')))
|
|
7384
|
-
throw new Error(`Reserved EVM field reached transaction validation: ${path}`);
|
|
7385
|
-
const expected = new Map(expectedEntries);
|
|
7386
|
-
const seen = new Set();
|
|
7387
|
-
for (const field of decoded.fields) {
|
|
7388
|
-
if (!field.name || seen.has(field.name) || !expected.has(field.name))
|
|
7389
|
-
throw new Error(`EVM transaction contains unexpected or duplicate field ${field.name}`);
|
|
7390
|
-
seen.add(field.name);
|
|
7391
|
-
assertField(field, expected.get(field.name));
|
|
7392
|
-
}
|
|
7393
|
-
const missing = [...expected.keys()].filter((name) => !seen.has(name));
|
|
7394
|
-
if (missing.length > 0)
|
|
7395
|
-
throw new Error(`EVM transaction omits requested fields: ${missing.join(', ')}`);
|
|
7396
|
-
}
|
|
7397
|
-
function zeroWei(raw) {
|
|
7398
|
-
if (raw === undefined || raw === null || raw === '')
|
|
7399
|
-
return true;
|
|
7400
|
-
if (typeof raw !== 'string' && typeof raw !== 'number')
|
|
7401
|
-
return false;
|
|
7402
|
-
try {
|
|
7403
|
-
return BigInt(raw) === BIGINT_ZERO;
|
|
7404
|
-
}
|
|
7405
|
-
catch (_a) {
|
|
7406
|
-
return false;
|
|
7407
|
-
}
|
|
7408
|
-
}
|
|
7409
|
-
/** Verify the untrusted 202 payload is exactly the governed write the app asked for. */
|
|
7410
|
-
function assertEvmTransactionMatchesWrite(transaction, context) {
|
|
7411
|
-
var _a, _b;
|
|
7412
|
-
if (!transaction || !Number.isSafeInteger(transaction.chainId) || transaction.chainId <= 0) {
|
|
7413
|
-
throw new Error('EVM transaction chainId is required');
|
|
7414
|
-
}
|
|
7415
|
-
const chainId = transaction.chainId;
|
|
7416
|
-
const policy = context.policy;
|
|
7417
|
-
if (!policy)
|
|
7418
|
-
throw new Error('EVM transaction signing requires an explicit runtime/chain policy');
|
|
7419
|
-
if (policy.chainId !== undefined && policy.chainId !== chainId)
|
|
7420
|
-
throw new Error(`EVM transaction chain ${chainId} does not match configured chain ${policy.chainId}`);
|
|
7421
|
-
const expectedRuntime = (_b = (_a = policy.runtimeAddresses) === null || _a === void 0 ? void 0 : _a[String(chainId)]) !== null && _b !== void 0 ? _b : (policy.chainId === chainId ? policy.runtimeAddress : undefined);
|
|
7422
|
-
const normalizedRuntime = normalizeEvmAddress(expectedRuntime);
|
|
7423
|
-
if (!normalizedRuntime)
|
|
7424
|
-
throw new Error(`No trusted BoundedRuntime address configured for EVM chain ${chainId}`);
|
|
7425
|
-
if (normalizeEvmAddress(transaction.to) !== normalizedRuntime)
|
|
7426
|
-
throw new Error('EVM transaction target is not the configured BoundedRuntime');
|
|
7427
|
-
if (!zeroWei(transaction.value))
|
|
7428
|
-
throw new Error('BoundedRuntime.setDocuments must not transfer native value');
|
|
7429
|
-
if (transaction.nonce !== undefined)
|
|
7430
|
-
throw new Error('Server-supplied EVM nonce is not accepted');
|
|
7431
|
-
const decoded = decodeRuntimeWrite(transaction.data);
|
|
7432
|
-
if (decoded.appId !== context.appId)
|
|
7433
|
-
throw new Error('EVM transaction appId does not match configured appId');
|
|
7434
|
-
if (decoded.ops.length !== context.documents.length)
|
|
7435
|
-
throw new Error('EVM transaction operation count does not match requested writes');
|
|
7436
|
-
// The worker's contract ABI groups upserts first and document deletes second.
|
|
7437
|
-
const expectedOps = [
|
|
7438
|
-
...context.documents.filter((entry) => entry.document !== null),
|
|
7439
|
-
...context.documents.filter((entry) => entry.document === null),
|
|
7440
|
-
];
|
|
7441
|
-
const paths = new Set();
|
|
7442
|
-
for (let i = 0; i < expectedOps.length; i++) {
|
|
7443
|
-
const expected = expectedOps[i];
|
|
7444
|
-
const canonicalPath = normalizedPathSegments(expected.destinationPath).join('/');
|
|
7445
|
-
if (paths.has(canonicalPath))
|
|
7446
|
-
throw new Error(`Duplicate EVM write path is not allowed: ${canonicalPath}`);
|
|
7447
|
-
paths.add(canonicalPath);
|
|
7448
|
-
assertOp(decoded.ops[i], canonicalPath, expected.document);
|
|
7449
|
-
}
|
|
7450
|
-
}
|
|
7451
|
-
|
|
7452
7043
|
/**
|
|
7453
7044
|
* Thrown when a user's wallet doesn't have enough SOL to cover the transaction.
|
|
7454
7045
|
* Apps can catch this to trigger an onramp/funding flow.
|
|
@@ -8564,12 +8155,12 @@ async function set(path, document, options) {
|
|
|
8564
8155
|
* state appears only under `observation`/`mirrorSync` once actually observed.
|
|
8565
8156
|
*/
|
|
8566
8157
|
async function setMany(many, options) {
|
|
8567
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l
|
|
8158
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
8568
8159
|
// Returns the data that was set, or undefined if the document was already set.
|
|
8569
8160
|
try {
|
|
8570
8161
|
// A server WalletClient threads its immutable config snapshot; the default
|
|
8571
8162
|
// path reads the mutable process-global init() config. This keeps the app-id
|
|
8572
|
-
// bound into an on-chain 202 write (
|
|
8163
|
+
// bound into an on-chain 202 write (Solana/offchain) on the wallet
|
|
8573
8164
|
// client's original app after a later global init() (CTA-05).
|
|
8574
8165
|
const config = (_b = (_a = options === null || options === void 0 ? void 0 : options._overrides) === null || _a === void 0 ? void 0 : _a._config) !== null && _b !== void 0 ? _b : await getConfig();
|
|
8575
8166
|
const authProvider = (_d = (_c = options === null || options === void 0 ? void 0 : options._overrides) === null || _c === void 0 ? void 0 : _c.authProvider) !== null && _d !== void 0 ? _d : config.authProvider;
|
|
@@ -8638,13 +8229,18 @@ async function setMany(many, options) {
|
|
|
8638
8229
|
throw new Error(`Expected exactly one on-chain transaction, received ${Array.isArray(transactions) ? transactions.length : 0}`);
|
|
8639
8230
|
}
|
|
8640
8231
|
const curTx = transactions[0];
|
|
8641
|
-
|
|
8232
|
+
// The EVM onchain data plane is retired: no worker mints an EVM 202 any
|
|
8233
|
+
// more. A payload that still carries one is an unexpected response and
|
|
8234
|
+
// fails loudly BEFORE anything is signed - never silently ignored.
|
|
8235
|
+
if ((curTx === null || curTx === void 0 ? void 0 : curTx.evmTransaction) != null) {
|
|
8236
|
+
throw new Error('Unsupported 202 transaction shape: the write response carried an evmTransaction, ' +
|
|
8237
|
+
'but EVM onchain writes are no longer supported. Nothing was signed or submitted.');
|
|
8238
|
+
}
|
|
8642
8239
|
const hasSerializedSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.serializedTransaction) != null;
|
|
8643
8240
|
const hasBuiltSolanaTarget = (curTx === null || curTx === void 0 ? void 0 : curTx.transactionArgs) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.remainingAccounts) != null ||
|
|
8644
8241
|
(curTx === null || curTx === void 0 ? void 0 : curTx.idl) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.txData) != null || (curTx === null || curTx === void 0 ? void 0 : curTx.preInstructions) != null;
|
|
8645
|
-
if (
|
|
8646
|
-
(
|
|
8647
|
-
throw new Error('Ambiguous on-chain write response mixes EVM and/or Solana transaction target formats');
|
|
8242
|
+
if (hasSerializedSolanaTarget && hasBuiltSolanaTarget) {
|
|
8243
|
+
throw new Error('Ambiguous on-chain write response mixes Solana transaction target formats');
|
|
8648
8244
|
}
|
|
8649
8245
|
const expectedWritePaths = documents.map(d => d.destinationPath);
|
|
8650
8246
|
const requestedDocuments = buildRequestedDocuments(documents);
|
|
@@ -8657,67 +8253,13 @@ async function setMany(many, options) {
|
|
|
8657
8253
|
throw new Error('The write response carried no execution-chain tag, so this receipt could not state where the write ran. ' +
|
|
8658
8254
|
'Nothing was signed or submitted.');
|
|
8659
8255
|
}
|
|
8660
|
-
|
|
8661
|
-
// `transactions[0].evmTransaction` (the EVM twin of Solana's pre-built
|
|
8662
|
-
// `serializedTransaction`). WORKER 202 CONTRACT (field name + shape the
|
|
8663
|
-
// worker side must emit to match):
|
|
8664
|
-
// { transactions: [ { evmTransaction: {
|
|
8665
|
-
// to, data, value?, chainId, gas?, maxFeePerGas?, maxPriorityFeePerGas?, nonce?
|
|
8666
|
-
// } } ] }
|
|
8667
|
-
// We hand it to the EVM auth provider's runTransaction(evmTx, undefined, options)
|
|
8668
|
-
// (eth_sendTransaction — the wallet signs + submits). syncItems below is
|
|
8669
|
-
// identical to the Solana path.
|
|
8670
|
-
if (curTx.evmTransaction) {
|
|
8671
|
-
assertEvmTransactionMatchesWrite(curTx.evmTransaction, {
|
|
8672
|
-
appId: config.appId,
|
|
8673
|
-
documents,
|
|
8674
|
-
policy: (_o = (_m = (_l = options === null || options === void 0 ? void 0 : options._overrides) === null || _l === void 0 ? void 0 : _l._evmTransactionPolicy) !== null && _m !== void 0 ? _m : config.evmWalletConfig) !== null && _o !== void 0 ? _o : null,
|
|
8675
|
-
});
|
|
8676
|
-
const evmChain = workerChain;
|
|
8677
|
-
const evmOutcome = await handleEvmTransaction(curTx.evmTransaction, authProvider, options);
|
|
8678
|
-
// CAPTURE THE RECEIPT FIRST. Mirror synchronization comes after, and its
|
|
8679
|
-
// failure can never turn a landed transaction into a thrown error that
|
|
8680
|
-
// invites the caller to submit the same write twice.
|
|
8681
|
-
if (evmOutcome.outcome === 'confirmed') {
|
|
8682
|
-
const mirrorSync = await attemptMirrorSync(expectedWritePaths, options);
|
|
8683
|
-
return {
|
|
8684
|
-
status: 'confirmed', appId, requestedDocuments, chain: evmChain,
|
|
8685
|
-
transactionId: evmOutcome.transactionSignature,
|
|
8686
|
-
blockNumber: evmOutcome.blockNumber,
|
|
8687
|
-
mirrorSync,
|
|
8688
|
-
};
|
|
8689
|
-
}
|
|
8690
|
-
// An unknown confirmation outcome is NOT a mirror question: nothing is
|
|
8691
|
-
// known to have landed, so no synchronization is attempted.
|
|
8692
|
-
if (evmOutcome.transactionSignature === null) {
|
|
8693
|
-
return {
|
|
8694
|
-
status: 'submitted', appId, requestedDocuments, chain: evmChain,
|
|
8695
|
-
transactionId: null,
|
|
8696
|
-
confirmation: { state: 'unknown', reason: evmOutcome.reason, reconcilable: false },
|
|
8697
|
-
};
|
|
8698
|
-
}
|
|
8699
|
-
if ('fence' in evmOutcome && evmOutcome.fence) {
|
|
8700
|
-
return {
|
|
8701
|
-
status: 'submitted', appId, requestedDocuments, chain: evmChain,
|
|
8702
|
-
transactionId: evmOutcome.transactionSignature,
|
|
8703
|
-
fence: evmOutcome.fence,
|
|
8704
|
-
confirmation: { state: 'unknown', reason: evmOutcome.reason },
|
|
8705
|
-
};
|
|
8706
|
-
}
|
|
8707
|
-
return {
|
|
8708
|
-
status: 'submitted', appId, requestedDocuments, chain: evmChain,
|
|
8709
|
-
transactionId: evmOutcome.transactionSignature,
|
|
8710
|
-
rebroadcastable: false,
|
|
8711
|
-
confirmation: { state: 'unknown', reason: evmOutcome.reason },
|
|
8712
|
-
};
|
|
8713
|
-
}
|
|
8714
|
-
else if (curTx.serializedTransaction) {
|
|
8256
|
+
if (curTx.serializedTransaction) {
|
|
8715
8257
|
if (!SOLANA_CHAIN_TAGS.has(workerChain)) {
|
|
8716
8258
|
throw new Error(`The write response reported execution chain "${workerChain}" for a Solana transaction. ` +
|
|
8717
8259
|
'Nothing was signed or submitted.');
|
|
8718
8260
|
}
|
|
8719
8261
|
const solanaChain = workerChain;
|
|
8720
|
-
const { handlePreBuiltTransaction } = await import('./solana-operations-
|
|
8262
|
+
const { handlePreBuiltTransaction } = await import('./solana-operations-ByMDnjog.mjs');
|
|
8721
8263
|
const solanaOutcome = await handlePreBuiltTransaction(curTx, authProvider, options, expectedWritePaths);
|
|
8722
8264
|
// `shouldSubmitTx: false` signed WITHOUT sending — no signature exists,
|
|
8723
8265
|
// and there is nothing to mirror, so no synchronization is attempted.
|
|
@@ -8751,7 +8293,7 @@ async function setMany(many, options) {
|
|
|
8751
8293
|
};
|
|
8752
8294
|
}
|
|
8753
8295
|
else {
|
|
8754
|
-
throw new Error('Unsupported 202 transaction shape: expected serializedTransaction
|
|
8296
|
+
throw new Error('Unsupported 202 transaction shape: expected serializedTransaction');
|
|
8755
8297
|
}
|
|
8756
8298
|
}
|
|
8757
8299
|
else if (setResponse.status === 200) {
|
|
@@ -8772,7 +8314,7 @@ async function setMany(many, options) {
|
|
|
8772
8314
|
observation: observationFromDirectCommit(documents.map(d => d.destinationPath), receipt === null || receipt === void 0 ? void 0 : receipt.observation),
|
|
8773
8315
|
// A worker predating the batch receipt reports no lane. Say so rather
|
|
8774
8316
|
// than inventing one — and never discard a committed write's receipt.
|
|
8775
|
-
chain: ((
|
|
8317
|
+
chain: ((_l = readWorkerChainTag(receipt)) !== null && _l !== void 0 ? _l : 'unknown'),
|
|
8776
8318
|
transactionId: typeof rawTransactionId === 'string' && rawTransactionId.length > 0 ? rawTransactionId : null,
|
|
8777
8319
|
};
|
|
8778
8320
|
}
|
|
@@ -8783,18 +8325,6 @@ async function setMany(many, options) {
|
|
|
8783
8325
|
catch (error) {
|
|
8784
8326
|
throw error;
|
|
8785
8327
|
}
|
|
8786
|
-
// EVM onchain write: hand the worker's pre-built EIP-1559 tx to the EVM auth
|
|
8787
|
-
// provider, which eth_sendTransaction-s it (the wallet signs + submits in one
|
|
8788
|
-
// step). Mirrors handlePreBuiltTransaction's shape but routes through the EVM
|
|
8789
|
-
// slot of runTransaction (Solana slot undefined). shouldSubmitTx is honored by
|
|
8790
|
-
// the provider itself.
|
|
8791
|
-
async function handleEvmTransaction(evmTransaction, authProvider, options) {
|
|
8792
|
-
const result = await authProvider.runTransaction(evmTransaction, undefined, options);
|
|
8793
|
-
if ((result === null || result === void 0 ? void 0 : result.lane) !== 'evm') {
|
|
8794
|
-
throw new Error('The EVM auth provider returned a non-EVM transaction outcome');
|
|
8795
|
-
}
|
|
8796
|
-
return result;
|
|
8797
|
-
}
|
|
8798
8328
|
}
|
|
8799
8329
|
/**
|
|
8800
8330
|
* Extract the document/collection path from a cache key. Keys are
|
|
@@ -9048,7 +8578,6 @@ var operations = /*#__PURE__*/Object.freeze({
|
|
|
9048
8578
|
const DEFAULT_TIMEOUT_MS = 60000;
|
|
9049
8579
|
const DEFAULT_MAX_ATTEMPTS = 20;
|
|
9050
8580
|
const DEFAULT_POLL_INTERVAL_MS = 3000;
|
|
9051
|
-
const DEFAULT_MIN_REBROADCAST_INTERVAL_MS = 15000;
|
|
9052
8581
|
/** Mirror read after a late confirmation. No settle delay: the tx already landed. */
|
|
9053
8582
|
const RECONCILE_MIRROR_SETTLE_MS = 0;
|
|
9054
8583
|
function errorText(error) {
|
|
@@ -9058,12 +8587,6 @@ function errorText(error) {
|
|
|
9058
8587
|
function isSolanaSubmitted(receipt) {
|
|
9059
8588
|
return receipt.status === 'submitted' && 'signedTransaction' in receipt;
|
|
9060
8589
|
}
|
|
9061
|
-
function isEvmFencedSubmitted(receipt) {
|
|
9062
|
-
return receipt.status === 'submitted' && 'fence' in receipt && !('signedTransaction' in receipt);
|
|
9063
|
-
}
|
|
9064
|
-
function isEvmHashKnownSubmitted(receipt) {
|
|
9065
|
-
return receipt.status === 'submitted' && receipt.rebroadcastable === false;
|
|
9066
|
-
}
|
|
9067
8590
|
/**
|
|
9068
8591
|
* The Solana execution lane a client config REQUIRES, or `null` when the config
|
|
9069
8592
|
* carries no constraint. Only a positive contradiction rejects a receipt — an
|
|
@@ -9088,7 +8611,6 @@ function requiredSolanaChain(config) {
|
|
|
9088
8611
|
* a global re-`init()` could otherwise poll the wrong app's endpoints entirely.
|
|
9089
8612
|
*/
|
|
9090
8613
|
function assertReceiptMatchesContext(receipt, config) {
|
|
9091
|
-
var _a;
|
|
9092
8614
|
if (!receipt.appId || typeof receipt.appId !== 'string') {
|
|
9093
8615
|
throw new Error('This write receipt carries no appId and cannot be reconciled.');
|
|
9094
8616
|
}
|
|
@@ -9101,22 +8623,6 @@ function assertReceiptMatchesContext(receipt, config) {
|
|
|
9101
8623
|
if (required && required !== receipt.chain) {
|
|
9102
8624
|
throw new Error(`This write receipt ran on "${receipt.chain}" but the bound client targets "${required}".`);
|
|
9103
8625
|
}
|
|
9104
|
-
return;
|
|
9105
|
-
}
|
|
9106
|
-
const fence = receipt.fence;
|
|
9107
|
-
if (fence && typeof fence.chainId === 'number') {
|
|
9108
|
-
const evm = config.evmWalletConfig;
|
|
9109
|
-
const configured = new Set();
|
|
9110
|
-
if (evm && typeof evm.chainId === 'number')
|
|
9111
|
-
configured.add(evm.chainId);
|
|
9112
|
-
for (const id of Object.keys((_a = evm === null || evm === void 0 ? void 0 : evm.runtimeAddresses) !== null && _a !== void 0 ? _a : {})) {
|
|
9113
|
-
const parsed = Number(id);
|
|
9114
|
-
if (Number.isSafeInteger(parsed))
|
|
9115
|
-
configured.add(parsed);
|
|
9116
|
-
}
|
|
9117
|
-
if (configured.size > 0 && !configured.has(fence.chainId)) {
|
|
9118
|
-
throw new Error(`This write receipt ran on EVM chain ${fence.chainId}, which the bound client does not configure.`);
|
|
9119
|
-
}
|
|
9120
8626
|
}
|
|
9121
8627
|
}
|
|
9122
8628
|
function stillSubmitted(receipt, reason) {
|
|
@@ -9233,67 +8739,18 @@ async function reconcileSolana(receipt, config, options) {
|
|
|
9233
8739
|
};
|
|
9234
8740
|
}
|
|
9235
8741
|
/**
|
|
9236
|
-
*
|
|
9237
|
-
* client reconciles with its own identity and RPC. There is deliberately NO
|
|
9238
|
-
* EVM retry-safe terminal state: nonce advancement is not proof the write did
|
|
9239
|
-
* not execute, and plain JSON-RPC offers no transaction-by-sender-and-nonce
|
|
9240
|
-
* lookup, so a `submitted` EVM receipt either confirms or stays `submitted`.
|
|
9241
|
-
*/
|
|
9242
|
-
async function reconcileEvm(receipt, provider, options) {
|
|
9243
|
-
if (!provider || typeof provider.reconcileEvmTransaction !== 'function') {
|
|
9244
|
-
return stillSubmitted(receipt, 'no EVM reconciliation transport is available on the bound auth provider');
|
|
9245
|
-
}
|
|
9246
|
-
const fenced = isEvmFencedSubmitted(receipt) ? receipt.fence : undefined;
|
|
9247
|
-
const allowRebroadcast = Boolean(fenced) && (options === null || options === void 0 ? void 0 : options.allowRebroadcast) !== false;
|
|
9248
|
-
let outcome;
|
|
9249
|
-
try {
|
|
9250
|
-
outcome = await provider.reconcileEvmTransaction({
|
|
9251
|
-
transactionHash: receipt.transactionId,
|
|
9252
|
-
fence: fenced,
|
|
9253
|
-
allowRebroadcast,
|
|
9254
|
-
});
|
|
9255
|
-
}
|
|
9256
|
-
catch (error) {
|
|
9257
|
-
return stillSubmitted(receipt, `EVM reconciliation failed: ${errorText(error)}`);
|
|
9258
|
-
}
|
|
9259
|
-
if (outcome.state === 'failed') {
|
|
9260
|
-
// Landed and reverted: a genuine failure, never `submitted`.
|
|
9261
|
-
throw new Error(outcome.reason);
|
|
9262
|
-
}
|
|
9263
|
-
if (outcome.state === 'confirmed') {
|
|
9264
|
-
if (typeof outcome.blockNumber !== 'number') {
|
|
9265
|
-
return stillSubmitted(receipt, 'the EVM receipt reported success without a block number, so confirmation could not be stated');
|
|
9266
|
-
}
|
|
9267
|
-
const confirmed = {
|
|
9268
|
-
status: 'confirmed',
|
|
9269
|
-
appId: receipt.appId,
|
|
9270
|
-
requestedDocuments: receipt.requestedDocuments,
|
|
9271
|
-
chain: receipt.chain,
|
|
9272
|
-
transactionId: receipt.transactionId,
|
|
9273
|
-
blockNumber: outcome.blockNumber,
|
|
9274
|
-
mirrorSync: await mirrorFor(receipt, options),
|
|
9275
|
-
};
|
|
9276
|
-
return confirmed;
|
|
9277
|
-
}
|
|
9278
|
-
if (outcome.state === 'absent_after_expiry') {
|
|
9279
|
-
// Defensive: no EVM lane may report a retry-safe terminal state.
|
|
9280
|
-
return stillSubmitted(receipt, 'the EVM transport reported absence, which is not proof an EVM write did not execute');
|
|
9281
|
-
}
|
|
9282
|
-
return stillSubmitted(receipt, outcome.reason);
|
|
9283
|
-
}
|
|
9284
|
-
/**
|
|
9285
|
-
* Poll the SAME signature/hash a `submitted` receipt carries and return the
|
|
8742
|
+
* Poll the SAME signature a `submitted` receipt carries and return the
|
|
9286
8743
|
* resulting `SetResult`.
|
|
9287
8744
|
*
|
|
9288
8745
|
* - Terminal receipts (`committed`, `confirmed`, `expired_not_landed`,
|
|
9289
8746
|
* `signed`) are returned unchanged; nothing was left open.
|
|
9290
|
-
* -
|
|
9291
|
-
* rejected immediately: no identifier exists to poll.
|
|
8747
|
+
* - A `submitted` receipt with no transaction identifier (`transactionId:
|
|
8748
|
+
* null`) is rejected immediately: no identifier exists to poll.
|
|
9292
8749
|
* - A transaction that landed and FAILED throws, exactly as it would have on
|
|
9293
8750
|
* the original write.
|
|
9294
8751
|
*/
|
|
9295
8752
|
async function reconcileSetResult(receipt, options) {
|
|
9296
|
-
var _a, _b
|
|
8753
|
+
var _a, _b;
|
|
9297
8754
|
if (!receipt || typeof receipt !== 'object' || typeof receipt.status !== 'string') {
|
|
9298
8755
|
throw new Error('reconcileSetResult requires a write receipt returned by set/setMany.');
|
|
9299
8756
|
}
|
|
@@ -9309,23 +8766,21 @@ async function reconcileSetResult(receipt, options) {
|
|
|
9309
8766
|
if (isSolanaSubmitted(receipt)) {
|
|
9310
8767
|
return reconcileSolana(receipt, config, options);
|
|
9311
8768
|
}
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
return reconcileEvm(receipt, provider, options);
|
|
9315
|
-
}
|
|
8769
|
+
// Anything else (e.g. a legacy serialized receipt from a retired lane) has no
|
|
8770
|
+
// reconciliation transport any more; say so rather than guessing.
|
|
9316
8771
|
return stillSubmitted(receipt, 'this receipt shape has no reconciliation lane');
|
|
9317
8772
|
}
|
|
9318
8773
|
/**
|
|
9319
8774
|
* Reconcile repeatedly until the receipt leaves `submitted` or the bound is hit.
|
|
9320
8775
|
*
|
|
9321
8776
|
* The bound is mandatory, because some states are permanently non-terminal by
|
|
9322
|
-
* design: an
|
|
9323
|
-
*
|
|
9324
|
-
*
|
|
9325
|
-
*
|
|
8777
|
+
* design: an RPC that can never observe the signature or the expiry never
|
|
8778
|
+
* resolves. On exhaustion (or abort) this returns the LATEST `submitted`
|
|
8779
|
+
* receipt with its identifier intact rather than hanging or throwing the
|
|
8780
|
+
* identifier away.
|
|
9326
8781
|
*/
|
|
9327
8782
|
async function waitForSetResult(receipt, options) {
|
|
9328
|
-
var _a, _b, _c, _d
|
|
8783
|
+
var _a, _b, _c, _d;
|
|
9329
8784
|
if (receipt && typeof receipt === 'object' && receipt.status === 'submitted' && receipt.transactionId === null) {
|
|
9330
8785
|
// Rejected immediately rather than polled: there is nothing to poll.
|
|
9331
8786
|
return reconcileSetResult(receipt, options);
|
|
@@ -9333,21 +8788,14 @@ async function waitForSetResult(receipt, options) {
|
|
|
9333
8788
|
const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT_MS;
|
|
9334
8789
|
const maxAttempts = (_b = options === null || options === void 0 ? void 0 : options.maxAttempts) !== null && _b !== void 0 ? _b : DEFAULT_MAX_ATTEMPTS;
|
|
9335
8790
|
const pollIntervalMs = (_c = options === null || options === void 0 ? void 0 : options.pollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_POLL_INTERVAL_MS;
|
|
9336
|
-
const minRebroadcastIntervalMs = (_d = options === null || options === void 0 ? void 0 : options.minRebroadcastIntervalMs) !== null && _d !== void 0 ? _d : DEFAULT_MIN_REBROADCAST_INTERVAL_MS;
|
|
9337
|
-
const rebroadcastPermitted = (options === null || options === void 0 ? void 0 : options.allowRebroadcast) !== false;
|
|
9338
8791
|
const startedAt = Date.now();
|
|
9339
8792
|
let current = receipt;
|
|
9340
8793
|
let attempts = 0;
|
|
9341
|
-
let lastRebroadcastAt = -Infinity;
|
|
9342
8794
|
while (attempts < maxAttempts && Date.now() - startedAt < timeoutMs) {
|
|
9343
|
-
if ((
|
|
8795
|
+
if ((_d = options === null || options === void 0 ? void 0 : options.signal) === null || _d === void 0 ? void 0 : _d.aborted)
|
|
9344
8796
|
return current;
|
|
9345
|
-
const now = Date.now();
|
|
9346
|
-
const allowRebroadcast = rebroadcastPermitted && (now - lastRebroadcastAt >= minRebroadcastIntervalMs);
|
|
9347
|
-
if (allowRebroadcast)
|
|
9348
|
-
lastRebroadcastAt = now;
|
|
9349
8797
|
attempts++;
|
|
9350
|
-
current = await reconcileSetResult(current,
|
|
8798
|
+
current = await reconcileSetResult(current, options);
|
|
9351
8799
|
if (current.status !== 'submitted')
|
|
9352
8800
|
return current;
|
|
9353
8801
|
if (attempts >= maxAttempts)
|
|
@@ -10252,7 +9700,7 @@ function toMillis(seconds) {
|
|
|
10252
9700
|
|
|
10253
9701
|
/** SIWS message creation is async already, so its onchain module can be lazy. */
|
|
10254
9702
|
async function genSolanaMessage(address, nonce) {
|
|
10255
|
-
const implementation = await import('./solana-message-
|
|
9703
|
+
const implementation = await import('./solana-message-BFqre_Oi.mjs');
|
|
10256
9704
|
return implementation.genSolanaMessage(address, nonce);
|
|
10257
9705
|
}
|
|
10258
9706
|
|
|
@@ -10727,5 +10175,5 @@ function defineLiveModule(mod) {
|
|
|
10727
10175
|
return mod;
|
|
10728
10176
|
}
|
|
10729
10177
|
|
|
10730
|
-
export { getSessionGeneration as $, closeAllSubscriptions as A, clearCache as B, getCachedData as C, reconnectWithNewAuth as D, createClient as E, subscribeQuery as F, closeAllQuerySubscriptions as G, canonicalizeArgs as H, InsufficientBalanceError as I, canonicalizeResult as J, DEFAULT_QUERY_POLL_INTERVAL_MS as K, BoundedDeclineError as L, MIN_QUERY_POLL_INTERVAL_MS as M, deriveDecline as N, dispatchDeclineEvent as O, DECLINE_EVENT_NAME as P, dispatchRateLimitEvent as Q, RATE_LIMIT_EVENT_NAME as R, ScopedClientReadOnlyError as S, hasActiveConnection as T, increment as U, serverTimestamp as V, now as W, toSeconds as X, toMillis as Y, getIdToken as Z, deriveUserIdentityFromIdToken as _, getWebhookKeysUrl as a, WebSessionManager as a0, ReactNativeSessionManager as a1, getActiveSessionManager as a2, onSessionCleared as a3, SessionEnvironmentError as a4, UNSUPPORTED_ENVIRONMENT_CODE as a5, AUTH_CHANGED_CODE as a6, AUTH_EXPIRED_CODE as a7, isAuthExpiredError as a8, getFreshAuthToken as a9, ServerSessionManager as aa, createSessionWithSignature as ab, refreshSession as ac, revokeSession as ad, signSessionCreateMessage as ae, genAuthNonce as af, genSolanaMessage as ag, genSiweMessage as ah, normalizeEvmAddress as ai, toLowerEvmAddress as aj,
|
|
10731
|
-
//# sourceMappingURL=index-
|
|
10178
|
+
export { getSessionGeneration as $, closeAllSubscriptions as A, clearCache as B, getCachedData as C, reconnectWithNewAuth as D, createClient as E, subscribeQuery as F, closeAllQuerySubscriptions as G, canonicalizeArgs as H, InsufficientBalanceError as I, canonicalizeResult as J, DEFAULT_QUERY_POLL_INTERVAL_MS as K, BoundedDeclineError as L, MIN_QUERY_POLL_INTERVAL_MS as M, deriveDecline as N, dispatchDeclineEvent as O, DECLINE_EVENT_NAME as P, dispatchRateLimitEvent as Q, RATE_LIMIT_EVENT_NAME as R, ScopedClientReadOnlyError as S, hasActiveConnection as T, increment as U, serverTimestamp as V, now as W, toSeconds as X, toMillis as Y, getIdToken as Z, deriveUserIdentityFromIdToken as _, getWebhookKeysUrl as a, WebSessionManager as a0, ReactNativeSessionManager as a1, getActiveSessionManager as a2, onSessionCleared as a3, SessionEnvironmentError as a4, UNSUPPORTED_ENVIRONMENT_CODE as a5, AUTH_CHANGED_CODE as a6, AUTH_EXPIRED_CODE as a7, isAuthExpiredError as a8, getFreshAuthToken as a9, ServerSessionManager as aa, createSessionWithSignature as ab, refreshSession as ac, revokeSession as ad, signSessionCreateMessage as ae, genAuthNonce as af, genSolanaMessage as ag, genSiweMessage as ah, normalizeEvmAddress as ai, toLowerEvmAddress as aj, functions as ak, invoke as al, FunctionInvokeError as am, live as an, intent as ao, status as ap, subscribeView as aq, LiveIntentError as ar, withEffects as as, isEffectResult as at, defineLiveModule as au, EFFECT_INTENT_ADDRESS as av, bufferExports as b, getConfiguredAppId as c, get as d, getMany as e, docId as f, getConfig as g, setMany as h, init as i, setFile as j, getFiles as k, search as l, runQueryMany as m, runExpression as n, runExpressionMany as o, signMessage as p, queryAggregate as q, runQuery as r, set as s, signTransaction as t, signAndSubmitTransaction as u, count as v, aggregate as w, reconcileSetResult as x, waitForSetResult as y, subscribe as z };
|
|
10179
|
+
//# sourceMappingURL=index-Csf_2uZF.mjs.map
|