@inversealtruism/cairn-sdk 0.1.3 → 0.1.4
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/dist/board.cjs +1 -1
- package/dist/board.d.cts +1 -1
- package/dist/board.d.ts +1 -1
- package/dist/board.js +2 -2
- package/dist/chain.cjs +1 -1
- package/dist/chain.d.cts +1 -1
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +1 -1
- package/dist/{chunk-G7FABIF4.js → chunk-BGJZF7S2.js} +11 -2
- package/dist/{chunk-5KAUQH72.js → chunk-C452UODV.js} +1 -1
- package/dist/{chunk-645CYBVN.js → chunk-FSQZVNEM.js} +1 -1
- package/dist/{chunk-G4OZKZSJ.js → chunk-K7SJOW3O.js} +1 -1
- package/dist/{chunk-ZAE2BIH5.js → chunk-MBSPHWZG.js} +21 -3
- package/dist/{chunk-OSXS26RA.js → chunk-MVX7VQT4.js} +4 -3
- package/dist/{chunk-FNBE2FPE.js → chunk-OG4KA3LS.js} +49 -17
- package/dist/{chunk-UMGWVOOT.js → chunk-RELP23CH.js} +1 -1
- package/dist/connect.cjs +1 -1
- package/dist/connect.d.cts +6 -0
- package/dist/connect.d.ts +6 -0
- package/dist/connect.js +2 -2
- package/dist/content.cjs +4 -3
- package/dist/content.d.cts +1 -1
- package/dist/content.d.ts +1 -1
- package/dist/content.js +2 -2
- package/dist/controller.cjs +21 -3
- package/dist/controller.d.cts +1 -0
- package/dist/controller.d.ts +1 -0
- package/dist/controller.js +3 -3
- package/dist/{http-D2CzfOsm.d.ts → http-CQsNL8w9.d.cts} +5 -0
- package/dist/{http-D2CzfOsm.d.cts → http-CQsNL8w9.d.ts} +5 -0
- package/dist/index.cjs +134 -30
- package/dist/index.d.cts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +56 -13
- package/dist/indexer.cjs +49 -17
- package/dist/indexer.d.cts +15 -4
- package/dist/indexer.d.ts +15 -4
- package/dist/indexer.js +1 -1
- package/dist/react.cjs +21 -3
- package/dist/react.js +3 -3
- package/dist/registry.cjs +11 -2
- package/dist/registry.d.cts +12 -3
- package/dist/registry.d.ts +12 -3
- package/dist/registry.js +1 -1
- package/package.json +29 -22
package/dist/board.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var import_csd_codec = require("@inversealtruism/csd-codec");
|
|
|
27
27
|
var import_csd_codec2 = require("@inversealtruism/csd-codec");
|
|
28
28
|
|
|
29
29
|
// src/errors.ts
|
|
30
|
-
var SDK_VERSION = "0.1.
|
|
30
|
+
var SDK_VERSION = "0.1.4";
|
|
31
31
|
var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
|
|
32
32
|
var CairnError = class extends Error {
|
|
33
33
|
/** Stable machine code for programmatic branching. */
|
package/dist/board.d.cts
CHANGED
package/dist/board.d.ts
CHANGED
package/dist/board.js
CHANGED
package/dist/chain.cjs
CHANGED
|
@@ -115,7 +115,7 @@ var Chain = class {
|
|
|
115
115
|
});
|
|
116
116
|
if (!built.ok || p.broadcast === false) return built;
|
|
117
117
|
const submitResult = await this.client.submit(built.nodeJson);
|
|
118
|
-
return { ...built, submitResult };
|
|
118
|
+
return { ...built, ok: built.ok && submitResult.ok !== false, submitResult };
|
|
119
119
|
}
|
|
120
120
|
/** A verifying light client over the same RPC (built once, reused). */
|
|
121
121
|
light() {
|
package/dist/chain.d.cts
CHANGED
|
@@ -7,7 +7,7 @@ export { App, BlockHeader, Tx, TxInput, TxOutput, canonicalJson, deserialize, hb
|
|
|
7
7
|
export { ADDR_RE, addrFromPriv, addrFromPub, hash160, isValidAddr, isValidPriv, keygen, pubFromPriv, randomNonce, signDigest, verifyDigest } from '@inversealtruism/csd-crypto';
|
|
8
8
|
import { LightClient } from '@inversealtruism/csd-light';
|
|
9
9
|
export { InclusionResult, LightClient, ReorgResult, TrustLevel, VerifiedHeader, expectedBits, expectedBitsFromWindow } from '@inversealtruism/csd-light';
|
|
10
|
-
import { F as FetchLike } from './http-
|
|
10
|
+
import { F as FetchLike } from './http-CQsNL8w9.cjs';
|
|
11
11
|
|
|
12
12
|
interface ChainOptions {
|
|
13
13
|
/** Node RPC base URL — defaults to the public Cairn proxy (no local node needed). */
|
package/dist/chain.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { App, BlockHeader, Tx, TxInput, TxOutput, canonicalJson, deserialize, hb
|
|
|
7
7
|
export { ADDR_RE, addrFromPriv, addrFromPub, hash160, isValidAddr, isValidPriv, keygen, pubFromPriv, randomNonce, signDigest, verifyDigest } from '@inversealtruism/csd-crypto';
|
|
8
8
|
import { LightClient } from '@inversealtruism/csd-light';
|
|
9
9
|
export { InclusionResult, LightClient, ReorgResult, TrustLevel, VerifiedHeader, expectedBits, expectedBitsFromWindow } from '@inversealtruism/csd-light';
|
|
10
|
-
import { F as FetchLike } from './http-
|
|
10
|
+
import { F as FetchLike } from './http-CQsNL8w9.js';
|
|
11
11
|
|
|
12
12
|
interface ChainOptions {
|
|
13
13
|
/** Node RPC base URL — defaults to the public Cairn proxy (no local node needed). */
|
package/dist/chain.js
CHANGED
|
@@ -31,11 +31,20 @@ var RegistryClient = class {
|
|
|
31
31
|
peers() {
|
|
32
32
|
return discoverPeers(this.src);
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
|
|
36
|
+
*
|
|
37
|
+
* ⚠ TRUST (CAIRN-SDK-RESOLVE-VERIFIED-FOOTGUN): the returned `verified` flag is the INDEXER's assertion,
|
|
38
|
+
* NOT a client-side proof. A hostile/compromised indexer (or a MITM of the read proxy) can return
|
|
39
|
+
* `verified:true` for a wrong address. Do NOT use this directly as a payee/login target without either (a)
|
|
40
|
+
* recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
|
|
41
|
+
* source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
|
|
42
|
+
*/
|
|
35
43
|
resolveName(handle) {
|
|
36
44
|
return resolveName(this.src, handle);
|
|
37
45
|
}
|
|
38
|
-
/** Reverse-resolve an address → identity (GET /address/:addr/identity).
|
|
46
|
+
/** Reverse-resolve an address → identity (GET /address/:addr/identity).
|
|
47
|
+
* ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
|
|
39
48
|
reverseName(address) {
|
|
40
49
|
return reverseName(this.src, address);
|
|
41
50
|
}
|
|
@@ -92,7 +92,7 @@ var Chain = class {
|
|
|
92
92
|
});
|
|
93
93
|
if (!built.ok || p.broadcast === false) return built;
|
|
94
94
|
const submitResult = await this.client.submit(built.nodeJson);
|
|
95
|
-
return { ...built, submitResult };
|
|
95
|
+
return { ...built, ok: built.ok && submitResult.ok !== false, submitResult };
|
|
96
96
|
}
|
|
97
97
|
/** A verifying light client over the same RPC (built once, reused). */
|
|
98
98
|
light() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getWallet
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-K7SJOW3O.js";
|
|
4
4
|
|
|
5
5
|
// src/controller.ts
|
|
6
6
|
var DISCONNECTED = Object.freeze({ status: "disconnected", account: null, error: null });
|
|
@@ -35,12 +35,28 @@ var CairnController = class {
|
|
|
35
35
|
for (const l of [...this.listeners]) l();
|
|
36
36
|
}
|
|
37
37
|
onAccounts = (accounts) => {
|
|
38
|
-
if (!Array.isArray(accounts) || accounts.length === 0)
|
|
39
|
-
|
|
38
|
+
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
39
|
+
this.setState({ status: "disconnected", account: null });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const next = String(accounts[0]);
|
|
43
|
+
if (this.state.status === "connected" && this.state.account && next.toLowerCase() === this.state.account.toLowerCase()) {
|
|
44
|
+
this.setState({ status: "connected", account: next, error: null });
|
|
45
|
+
} else {
|
|
46
|
+
this.setState({ status: "disconnected", account: null });
|
|
47
|
+
}
|
|
40
48
|
};
|
|
41
49
|
onDisconnect = () => {
|
|
42
50
|
this.setState({ status: "disconnected", account: null });
|
|
43
51
|
};
|
|
52
|
+
// Detach the provider listeners (ghost-reconnect fix). Idempotent + best-effort.
|
|
53
|
+
detach() {
|
|
54
|
+
try {
|
|
55
|
+
this.conn?.off?.("accountsChanged", this.onAccounts);
|
|
56
|
+
this.conn?.off?.("disconnect", this.onDisconnect);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
44
60
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
45
61
|
connect = async () => {
|
|
46
62
|
this.setState({ status: "connecting", error: null });
|
|
@@ -64,6 +80,8 @@ var CairnController = class {
|
|
|
64
80
|
await this.conn?.revokePermissions();
|
|
65
81
|
} catch {
|
|
66
82
|
}
|
|
83
|
+
this.detach();
|
|
84
|
+
this.conn = null;
|
|
67
85
|
this.setState({ status: "disconnected", account: null, error: null });
|
|
68
86
|
};
|
|
69
87
|
/** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContentVerificationError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RELP23CH.js";
|
|
4
4
|
|
|
5
5
|
// src/content.ts
|
|
6
6
|
import { payloadHash, canonicalJson, verifyContentBytes } from "@inversealtruism/csd-codec";
|
|
@@ -67,8 +67,9 @@ var ContentClient = class {
|
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
69
|
function normalizeHash(hash) {
|
|
70
|
-
const h = hash.startsWith("0x") || hash.startsWith("0X") ? hash : `0x${hash}
|
|
71
|
-
|
|
70
|
+
const h = (hash.startsWith("0x") || hash.startsWith("0X") ? hash : `0x${hash}`).toLowerCase();
|
|
71
|
+
if (!/^0x[0-9a-f]{64}$/.test(h)) throw new Error(`invalid content hash: ${hash}`);
|
|
72
|
+
return h;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
export {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// src/indexer.ts
|
|
2
2
|
import { verifyMerkleProof } from "@inversealtruism/csd-codec";
|
|
3
|
+
var HEX64 = /^0x[0-9a-fA-F]{64}$/;
|
|
4
|
+
var MAX_SSE_BUF = 1024 * 1024;
|
|
5
|
+
function isValidMerkleProof(p) {
|
|
6
|
+
if (!p || typeof p !== "object") return false;
|
|
7
|
+
const m = p;
|
|
8
|
+
return Number.isInteger(m.block_height) && m.block_height >= 0 && Number.isInteger(m.pos) && m.pos >= 0 && typeof m.merkle_root === "string" && HEX64.test(m.merkle_root) && Array.isArray(m.merkle) && m.merkle.every((x) => typeof x === "string" && HEX64.test(x));
|
|
9
|
+
}
|
|
3
10
|
var IndexerClient = class {
|
|
4
11
|
http;
|
|
5
12
|
opts;
|
|
@@ -20,32 +27,35 @@ var IndexerClient = class {
|
|
|
20
27
|
blockHashAtHeight(h) {
|
|
21
28
|
return this.http.getJson(`/block-height/${h}`);
|
|
22
29
|
}
|
|
30
|
+
// CAIRNSDK-CONTENT-HASH-PATH-1: encode every caller-supplied id segment. Raw interpolation let an id like
|
|
31
|
+
// `../../api/rpc/tip` walk `new URL` to a DIFFERENT same-origin endpoint whose body was then returned
|
|
32
|
+
// verbatim as the requested object (worse than the content case, which discards the walked-to response).
|
|
23
33
|
block(hash) {
|
|
24
|
-
return this.http.getJson(`/block/${hash}`);
|
|
34
|
+
return this.http.getJson(`/block/${encodeURIComponent(hash)}`);
|
|
25
35
|
}
|
|
26
36
|
blockTxids(hash) {
|
|
27
|
-
return this.http.getJson(`/block/${hash}/txids`);
|
|
37
|
+
return this.http.getJson(`/block/${encodeURIComponent(hash)}/txids`);
|
|
28
38
|
}
|
|
29
39
|
tx(id) {
|
|
30
|
-
return this.http.getJson(`/tx/${id}`);
|
|
40
|
+
return this.http.getJson(`/tx/${encodeURIComponent(id)}`);
|
|
31
41
|
}
|
|
32
42
|
txStatus(id) {
|
|
33
|
-
return this.http.getJson(`/tx/${id}/status`);
|
|
43
|
+
return this.http.getJson(`/tx/${encodeURIComponent(id)}/status`);
|
|
34
44
|
}
|
|
35
45
|
txMerkleProof(id) {
|
|
36
|
-
return this.http.getJson(`/tx/${id}/merkle-proof`);
|
|
46
|
+
return this.http.getJson(`/tx/${encodeURIComponent(id)}/merkle-proof`);
|
|
37
47
|
}
|
|
38
48
|
address(a) {
|
|
39
|
-
return this.http.getJson(`/address/${a}`);
|
|
49
|
+
return this.http.getJson(`/address/${encodeURIComponent(a)}`);
|
|
40
50
|
}
|
|
41
51
|
addressTxs(a) {
|
|
42
|
-
return this.http.getJson(`/address/${a}/txs`);
|
|
52
|
+
return this.http.getJson(`/address/${encodeURIComponent(a)}/txs`);
|
|
43
53
|
}
|
|
44
54
|
addressUtxo(a) {
|
|
45
|
-
return this.http.getJson(`/address/${a}/utxo`);
|
|
55
|
+
return this.http.getJson(`/address/${encodeURIComponent(a)}/utxo`);
|
|
46
56
|
}
|
|
47
57
|
reputation(a) {
|
|
48
|
-
return this.http.getJson(`/address/${a}/reputation`);
|
|
58
|
+
return this.http.getJson(`/address/${encodeURIComponent(a)}/reputation`);
|
|
49
59
|
}
|
|
50
60
|
domains() {
|
|
51
61
|
return this.http.getJson("/domains");
|
|
@@ -54,10 +64,10 @@ var IndexerClient = class {
|
|
|
54
64
|
return this.http.getJson(`/domain/${encodeURIComponent(d)}/proposals`);
|
|
55
65
|
}
|
|
56
66
|
proposal(id) {
|
|
57
|
-
return this.http.getJson(`/proposal/${id}`);
|
|
67
|
+
return this.http.getJson(`/proposal/${encodeURIComponent(id)}`);
|
|
58
68
|
}
|
|
59
69
|
attestations(id) {
|
|
60
|
-
return this.http.getJson(`/proposal/${id}/attestations`);
|
|
70
|
+
return this.http.getJson(`/proposal/${encodeURIComponent(id)}/attestations`);
|
|
61
71
|
}
|
|
62
72
|
registryPeers() {
|
|
63
73
|
return this.http.getJson("/registry/peers");
|
|
@@ -69,7 +79,7 @@ var IndexerClient = class {
|
|
|
69
79
|
return this.http.getJson(`/identity/${encodeURIComponent(handle)}`);
|
|
70
80
|
}
|
|
71
81
|
reverseIdentity(addr) {
|
|
72
|
-
return this.http.getJson(`/address/${addr}/identity`);
|
|
82
|
+
return this.http.getJson(`/address/${encodeURIComponent(addr)}/identity`);
|
|
73
83
|
}
|
|
74
84
|
// ---- trust-minimized inclusion ----------------------------------------
|
|
75
85
|
/**
|
|
@@ -78,9 +88,11 @@ var IndexerClient = class {
|
|
|
78
88
|
* proof's root is cross-checked against the header merkle at that height →
|
|
79
89
|
* trustLevel "verified-inclusion" (only as trustworthy as that header source —
|
|
80
90
|
* see headerMerkleAt). Otherwise the proof is only internally consistent →
|
|
81
|
-
* "proof-consistent" (still useful, but the indexer is trusted).
|
|
82
|
-
*
|
|
83
|
-
*
|
|
91
|
+
* "proof-consistent" (still useful, but the indexer is trusted). INDEXER-DOC-STALE-1: the Cairn facade now
|
|
92
|
+
* wires headerMerkleAt UNCONDITIONALLY via a PoW-verifying checkpoint light client (index.ts), so the
|
|
93
|
+
* default reaches "verified-inclusion" when the proof root matches the PoW-verified header — what makes a
|
|
94
|
+
* same-origin RPC safe is the forward PoW re-verification, not an origin gate (which no longer exists).
|
|
95
|
+
* Fails closed: a malformed proof or a chain-disagreement returns "not-found" (the latter with equivocation:true).
|
|
84
96
|
*/
|
|
85
97
|
async verifyInclusion(txid) {
|
|
86
98
|
let proof;
|
|
@@ -89,7 +101,15 @@ var IndexerClient = class {
|
|
|
89
101
|
} catch {
|
|
90
102
|
return { included: false, trustLevel: "not-found", reason: "no merkle proof (tx not indexed)" };
|
|
91
103
|
}
|
|
92
|
-
|
|
104
|
+
if (!isValidMerkleProof(proof)) {
|
|
105
|
+
return { included: false, trustLevel: "not-found", reason: "malformed merkle proof" };
|
|
106
|
+
}
|
|
107
|
+
let folds = false;
|
|
108
|
+
try {
|
|
109
|
+
folds = verifyMerkleProof(txid, proof.pos, proof.merkle, proof.merkle_root);
|
|
110
|
+
} catch {
|
|
111
|
+
return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "malformed merkle proof" };
|
|
112
|
+
}
|
|
93
113
|
if (!folds) {
|
|
94
114
|
return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "merkle branch does not fold to the claimed root" };
|
|
95
115
|
}
|
|
@@ -97,7 +117,7 @@ var IndexerClient = class {
|
|
|
97
117
|
try {
|
|
98
118
|
const onchain = await this.opts.headerMerkleAt(proof.block_height);
|
|
99
119
|
if (norm(onchain) !== norm(proof.merkle_root)) {
|
|
100
|
-
return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "proof root != on-chain header merkle (indexer disagrees with chain)" };
|
|
120
|
+
return { included: false, trustLevel: "not-found", equivocation: true, blockHeight: proof.block_height, reason: "proof root != on-chain header merkle (indexer disagrees with chain)" };
|
|
101
121
|
}
|
|
102
122
|
return { included: true, trustLevel: "verified-inclusion", blockHeight: proof.block_height, pos: proof.pos, merkleRoot: proof.merkle_root };
|
|
103
123
|
} catch {
|
|
@@ -121,6 +141,11 @@ var IndexerClient = class {
|
|
|
121
141
|
/**
|
|
122
142
|
* Subscribe over WebSocket with selective tracking. Returns a handle with
|
|
123
143
|
* `.close()`. `onEvent` receives every matching IndexEvent (incl. reorgs).
|
|
144
|
+
*
|
|
145
|
+
* WS-NORECONNECT-1: unlike the SSE feeds (streamAll/streamBlocks/streamDomain, which auto-reconnect with
|
|
146
|
+
* backoff), this WebSocket subscription does NOT auto-reconnect — it surfaces a terminal error via
|
|
147
|
+
* `onError` (when provided) and stops. A consumer tracking reorgs over WS MUST re-`subscribe()` on error
|
|
148
|
+
* to avoid a silently-stale view; for built-in reconnect prefer the SSE `stream*` methods.
|
|
124
149
|
*/
|
|
125
150
|
subscribe(sub, onEvent, onError) {
|
|
126
151
|
const WS = this.opts.WebSocketImpl ?? globalThis.WebSocket;
|
|
@@ -183,6 +208,13 @@ var IndexerClient = class {
|
|
|
183
208
|
buf = buf.slice(idx + 2);
|
|
184
209
|
dispatchFrame(frame, handlers);
|
|
185
210
|
}
|
|
211
|
+
if (buf.length > MAX_SSE_BUF) {
|
|
212
|
+
try {
|
|
213
|
+
await reader.cancel();
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
throw new Error(`SSE buffer exceeded ${MAX_SSE_BUF} bytes with no frame delimiter \u2014 aborting`);
|
|
217
|
+
}
|
|
186
218
|
}
|
|
187
219
|
} catch (err) {
|
|
188
220
|
if (closed) return;
|
package/dist/connect.cjs
CHANGED
|
@@ -30,7 +30,7 @@ __export(connect_exports, {
|
|
|
30
30
|
module.exports = __toCommonJS(connect_exports);
|
|
31
31
|
|
|
32
32
|
// src/errors.ts
|
|
33
|
-
var SDK_VERSION = "0.1.
|
|
33
|
+
var SDK_VERSION = "0.1.4";
|
|
34
34
|
var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
|
|
35
35
|
var CairnError = class extends Error {
|
|
36
36
|
/** Stable machine code for programmatic branching. */
|
package/dist/connect.d.cts
CHANGED
|
@@ -188,6 +188,12 @@ declare function isInstalled(): boolean;
|
|
|
188
188
|
/**
|
|
189
189
|
* High-level, typed wallet handle. Construct via `connect()` or `new WalletConnection(provider)`.
|
|
190
190
|
* Methods throw `UserRejectedError` / `WalletLockedError` / `UnsupportedMethodError` on failure.
|
|
191
|
+
*
|
|
192
|
+
* Note (typed-error contract): the genuine Cairn Wallet always RESOLVES a `{ok,result|error}` reply, so a
|
|
193
|
+
* failure is mapped to a typed `CairnError` via `unwrap`. A non-conformant or SPOOFED provider that instead
|
|
194
|
+
* REJECTS its promise will surface that raw rejection un-mapped (it bypasses `unwrap`), so a dApp's
|
|
195
|
+
* `catch (e) { errorCode(e) }` may see `UNKNOWN` from such a provider. The real auth boundary is server-side
|
|
196
|
+
* SIWC verification, never a client-side error code.
|
|
191
197
|
*/
|
|
192
198
|
declare class WalletConnection {
|
|
193
199
|
readonly provider: CairnProvider;
|
package/dist/connect.d.ts
CHANGED
|
@@ -188,6 +188,12 @@ declare function isInstalled(): boolean;
|
|
|
188
188
|
/**
|
|
189
189
|
* High-level, typed wallet handle. Construct via `connect()` or `new WalletConnection(provider)`.
|
|
190
190
|
* Methods throw `UserRejectedError` / `WalletLockedError` / `UnsupportedMethodError` on failure.
|
|
191
|
+
*
|
|
192
|
+
* Note (typed-error contract): the genuine Cairn Wallet always RESOLVES a `{ok,result|error}` reply, so a
|
|
193
|
+
* failure is mapped to a typed `CairnError` via `unwrap`. A non-conformant or SPOOFED provider that instead
|
|
194
|
+
* REJECTS its promise will surface that raw rejection un-mapped (it bypasses `unwrap`), so a dApp's
|
|
195
|
+
* `catch (e) { errorCode(e) }` may see `UNKNOWN` from such a provider. The real auth boundary is server-side
|
|
196
|
+
* SIWC verification, never a client-side error code.
|
|
191
197
|
*/
|
|
192
198
|
declare class WalletConnection {
|
|
193
199
|
readonly provider: CairnProvider;
|
package/dist/connect.js
CHANGED
package/dist/content.cjs
CHANGED
|
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(content_exports);
|
|
|
26
26
|
var import_csd_codec = require("@inversealtruism/csd-codec");
|
|
27
27
|
|
|
28
28
|
// src/errors.ts
|
|
29
|
-
var SDK_VERSION = "0.1.
|
|
29
|
+
var SDK_VERSION = "0.1.4";
|
|
30
30
|
var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
|
|
31
31
|
var CairnError = class extends Error {
|
|
32
32
|
/** Stable machine code for programmatic branching. */
|
|
@@ -135,8 +135,9 @@ var ContentClient = class {
|
|
|
135
135
|
}
|
|
136
136
|
};
|
|
137
137
|
function normalizeHash(hash) {
|
|
138
|
-
const h = hash.startsWith("0x") || hash.startsWith("0X") ? hash : `0x${hash}
|
|
139
|
-
|
|
138
|
+
const h = (hash.startsWith("0x") || hash.startsWith("0X") ? hash : `0x${hash}`).toLowerCase();
|
|
139
|
+
if (!/^0x[0-9a-f]{64}$/.test(h)) throw new Error(`invalid content hash: ${hash}`);
|
|
140
|
+
return h;
|
|
140
141
|
}
|
|
141
142
|
// Annotate the CommonJS export names for ESM import in node:
|
|
142
143
|
0 && (module.exports = {
|
package/dist/content.d.cts
CHANGED
package/dist/content.d.ts
CHANGED
package/dist/content.js
CHANGED
package/dist/controller.cjs
CHANGED
|
@@ -25,7 +25,7 @@ __export(controller_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(controller_exports);
|
|
26
26
|
|
|
27
27
|
// src/errors.ts
|
|
28
|
-
var SDK_VERSION = "0.1.
|
|
28
|
+
var SDK_VERSION = "0.1.4";
|
|
29
29
|
var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
|
|
30
30
|
var CairnError = class extends Error {
|
|
31
31
|
/** Stable machine code for programmatic branching. */
|
|
@@ -294,12 +294,28 @@ var CairnController = class {
|
|
|
294
294
|
for (const l of [...this.listeners]) l();
|
|
295
295
|
}
|
|
296
296
|
onAccounts = (accounts) => {
|
|
297
|
-
if (!Array.isArray(accounts) || accounts.length === 0)
|
|
298
|
-
|
|
297
|
+
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
298
|
+
this.setState({ status: "disconnected", account: null });
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const next = String(accounts[0]);
|
|
302
|
+
if (this.state.status === "connected" && this.state.account && next.toLowerCase() === this.state.account.toLowerCase()) {
|
|
303
|
+
this.setState({ status: "connected", account: next, error: null });
|
|
304
|
+
} else {
|
|
305
|
+
this.setState({ status: "disconnected", account: null });
|
|
306
|
+
}
|
|
299
307
|
};
|
|
300
308
|
onDisconnect = () => {
|
|
301
309
|
this.setState({ status: "disconnected", account: null });
|
|
302
310
|
};
|
|
311
|
+
// Detach the provider listeners (ghost-reconnect fix). Idempotent + best-effort.
|
|
312
|
+
detach() {
|
|
313
|
+
try {
|
|
314
|
+
this.conn?.off?.("accountsChanged", this.onAccounts);
|
|
315
|
+
this.conn?.off?.("disconnect", this.onDisconnect);
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
}
|
|
303
319
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
304
320
|
connect = async () => {
|
|
305
321
|
this.setState({ status: "connecting", error: null });
|
|
@@ -323,6 +339,8 @@ var CairnController = class {
|
|
|
323
339
|
await this.conn?.revokePermissions();
|
|
324
340
|
} catch {
|
|
325
341
|
}
|
|
342
|
+
this.detach();
|
|
343
|
+
this.conn = null;
|
|
326
344
|
this.setState({ status: "disconnected", account: null, error: null });
|
|
327
345
|
};
|
|
328
346
|
/** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
|
package/dist/controller.d.cts
CHANGED
|
@@ -25,6 +25,7 @@ declare class CairnController {
|
|
|
25
25
|
private setState;
|
|
26
26
|
private onAccounts;
|
|
27
27
|
private onDisconnect;
|
|
28
|
+
private detach;
|
|
28
29
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
29
30
|
connect: () => Promise<string>;
|
|
30
31
|
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort). */
|
package/dist/controller.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ declare class CairnController {
|
|
|
25
25
|
private setState;
|
|
26
26
|
private onAccounts;
|
|
27
27
|
private onDisconnect;
|
|
28
|
+
private detach;
|
|
28
29
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
29
30
|
connect: () => Promise<string>;
|
|
30
31
|
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort). */
|
package/dist/controller.js
CHANGED
|
@@ -6,6 +6,10 @@ interface HttpOptions {
|
|
|
6
6
|
fetch?: FetchLike;
|
|
7
7
|
/** Per-request timeout in ms (default 15000). */
|
|
8
8
|
timeoutMs?: number;
|
|
9
|
+
/** Max response body bytes accepted before aborting (default 16 MiB — matches the cairn proxy cap).
|
|
10
|
+
* CAIRNSDK-DESER-4: the SDK's sources (indexer/content) are self-classified UNTRUSTED; without a cap a
|
|
11
|
+
* hostile/MITM'd source can stream an unbounded body and OOM the client (browser tab / MV3 worker / bot). */
|
|
12
|
+
maxBytes?: number;
|
|
9
13
|
/** Extra headers sent on every request. */
|
|
10
14
|
headers?: Record<string, string>;
|
|
11
15
|
}
|
|
@@ -13,6 +17,7 @@ declare class Http {
|
|
|
13
17
|
readonly baseUrl: string;
|
|
14
18
|
private readonly _fetch;
|
|
15
19
|
private readonly timeoutMs;
|
|
20
|
+
private readonly maxBytes;
|
|
16
21
|
private readonly headers;
|
|
17
22
|
constructor(opts: HttpOptions);
|
|
18
23
|
/** Build an absolute URL from a path + optional query params. */
|
|
@@ -6,6 +6,10 @@ interface HttpOptions {
|
|
|
6
6
|
fetch?: FetchLike;
|
|
7
7
|
/** Per-request timeout in ms (default 15000). */
|
|
8
8
|
timeoutMs?: number;
|
|
9
|
+
/** Max response body bytes accepted before aborting (default 16 MiB — matches the cairn proxy cap).
|
|
10
|
+
* CAIRNSDK-DESER-4: the SDK's sources (indexer/content) are self-classified UNTRUSTED; without a cap a
|
|
11
|
+
* hostile/MITM'd source can stream an unbounded body and OOM the client (browser tab / MV3 worker / bot). */
|
|
12
|
+
maxBytes?: number;
|
|
9
13
|
/** Extra headers sent on every request. */
|
|
10
14
|
headers?: Record<string, string>;
|
|
11
15
|
}
|
|
@@ -13,6 +17,7 @@ declare class Http {
|
|
|
13
17
|
readonly baseUrl: string;
|
|
14
18
|
private readonly _fetch;
|
|
15
19
|
private readonly timeoutMs;
|
|
20
|
+
private readonly maxBytes;
|
|
16
21
|
private readonly headers;
|
|
17
22
|
constructor(opts: HttpOptions);
|
|
18
23
|
/** Build an absolute URL from a path + optional query params. */
|