@inversealtruism/cairn-sdk 0.3.2 → 0.4.1
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.js +2 -2
- package/dist/chunk-7UOTIAYK.js +224 -0
- package/dist/{chunk-CZWDA3A5.js → chunk-DUTVUIQF.js} +1 -1
- package/dist/{chunk-FMXNJQWL.js → chunk-ERBT4DFD.js} +2 -2
- package/dist/{chunk-JKC5K253.js → chunk-OCEHXK6A.js} +1 -1
- package/dist/{chunk-BN3PFJDY.js → chunk-PIPN6FSA.js} +53 -16
- package/dist/{chunk-AGU6K5EZ.js → chunk-QATE6AIP.js} +25 -13
- package/dist/connect.cjs +53 -17
- package/dist/connect.d.cts +11 -2
- package/dist/connect.d.ts +11 -2
- package/dist/connect.js +2 -2
- package/dist/content.cjs +1 -1
- package/dist/content.js +2 -2
- package/dist/controller.cjs +77 -29
- package/dist/controller.d.cts +4 -1
- package/dist/controller.d.ts +4 -1
- package/dist/controller.js +3 -3
- package/dist/index.cjs +317 -71
- package/dist/index.d.cts +65 -18
- package/dist/index.d.ts +65 -18
- package/dist/index.js +235 -181
- package/dist/react.cjs +77 -29
- package/dist/react.js +3 -3
- package/dist/registry.cjs +219 -7
- package/dist/registry.d.cts +16 -11
- package/dist/registry.d.ts +16 -11
- package/dist/registry.js +2 -1
- package/package.json +21 -28
- package/dist/chunk-BGJZF7S2.js +0 -69
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.
|
|
33
|
+
var SDK_VERSION = "0.4.1";
|
|
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. */
|
|
@@ -104,7 +104,7 @@ var SubmitInFlightError = class extends CairnError {
|
|
|
104
104
|
this.txid = txid;
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
|
-
var NATIVE_CODES = /* @__PURE__ */ new Set(["
|
|
107
|
+
var NATIVE_CODES = /* @__PURE__ */ new Set(["FIRST_PARTY_ONLY", "RATE_LIMITED", "FORBIDDEN", "INTERNAL"]);
|
|
108
108
|
var NESTED_TRANSIENT = /* @__PURE__ */ new Set(["VERIFY_UNAVAILABLE", "OFFER_UNKNOWN"]);
|
|
109
109
|
var NESTED_TERMINAL = /* @__PURE__ */ new Set([
|
|
110
110
|
"FILL_UNSAFE",
|
|
@@ -148,6 +148,7 @@ function mapSubmitResultError(result) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/connect.ts
|
|
151
|
+
var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
|
|
151
152
|
function discoverProviders(opts = {}) {
|
|
152
153
|
const timeoutMs = opts.timeoutMs ?? 1e3;
|
|
153
154
|
return new Promise((resolve) => {
|
|
@@ -227,8 +228,27 @@ function unwrapWrite(reply) {
|
|
|
227
228
|
var WalletConnection = class {
|
|
228
229
|
provider;
|
|
229
230
|
_addr = null;
|
|
230
|
-
|
|
231
|
+
/** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
|
|
232
|
+
* approval; 0 disables it. Every provider call (writes especially) races against it. */
|
|
233
|
+
_timeoutMs;
|
|
234
|
+
constructor(provider, opts = {}) {
|
|
231
235
|
this.provider = provider;
|
|
236
|
+
this._timeoutMs = opts.timeoutMs ?? DEFAULT_PROVIDER_TIMEOUT_MS;
|
|
237
|
+
}
|
|
238
|
+
// B7c: race a provider call against the per-call timeout. On timeout, throw a fail-closed CairnError
|
|
239
|
+
// (retryable:false: a write may already have reached the wallet, so never invite a blind auto-retry). The
|
|
240
|
+
// genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
|
|
241
|
+
call(op, label) {
|
|
242
|
+
const ms = this._timeoutMs;
|
|
243
|
+
const invoke = () => new Promise((resolve) => {
|
|
244
|
+
resolve(op());
|
|
245
|
+
});
|
|
246
|
+
if (!Number.isFinite(ms) || ms <= 0) return invoke();
|
|
247
|
+
let timer;
|
|
248
|
+
const timeout = new Promise((_, reject) => {
|
|
249
|
+
timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
|
|
250
|
+
});
|
|
251
|
+
return Promise.race([invoke(), timeout]).finally(() => clearTimeout(timer));
|
|
232
252
|
}
|
|
233
253
|
/** The wallet's version string (e.g. "0.2.x"). */
|
|
234
254
|
get version() {
|
|
@@ -240,13 +260,13 @@ var WalletConnection = class {
|
|
|
240
260
|
}
|
|
241
261
|
/** Request connection. First time per origin prompts the user; afterwards may be silent. */
|
|
242
262
|
async connect() {
|
|
243
|
-
const { addr } = unwrap(await this.provider.connect());
|
|
263
|
+
const { addr } = unwrap(await this.call(() => this.provider.connect(), "connect"));
|
|
244
264
|
this._addr = addr;
|
|
245
265
|
return addr;
|
|
246
266
|
}
|
|
247
267
|
/** Get the connected address (does not force a new connection prompt if already consented). */
|
|
248
268
|
async getAddress() {
|
|
249
|
-
const { addr } = unwrap(await this.provider.getAddress());
|
|
269
|
+
const { addr } = unwrap(await this.call(() => this.provider.getAddress(), "getAddress"));
|
|
250
270
|
this._addr = addr;
|
|
251
271
|
return addr;
|
|
252
272
|
}
|
|
@@ -259,7 +279,7 @@ var WalletConnection = class {
|
|
|
259
279
|
if (typeof this.provider.signIn !== "function") {
|
|
260
280
|
return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
|
|
261
281
|
}
|
|
262
|
-
return
|
|
282
|
+
return this.call(() => this.provider.signIn(), "signIn").then(unwrap);
|
|
263
283
|
}
|
|
264
284
|
/**
|
|
265
285
|
* Audience-bound "Sign in with CSD" (SIWC) — the secure, replay-resistant third-party login.
|
|
@@ -275,7 +295,7 @@ var WalletConnection = class {
|
|
|
275
295
|
if (typeof this.provider.signInWithCsd !== "function") {
|
|
276
296
|
return Promise.reject(new UnsupportedMethodError("this wallet predates Sign-in-with-CSD \u2014 ask the user to update the Cairn Wallet"));
|
|
277
297
|
}
|
|
278
|
-
return
|
|
298
|
+
return this.call(() => this.provider.signInWithCsd(params), "signInWithCsd").then(unwrap);
|
|
279
299
|
}
|
|
280
300
|
/** True if this wallet supports audience-bound SIWC (safe to call `signInWithCsd`). */
|
|
281
301
|
get supportsSiwc() {
|
|
@@ -285,7 +305,7 @@ var WalletConnection = class {
|
|
|
285
305
|
async getCapabilities() {
|
|
286
306
|
if (typeof this.provider.getCapabilities !== "function") return null;
|
|
287
307
|
try {
|
|
288
|
-
return await this.provider.getCapabilities();
|
|
308
|
+
return await this.call(() => this.provider.getCapabilities(), "getCapabilities");
|
|
289
309
|
} catch {
|
|
290
310
|
return null;
|
|
291
311
|
}
|
|
@@ -301,24 +321,40 @@ var WalletConnection = class {
|
|
|
301
321
|
if (typeof this.provider.fillOffer !== "function") {
|
|
302
322
|
return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
|
|
303
323
|
}
|
|
304
|
-
|
|
324
|
+
this.adviseFillSpvCapability();
|
|
325
|
+
return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
|
|
326
|
+
}
|
|
327
|
+
// P75-5 MF-10: ONE capability advisory per connection. The wallet's on-device fill-SPV (0.2.60+) is the
|
|
328
|
+
// payment-grade fill boundary; when the provider SELF-REPORTS an older version, warn the integrator once
|
|
329
|
+
// and STILL RELAY. WARN, NEVER BLOCK: a hard version gate would decline honest fills (version strings are
|
|
330
|
+
// spoofable and unparseable forks exist), and the wallet itself is the enforcement point. Reads only the
|
|
331
|
+
// sync `provider.version`; never an awaited getCapabilities() round-trip on the fill hot path. An
|
|
332
|
+
// unparseable version never warns (no false alarms).
|
|
333
|
+
_fillSpvAdvised = false;
|
|
334
|
+
adviseFillSpvCapability() {
|
|
335
|
+
if (this._fillSpvAdvised) return;
|
|
336
|
+
this._fillSpvAdvised = true;
|
|
337
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(this.provider?.version ?? ""));
|
|
338
|
+
if (!m) return;
|
|
339
|
+
const below = Number(m[1]) === 0 && (Number(m[2]) < 2 || Number(m[2]) === 2 && Number(m[3]) < 60);
|
|
340
|
+
if (below) console.warn(`[cairn-sdk] this Cairn Wallet (v${m[0]}) predates the on-device fill-SPV boundary (cairn-wallet 0.2.60+). The fill proceeds; suggest a wallet update for payment-grade offer verification.`);
|
|
305
341
|
}
|
|
306
342
|
/** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
|
|
307
343
|
async getPermissions() {
|
|
308
344
|
if (typeof this.provider.getPermissions !== "function") return [];
|
|
309
|
-
return unwrap(await this.provider.getPermissions());
|
|
345
|
+
return unwrap(await this.call(() => this.provider.getPermissions(), "getPermissions"));
|
|
310
346
|
}
|
|
311
347
|
/** Explicitly request permission to see the address (prompts, like connect). */
|
|
312
348
|
requestPermissions() {
|
|
313
349
|
if (typeof this.provider.requestPermissions !== "function") {
|
|
314
350
|
return Promise.reject(new UnsupportedMethodError("this wallet predates requestPermissions \u2014 use connect()"));
|
|
315
351
|
}
|
|
316
|
-
return
|
|
352
|
+
return this.call(() => this.provider.requestPermissions(), "requestPermissions").then(unwrap);
|
|
317
353
|
}
|
|
318
354
|
/** Revoke THIS origin's own access (silent). Returns { revoked }. Safe no-op on older wallets. */
|
|
319
355
|
async revokePermissions() {
|
|
320
356
|
if (typeof this.provider.revokePermissions !== "function") return { revoked: false };
|
|
321
|
-
return unwrap(await this.provider.revokePermissions());
|
|
357
|
+
return unwrap(await this.call(() => this.provider.revokePermissions(), "revokePermissions"));
|
|
322
358
|
}
|
|
323
359
|
/**
|
|
324
360
|
* Subscribe to provider events. `accountsChanged` fires with `[]` when this site loses access
|
|
@@ -334,26 +370,26 @@ var WalletConnection = class {
|
|
|
334
370
|
}
|
|
335
371
|
/** Send CSD (always prompts with clear-signing; wallet picks inputs + returns change to itself). */
|
|
336
372
|
send(params) {
|
|
337
|
-
return
|
|
373
|
+
return this.call(() => this.provider.send(params), "send").then(unwrapWrite);
|
|
338
374
|
}
|
|
339
375
|
/** Low-level Propose (always prompts). Prefer `cairn.board.propose()` which computes the hash/uri/expiry. */
|
|
340
376
|
propose(params) {
|
|
341
|
-
return
|
|
377
|
+
return this.call(() => this.provider.propose(params), "propose").then(unwrapWrite);
|
|
342
378
|
}
|
|
343
379
|
/** Attest/support a proposal (always prompts). */
|
|
344
380
|
attest(params) {
|
|
345
|
-
return
|
|
381
|
+
return this.call(() => this.provider.attest(params), "attest").then(unwrapWrite);
|
|
346
382
|
}
|
|
347
383
|
/** Seal a commit-reveal claim (always prompts; salt stays inside the wallet). */
|
|
348
384
|
sealClaim(params) {
|
|
349
|
-
return
|
|
385
|
+
return this.call(() => this.provider.sealClaim(params), "sealClaim").then(unwrapWrite);
|
|
350
386
|
}
|
|
351
387
|
/** Reveal a previously sealed claim by its sealing txid (always prompts). */
|
|
352
388
|
revealClaim(txid) {
|
|
353
389
|
if (typeof txid !== "string" || !txid) {
|
|
354
390
|
return Promise.reject(new UnsupportedMethodError("revealClaim requires the sealing txid"));
|
|
355
391
|
}
|
|
356
|
-
return
|
|
392
|
+
return this.call(() => this.provider.revealClaim(txid), "revealClaim").then(unwrapWrite);
|
|
357
393
|
}
|
|
358
394
|
};
|
|
359
395
|
async function getWallet(opts = {}) {
|
package/dist/connect.d.cts
CHANGED
|
@@ -57,7 +57,8 @@ interface SealClaimParams {
|
|
|
57
57
|
* so a dApp settling real value MUST merkle-prove the offer and derive the recipient from its on-chain author
|
|
58
58
|
* before building `outputs`. Payment-grade offer verification is the wallet's on-device fill-SPV path (F13;
|
|
59
59
|
* added in cairn-wallet 0.2.60, which binds the fill payment/rebate/fee legs to the merkle-proven offer; see
|
|
60
|
-
* docs/SDK-GUIDE.md verifies-vs-trusts).
|
|
60
|
+
* docs/SDK-GUIDE.md verifies-vs-trusts). The SDK's SPV pre-verify SHIPPED: call `preverifyOffer` or
|
|
61
|
+
* `cairn.verifyOfferForFill(offerId, servedOffer, { pay })` before building `outputs`.
|
|
61
62
|
*/
|
|
62
63
|
interface FillParams {
|
|
63
64
|
proposalId: string;
|
|
@@ -208,7 +209,13 @@ declare function isInstalled(): boolean;
|
|
|
208
209
|
declare class WalletConnection {
|
|
209
210
|
readonly provider: CairnProvider;
|
|
210
211
|
private _addr;
|
|
211
|
-
|
|
212
|
+
/** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
|
|
213
|
+
* approval; 0 disables it. Every provider call (writes especially) races against it. */
|
|
214
|
+
private readonly _timeoutMs;
|
|
215
|
+
constructor(provider: CairnProvider, opts?: {
|
|
216
|
+
timeoutMs?: number;
|
|
217
|
+
});
|
|
218
|
+
private call;
|
|
212
219
|
/** The wallet's version string (e.g. "0.2.x"). */
|
|
213
220
|
get version(): string;
|
|
214
221
|
/** The connected address, if `connect()`/`getAddress()` has resolved. */
|
|
@@ -243,6 +250,8 @@ declare class WalletConnection {
|
|
|
243
250
|
* 0.2.60+, which binds payment/rebate/fee to the merkle-proven offer) is the payment-grade check; see FillParams.
|
|
244
251
|
*/
|
|
245
252
|
fillOffer(params: FillParams): Promise<TxResult>;
|
|
253
|
+
private _fillSpvAdvised;
|
|
254
|
+
private adviseFillSpvCapability;
|
|
246
255
|
/** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
|
|
247
256
|
getPermissions(): Promise<WalletPermission[]>;
|
|
248
257
|
/** Explicitly request permission to see the address (prompts, like connect). */
|
package/dist/connect.d.ts
CHANGED
|
@@ -57,7 +57,8 @@ interface SealClaimParams {
|
|
|
57
57
|
* so a dApp settling real value MUST merkle-prove the offer and derive the recipient from its on-chain author
|
|
58
58
|
* before building `outputs`. Payment-grade offer verification is the wallet's on-device fill-SPV path (F13;
|
|
59
59
|
* added in cairn-wallet 0.2.60, which binds the fill payment/rebate/fee legs to the merkle-proven offer; see
|
|
60
|
-
* docs/SDK-GUIDE.md verifies-vs-trusts).
|
|
60
|
+
* docs/SDK-GUIDE.md verifies-vs-trusts). The SDK's SPV pre-verify SHIPPED: call `preverifyOffer` or
|
|
61
|
+
* `cairn.verifyOfferForFill(offerId, servedOffer, { pay })` before building `outputs`.
|
|
61
62
|
*/
|
|
62
63
|
interface FillParams {
|
|
63
64
|
proposalId: string;
|
|
@@ -208,7 +209,13 @@ declare function isInstalled(): boolean;
|
|
|
208
209
|
declare class WalletConnection {
|
|
209
210
|
readonly provider: CairnProvider;
|
|
210
211
|
private _addr;
|
|
211
|
-
|
|
212
|
+
/** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
|
|
213
|
+
* approval; 0 disables it. Every provider call (writes especially) races against it. */
|
|
214
|
+
private readonly _timeoutMs;
|
|
215
|
+
constructor(provider: CairnProvider, opts?: {
|
|
216
|
+
timeoutMs?: number;
|
|
217
|
+
});
|
|
218
|
+
private call;
|
|
212
219
|
/** The wallet's version string (e.g. "0.2.x"). */
|
|
213
220
|
get version(): string;
|
|
214
221
|
/** The connected address, if `connect()`/`getAddress()` has resolved. */
|
|
@@ -243,6 +250,8 @@ declare class WalletConnection {
|
|
|
243
250
|
* 0.2.60+, which binds payment/rebate/fee to the merkle-proven offer) is the payment-grade check; see FillParams.
|
|
244
251
|
*/
|
|
245
252
|
fillOffer(params: FillParams): Promise<TxResult>;
|
|
253
|
+
private _fillSpvAdvised;
|
|
254
|
+
private adviseFillSpvCapability;
|
|
246
255
|
/** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
|
|
247
256
|
getPermissions(): Promise<WalletPermission[]>;
|
|
248
257
|
/** Explicitly request permission to see the address (prompts, like connect). */
|
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.
|
|
29
|
+
var SDK_VERSION = "0.4.1";
|
|
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. */
|
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.
|
|
28
|
+
var SDK_VERSION = "0.4.1";
|
|
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. */
|
|
@@ -99,7 +99,7 @@ var SubmitInFlightError = class extends CairnError {
|
|
|
99
99
|
this.txid = txid;
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
|
-
var NATIVE_CODES = /* @__PURE__ */ new Set(["
|
|
102
|
+
var NATIVE_CODES = /* @__PURE__ */ new Set(["FIRST_PARTY_ONLY", "RATE_LIMITED", "FORBIDDEN", "INTERNAL"]);
|
|
103
103
|
var NESTED_TRANSIENT = /* @__PURE__ */ new Set(["VERIFY_UNAVAILABLE", "OFFER_UNKNOWN"]);
|
|
104
104
|
var NESTED_TERMINAL = /* @__PURE__ */ new Set([
|
|
105
105
|
"FILL_UNSAFE",
|
|
@@ -143,6 +143,7 @@ function mapSubmitResultError(result) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// src/connect.ts
|
|
146
|
+
var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
|
|
146
147
|
function detectProvider(opts = {}) {
|
|
147
148
|
const timeoutMs = opts.timeoutMs ?? 3e3;
|
|
148
149
|
return new Promise((resolve, reject) => {
|
|
@@ -199,8 +200,27 @@ function unwrapWrite(reply) {
|
|
|
199
200
|
var WalletConnection = class {
|
|
200
201
|
provider;
|
|
201
202
|
_addr = null;
|
|
202
|
-
|
|
203
|
+
/** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
|
|
204
|
+
* approval; 0 disables it. Every provider call (writes especially) races against it. */
|
|
205
|
+
_timeoutMs;
|
|
206
|
+
constructor(provider, opts = {}) {
|
|
203
207
|
this.provider = provider;
|
|
208
|
+
this._timeoutMs = opts.timeoutMs ?? DEFAULT_PROVIDER_TIMEOUT_MS;
|
|
209
|
+
}
|
|
210
|
+
// B7c: race a provider call against the per-call timeout. On timeout, throw a fail-closed CairnError
|
|
211
|
+
// (retryable:false: a write may already have reached the wallet, so never invite a blind auto-retry). The
|
|
212
|
+
// genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
|
|
213
|
+
call(op, label) {
|
|
214
|
+
const ms = this._timeoutMs;
|
|
215
|
+
const invoke = () => new Promise((resolve) => {
|
|
216
|
+
resolve(op());
|
|
217
|
+
});
|
|
218
|
+
if (!Number.isFinite(ms) || ms <= 0) return invoke();
|
|
219
|
+
let timer;
|
|
220
|
+
const timeout = new Promise((_, reject) => {
|
|
221
|
+
timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
|
|
222
|
+
});
|
|
223
|
+
return Promise.race([invoke(), timeout]).finally(() => clearTimeout(timer));
|
|
204
224
|
}
|
|
205
225
|
/** The wallet's version string (e.g. "0.2.x"). */
|
|
206
226
|
get version() {
|
|
@@ -212,13 +232,13 @@ var WalletConnection = class {
|
|
|
212
232
|
}
|
|
213
233
|
/** Request connection. First time per origin prompts the user; afterwards may be silent. */
|
|
214
234
|
async connect() {
|
|
215
|
-
const { addr } = unwrap(await this.provider.connect());
|
|
235
|
+
const { addr } = unwrap(await this.call(() => this.provider.connect(), "connect"));
|
|
216
236
|
this._addr = addr;
|
|
217
237
|
return addr;
|
|
218
238
|
}
|
|
219
239
|
/** Get the connected address (does not force a new connection prompt if already consented). */
|
|
220
240
|
async getAddress() {
|
|
221
|
-
const { addr } = unwrap(await this.provider.getAddress());
|
|
241
|
+
const { addr } = unwrap(await this.call(() => this.provider.getAddress(), "getAddress"));
|
|
222
242
|
this._addr = addr;
|
|
223
243
|
return addr;
|
|
224
244
|
}
|
|
@@ -231,7 +251,7 @@ var WalletConnection = class {
|
|
|
231
251
|
if (typeof this.provider.signIn !== "function") {
|
|
232
252
|
return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
|
|
233
253
|
}
|
|
234
|
-
return
|
|
254
|
+
return this.call(() => this.provider.signIn(), "signIn").then(unwrap);
|
|
235
255
|
}
|
|
236
256
|
/**
|
|
237
257
|
* Audience-bound "Sign in with CSD" (SIWC) — the secure, replay-resistant third-party login.
|
|
@@ -247,7 +267,7 @@ var WalletConnection = class {
|
|
|
247
267
|
if (typeof this.provider.signInWithCsd !== "function") {
|
|
248
268
|
return Promise.reject(new UnsupportedMethodError("this wallet predates Sign-in-with-CSD \u2014 ask the user to update the Cairn Wallet"));
|
|
249
269
|
}
|
|
250
|
-
return
|
|
270
|
+
return this.call(() => this.provider.signInWithCsd(params), "signInWithCsd").then(unwrap);
|
|
251
271
|
}
|
|
252
272
|
/** True if this wallet supports audience-bound SIWC (safe to call `signInWithCsd`). */
|
|
253
273
|
get supportsSiwc() {
|
|
@@ -257,7 +277,7 @@ var WalletConnection = class {
|
|
|
257
277
|
async getCapabilities() {
|
|
258
278
|
if (typeof this.provider.getCapabilities !== "function") return null;
|
|
259
279
|
try {
|
|
260
|
-
return await this.provider.getCapabilities();
|
|
280
|
+
return await this.call(() => this.provider.getCapabilities(), "getCapabilities");
|
|
261
281
|
} catch {
|
|
262
282
|
return null;
|
|
263
283
|
}
|
|
@@ -273,24 +293,40 @@ var WalletConnection = class {
|
|
|
273
293
|
if (typeof this.provider.fillOffer !== "function") {
|
|
274
294
|
return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
|
|
275
295
|
}
|
|
276
|
-
|
|
296
|
+
this.adviseFillSpvCapability();
|
|
297
|
+
return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
|
|
298
|
+
}
|
|
299
|
+
// P75-5 MF-10: ONE capability advisory per connection. The wallet's on-device fill-SPV (0.2.60+) is the
|
|
300
|
+
// payment-grade fill boundary; when the provider SELF-REPORTS an older version, warn the integrator once
|
|
301
|
+
// and STILL RELAY. WARN, NEVER BLOCK: a hard version gate would decline honest fills (version strings are
|
|
302
|
+
// spoofable and unparseable forks exist), and the wallet itself is the enforcement point. Reads only the
|
|
303
|
+
// sync `provider.version`; never an awaited getCapabilities() round-trip on the fill hot path. An
|
|
304
|
+
// unparseable version never warns (no false alarms).
|
|
305
|
+
_fillSpvAdvised = false;
|
|
306
|
+
adviseFillSpvCapability() {
|
|
307
|
+
if (this._fillSpvAdvised) return;
|
|
308
|
+
this._fillSpvAdvised = true;
|
|
309
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(this.provider?.version ?? ""));
|
|
310
|
+
if (!m) return;
|
|
311
|
+
const below = Number(m[1]) === 0 && (Number(m[2]) < 2 || Number(m[2]) === 2 && Number(m[3]) < 60);
|
|
312
|
+
if (below) console.warn(`[cairn-sdk] this Cairn Wallet (v${m[0]}) predates the on-device fill-SPV boundary (cairn-wallet 0.2.60+). The fill proceeds; suggest a wallet update for payment-grade offer verification.`);
|
|
277
313
|
}
|
|
278
314
|
/** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
|
|
279
315
|
async getPermissions() {
|
|
280
316
|
if (typeof this.provider.getPermissions !== "function") return [];
|
|
281
|
-
return unwrap(await this.provider.getPermissions());
|
|
317
|
+
return unwrap(await this.call(() => this.provider.getPermissions(), "getPermissions"));
|
|
282
318
|
}
|
|
283
319
|
/** Explicitly request permission to see the address (prompts, like connect). */
|
|
284
320
|
requestPermissions() {
|
|
285
321
|
if (typeof this.provider.requestPermissions !== "function") {
|
|
286
322
|
return Promise.reject(new UnsupportedMethodError("this wallet predates requestPermissions \u2014 use connect()"));
|
|
287
323
|
}
|
|
288
|
-
return
|
|
324
|
+
return this.call(() => this.provider.requestPermissions(), "requestPermissions").then(unwrap);
|
|
289
325
|
}
|
|
290
326
|
/** Revoke THIS origin's own access (silent). Returns { revoked }. Safe no-op on older wallets. */
|
|
291
327
|
async revokePermissions() {
|
|
292
328
|
if (typeof this.provider.revokePermissions !== "function") return { revoked: false };
|
|
293
|
-
return unwrap(await this.provider.revokePermissions());
|
|
329
|
+
return unwrap(await this.call(() => this.provider.revokePermissions(), "revokePermissions"));
|
|
294
330
|
}
|
|
295
331
|
/**
|
|
296
332
|
* Subscribe to provider events. `accountsChanged` fires with `[]` when this site loses access
|
|
@@ -306,26 +342,26 @@ var WalletConnection = class {
|
|
|
306
342
|
}
|
|
307
343
|
/** Send CSD (always prompts with clear-signing; wallet picks inputs + returns change to itself). */
|
|
308
344
|
send(params) {
|
|
309
|
-
return
|
|
345
|
+
return this.call(() => this.provider.send(params), "send").then(unwrapWrite);
|
|
310
346
|
}
|
|
311
347
|
/** Low-level Propose (always prompts). Prefer `cairn.board.propose()` which computes the hash/uri/expiry. */
|
|
312
348
|
propose(params) {
|
|
313
|
-
return
|
|
349
|
+
return this.call(() => this.provider.propose(params), "propose").then(unwrapWrite);
|
|
314
350
|
}
|
|
315
351
|
/** Attest/support a proposal (always prompts). */
|
|
316
352
|
attest(params) {
|
|
317
|
-
return
|
|
353
|
+
return this.call(() => this.provider.attest(params), "attest").then(unwrapWrite);
|
|
318
354
|
}
|
|
319
355
|
/** Seal a commit-reveal claim (always prompts; salt stays inside the wallet). */
|
|
320
356
|
sealClaim(params) {
|
|
321
|
-
return
|
|
357
|
+
return this.call(() => this.provider.sealClaim(params), "sealClaim").then(unwrapWrite);
|
|
322
358
|
}
|
|
323
359
|
/** Reveal a previously sealed claim by its sealing txid (always prompts). */
|
|
324
360
|
revealClaim(txid) {
|
|
325
361
|
if (typeof txid !== "string" || !txid) {
|
|
326
362
|
return Promise.reject(new UnsupportedMethodError("revealClaim requires the sealing txid"));
|
|
327
363
|
}
|
|
328
|
-
return
|
|
364
|
+
return this.call(() => this.provider.revealClaim(txid), "revealClaim").then(unwrapWrite);
|
|
329
365
|
}
|
|
330
366
|
};
|
|
331
367
|
async function getWallet(opts = {}) {
|
|
@@ -337,6 +373,9 @@ var DISCONNECTED = Object.freeze({ status: "disconnected", account: null, error:
|
|
|
337
373
|
var CairnController = class {
|
|
338
374
|
state = DISCONNECTED;
|
|
339
375
|
conn = null;
|
|
376
|
+
// MF-15: connect/disconnect generation token. disconnect() bumps it; a connect() whose await straddled
|
|
377
|
+
// the bump must never commit "connected" over the torn-down session (a zombie null-connection session).
|
|
378
|
+
epoch = 0;
|
|
340
379
|
listeners = /* @__PURE__ */ new Set();
|
|
341
380
|
getWalletFn;
|
|
342
381
|
detectOpts;
|
|
@@ -389,34 +428,43 @@ var CairnController = class {
|
|
|
389
428
|
}
|
|
390
429
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
391
430
|
connect = async () => {
|
|
431
|
+
const epoch = this.epoch;
|
|
392
432
|
this.setState({ status: "connecting", error: null });
|
|
393
433
|
try {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
this.
|
|
397
|
-
this.
|
|
434
|
+
let conn = this.conn;
|
|
435
|
+
if (!conn) {
|
|
436
|
+
conn = await this.getWalletFn(this.detectOpts);
|
|
437
|
+
if (epoch !== this.epoch) throw new Error("connection was torn down while connecting - call connect() again");
|
|
438
|
+
conn.on("accountsChanged", this.onAccounts);
|
|
439
|
+
conn.on("disconnect", this.onDisconnect);
|
|
440
|
+
this.conn = conn;
|
|
398
441
|
}
|
|
399
|
-
const addr = await
|
|
442
|
+
const addr = await conn.connect();
|
|
443
|
+
if (epoch !== this.epoch) throw new Error("connection was torn down while awaiting approval - call connect() again");
|
|
400
444
|
this.setState({ status: "connected", account: addr, error: null });
|
|
401
445
|
return addr;
|
|
402
446
|
} catch (e) {
|
|
403
|
-
this.setState({ status: "disconnected", account: null, error: e?.message ?? String(e) });
|
|
447
|
+
if (epoch === this.epoch) this.setState({ status: "disconnected", account: null, error: e?.message ?? String(e) });
|
|
404
448
|
throw e;
|
|
405
449
|
}
|
|
406
450
|
};
|
|
407
|
-
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
451
|
+
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
452
|
+
* Local teardown is IMMEDIATE and unconditional; the wallet-side revoke happens after, so a hung
|
|
453
|
+
* provider can never hold a torn-down session in "connected". */
|
|
408
454
|
disconnect = async () => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
} catch {
|
|
412
|
-
}
|
|
455
|
+
this.epoch++;
|
|
456
|
+
const conn = this.conn;
|
|
413
457
|
this.detach();
|
|
414
458
|
this.conn = null;
|
|
415
459
|
this.setState({ status: "disconnected", account: null, error: null });
|
|
460
|
+
try {
|
|
461
|
+
await conn?.revokePermissions();
|
|
462
|
+
} catch {
|
|
463
|
+
}
|
|
416
464
|
};
|
|
417
465
|
/** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
|
|
418
466
|
signInWithCsd = (params) => {
|
|
419
|
-
if (!this.conn) return Promise.reject(new Error("call connect() before signInWithCsd()"));
|
|
467
|
+
if (!this.conn || this.state.status !== "connected") return Promise.reject(new Error("call connect() before signInWithCsd()"));
|
|
420
468
|
return this.conn.signInWithCsd(params);
|
|
421
469
|
};
|
|
422
470
|
};
|
package/dist/controller.d.cts
CHANGED
|
@@ -12,6 +12,7 @@ interface CairnControllerOptions extends DetectOptions {
|
|
|
12
12
|
declare class CairnController {
|
|
13
13
|
private state;
|
|
14
14
|
private conn;
|
|
15
|
+
private epoch;
|
|
15
16
|
private readonly listeners;
|
|
16
17
|
private readonly getWalletFn;
|
|
17
18
|
private readonly detectOpts;
|
|
@@ -28,7 +29,9 @@ declare class CairnController {
|
|
|
28
29
|
private detach;
|
|
29
30
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
30
31
|
connect: () => Promise<string>;
|
|
31
|
-
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
32
|
+
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
33
|
+
* Local teardown is IMMEDIATE and unconditional; the wallet-side revoke happens after, so a hung
|
|
34
|
+
* provider can never hold a torn-down session in "connected". */
|
|
32
35
|
disconnect: () => Promise<void>;
|
|
33
36
|
/** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
|
|
34
37
|
signInWithCsd: (params: SiwcParams) => Promise<SiwcResult>;
|
package/dist/controller.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface CairnControllerOptions extends DetectOptions {
|
|
|
12
12
|
declare class CairnController {
|
|
13
13
|
private state;
|
|
14
14
|
private conn;
|
|
15
|
+
private epoch;
|
|
15
16
|
private readonly listeners;
|
|
16
17
|
private readonly getWalletFn;
|
|
17
18
|
private readonly detectOpts;
|
|
@@ -28,7 +29,9 @@ declare class CairnController {
|
|
|
28
29
|
private detach;
|
|
29
30
|
/** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
|
|
30
31
|
connect: () => Promise<string>;
|
|
31
|
-
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
32
|
+
/** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
|
|
33
|
+
* Local teardown is IMMEDIATE and unconditional; the wallet-side revoke happens after, so a hung
|
|
34
|
+
* provider can never hold a torn-down session in "connected". */
|
|
32
35
|
disconnect: () => Promise<void>;
|
|
33
36
|
/** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
|
|
34
37
|
signInWithCsd: (params: SiwcParams) => Promise<SiwcResult>;
|
package/dist/controller.js
CHANGED