@bwalletx/connect 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +145 -4
- package/assets/protocol/401.jpg +0 -0
- package/assets/protocol/401.mp4 +0 -0
- package/assets/protocol/402.jpg +0 -0
- package/assets/protocol/402.mp4 +0 -0
- package/assets/protocol/403.jpg +0 -0
- package/assets/protocol/403.mp4 +0 -0
- package/dist/access-Z6xQwKVF.d.cts +118 -0
- package/dist/access-Z6xQwKVF.d.ts +118 -0
- package/dist/bwalletx-connect.global.js +41 -3
- package/dist/bwalletx-connect.global.js.map +1 -1
- package/dist/{chunk-NNMUSWPG.js → chunk-CVLXFNAG.js} +123 -1
- package/dist/chunk-CVLXFNAG.js.map +1 -0
- package/dist/chunk-GF6GUDZL.js +404 -0
- package/dist/chunk-GF6GUDZL.js.map +1 -0
- package/dist/chunk-MWD4BCO5.js +182 -0
- package/dist/chunk-MWD4BCO5.js.map +1 -0
- package/dist/chunk-QRXFL66W.js +652 -0
- package/dist/chunk-QRXFL66W.js.map +1 -0
- package/dist/element.cjs +209 -0
- package/dist/element.cjs.map +1 -1
- package/dist/element.js +2 -1
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +706 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -62
- package/dist/index.d.ts +66 -62
- package/dist/index.js +63 -3
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +783 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +98 -0
- package/dist/next.d.ts +98 -0
- package/dist/next.js +198 -0
- package/dist/next.js.map +1 -0
- package/dist/react.cjs +795 -36
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +74 -28
- package/dist/react.d.ts +74 -28
- package/dist/react.js +177 -4
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +742 -10
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +314 -2
- package/dist/server.d.ts +314 -2
- package/dist/server.js +82 -107
- package/dist/server.js.map +1 -1
- package/dist/wallets-VOKIKBbO.d.cts +250 -0
- package/dist/wallets-VOKIKBbO.d.ts +250 -0
- package/package.json +32 -6
- package/dist/chunk-NNMUSWPG.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,33 +17,69 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var src_exports = {};
|
|
22
32
|
__export(src_exports, {
|
|
33
|
+
ACCESS_ERROR_STATUS: () => ACCESS_ERROR_STATUS,
|
|
34
|
+
ACCESS_PROTOCOL: () => ACCESS_PROTOCOL,
|
|
35
|
+
ACCESS_VERSION: () => ACCESS_VERSION,
|
|
36
|
+
AccessError: () => AccessError,
|
|
37
|
+
BRC29_PROTOCOL: () => BRC29_PROTOCOL,
|
|
23
38
|
BWALLETX_RDNS: () => BWALLETX_RDNS,
|
|
24
39
|
GET_URL: () => GET_URL,
|
|
25
40
|
LOGIN_PROTOCOL: () => LOGIN_PROTOCOL,
|
|
26
41
|
LOGO_URL: () => LOGO_URL,
|
|
42
|
+
PROTOCOL_MEDIA: () => PROTOCOL_MEDIA,
|
|
43
|
+
PaymentError: () => PaymentError,
|
|
27
44
|
RDNS_EXTENSION: () => RDNS_EXTENSION,
|
|
28
45
|
RDNS_MOBILE: () => RDNS_MOBILE,
|
|
29
46
|
SIGNIN_CSS: () => SIGNIN_CSS,
|
|
30
47
|
SIGNIN_TAG: () => SIGNIN_TAG,
|
|
31
48
|
WEB_WALLET_URL: () => WEB_WALLET_URL,
|
|
49
|
+
accessErrorMessage: () => accessErrorMessage,
|
|
50
|
+
amountInSats: () => amountInSats,
|
|
51
|
+
brc100Option: () => brc100Option,
|
|
32
52
|
buildAuthorizeUrl: () => buildAuthorizeUrl,
|
|
53
|
+
buyToken: () => buyToken,
|
|
54
|
+
canonicalJson: () => canonicalJson,
|
|
33
55
|
clearPairing: () => clearPairing,
|
|
34
56
|
connectBwalletX: () => connectBwalletX,
|
|
35
57
|
defineBwalletxSignin: () => defineBwalletxSignin,
|
|
36
58
|
discoverBwalletX: () => discoverBwalletX,
|
|
59
|
+
discoverWallets: () => discoverWallets,
|
|
37
60
|
findBwalletX: () => findBwalletX,
|
|
61
|
+
handcashAdapter: () => handcashAdapter,
|
|
38
62
|
inOrder: () => inOrder,
|
|
63
|
+
isIdentityKeyHex: () => isIdentityKeyHex,
|
|
64
|
+
isPaymail: () => isPaymail,
|
|
39
65
|
markSvg: () => markSvg,
|
|
40
66
|
pairBwalletX: () => pairBwalletX,
|
|
67
|
+
pairingModal: () => pairingModal,
|
|
68
|
+
pairingOption: () => pairingOption,
|
|
69
|
+
pay: () => pay,
|
|
70
|
+
pickWallet: () => pickWallet,
|
|
71
|
+
protocolMediaUrl: () => protocolMediaUrl,
|
|
72
|
+
proveHoldings: () => proveHoldings,
|
|
73
|
+
qrSvg: () => qrSvg,
|
|
41
74
|
renderButton: () => renderButton,
|
|
75
|
+
requestAccess: () => requestAccess,
|
|
76
|
+
resolvePaymailP2P: () => resolvePaymailP2P,
|
|
42
77
|
restorePairing: () => restorePairing,
|
|
43
78
|
signInWithBwalletX: () => signInWithBwalletX,
|
|
44
|
-
signinText: () => signinText
|
|
79
|
+
signinText: () => signinText,
|
|
80
|
+
throwIfAccessError: () => throwIfAccessError,
|
|
81
|
+
tip: () => tip,
|
|
82
|
+
toAccessError: () => toAccessError
|
|
45
83
|
});
|
|
46
84
|
module.exports = __toCommonJS(src_exports);
|
|
47
85
|
|
|
@@ -480,9 +518,9 @@ var CSS = `
|
|
|
480
518
|
.bwx-signin .bwx-mark{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:#000;flex:none}
|
|
481
519
|
.bwx-signin small{display:block;font-size:11px;font-weight:500;color:rgba(0,0,0,.6)}`;
|
|
482
520
|
var LOGO_URL = "https://bwalletx.com/logo.svg";
|
|
483
|
-
function renderButton(
|
|
484
|
-
const host = typeof
|
|
485
|
-
if (!host) throw new Error(`renderButton: no element ${String(
|
|
521
|
+
function renderButton(el2, opts = {}) {
|
|
522
|
+
const host = typeof el2 === "string" ? document.querySelector(el2) : el2;
|
|
523
|
+
if (!host) throw new Error(`renderButton: no element ${String(el2)}`);
|
|
486
524
|
if (!document.getElementById("bwx-signin-css")) {
|
|
487
525
|
const style = document.createElement("style");
|
|
488
526
|
style.id = "bwx-signin-css";
|
|
@@ -515,6 +553,239 @@ function renderButton(el, opts = {}) {
|
|
|
515
553
|
return b;
|
|
516
554
|
}
|
|
517
555
|
|
|
556
|
+
// src/access-client.ts
|
|
557
|
+
var import_sdk3 = require("@bsv/sdk");
|
|
558
|
+
|
|
559
|
+
// src/access.ts
|
|
560
|
+
var ACCESS_VERSION = 1;
|
|
561
|
+
var ACCESS_PROTOCOL = [2, "bwalletx access"];
|
|
562
|
+
var MAX_HOLDING_PROOFS = 50;
|
|
563
|
+
var ACCESS_ERROR_STATUS = {
|
|
564
|
+
not_installed: 424,
|
|
565
|
+
user_rejected: 403,
|
|
566
|
+
wrong_account: 409,
|
|
567
|
+
insufficient_tokens: 403,
|
|
568
|
+
identity_level_too_low: 403,
|
|
569
|
+
kyc_required: 403,
|
|
570
|
+
indexer_unavailable: 503,
|
|
571
|
+
expired: 401,
|
|
572
|
+
replay: 401,
|
|
573
|
+
invalid_signature: 401,
|
|
574
|
+
bad_request: 400
|
|
575
|
+
};
|
|
576
|
+
var AccessError = class extends Error {
|
|
577
|
+
code;
|
|
578
|
+
have;
|
|
579
|
+
need;
|
|
580
|
+
constructor(code, message, extra = {}) {
|
|
581
|
+
super(message ?? code);
|
|
582
|
+
this.name = "AccessError";
|
|
583
|
+
this.code = code;
|
|
584
|
+
this.have = extra.have;
|
|
585
|
+
this.need = extra.need;
|
|
586
|
+
}
|
|
587
|
+
toJSON() {
|
|
588
|
+
const b = { error: this.code, message: this.message };
|
|
589
|
+
if (this.have !== void 0) b.have = this.have;
|
|
590
|
+
if (this.need !== void 0) b.need = this.need;
|
|
591
|
+
return b;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
var PROTOCOL_MEDIA = {
|
|
595
|
+
"401": { name: "Identity", color: "#22c55e", video: "assets/protocol/401.mp4", poster: "assets/protocol/401.jpg" },
|
|
596
|
+
"402": { name: "Payment", color: "#3b82f6", video: "assets/protocol/402.mp4", poster: "assets/protocol/402.jpg" },
|
|
597
|
+
"403": { name: "Access", color: "#ef4444", video: "assets/protocol/403.mp4", poster: "assets/protocol/403.jpg" }
|
|
598
|
+
};
|
|
599
|
+
function protocolMediaUrl(kind, which, version = "latest") {
|
|
600
|
+
return `https://unpkg.com/@bwalletx/connect@${version}/${PROTOCOL_MEDIA[kind][which]}`;
|
|
601
|
+
}
|
|
602
|
+
function canonicalJson(v) {
|
|
603
|
+
if (v === null) return "null";
|
|
604
|
+
if (typeof v === "string" || typeof v === "boolean") return JSON.stringify(v);
|
|
605
|
+
if (typeof v === "number") {
|
|
606
|
+
if (!Number.isFinite(v)) throw new Error("canonicalJson: non-finite number");
|
|
607
|
+
return JSON.stringify(v);
|
|
608
|
+
}
|
|
609
|
+
if (Array.isArray(v)) return `[${v.map((x) => canonicalJson(x)).join(",")}]`;
|
|
610
|
+
if (typeof v === "object") {
|
|
611
|
+
const o = v;
|
|
612
|
+
const keys = Object.keys(o).filter((k) => o[k] !== void 0).sort();
|
|
613
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(o[k])}`).join(",")}}`;
|
|
614
|
+
}
|
|
615
|
+
throw new Error(`canonicalJson: unsupported ${typeof v}`);
|
|
616
|
+
}
|
|
617
|
+
function challengeBytes(challenge) {
|
|
618
|
+
return Array.from(new TextEncoder().encode(challenge));
|
|
619
|
+
}
|
|
620
|
+
var TOKEN_ID = /^bsv21:[0-9a-f]{64}_\d{1,6}$/;
|
|
621
|
+
var RAW = /^[1-9]\d{0,38}$/;
|
|
622
|
+
function normaliseRequirement(r) {
|
|
623
|
+
if (!r || typeof r !== "object") throw new AccessError("bad_request", "require must be an object");
|
|
624
|
+
const req = r;
|
|
625
|
+
const out = {};
|
|
626
|
+
if (req.token !== void 0) {
|
|
627
|
+
const t = req.token;
|
|
628
|
+
if (!t || typeof t !== "object" || typeof t.id !== "string") throw new AccessError("bad_request", "require.token.id is required");
|
|
629
|
+
const id = t.id.trim().toLowerCase();
|
|
630
|
+
if (!TOKEN_ID.test(id)) throw new AccessError("bad_request", "require.token.id must be 'bsv21:<txid>_<vout>'");
|
|
631
|
+
const hasMin = t.min !== void 0;
|
|
632
|
+
const hasBps = t.minBps !== void 0;
|
|
633
|
+
if (hasMin === hasBps) throw new AccessError("bad_request", "require.token needs exactly one of min or minBps");
|
|
634
|
+
out.token = { id };
|
|
635
|
+
if (hasMin) {
|
|
636
|
+
if (typeof t.min !== "string" || !RAW.test(t.min)) throw new AccessError("bad_request", "require.token.min must be a positive integer string (raw units)");
|
|
637
|
+
out.token.min = t.min;
|
|
638
|
+
} else {
|
|
639
|
+
if (!Number.isInteger(t.minBps) || t.minBps < 1 || t.minBps > 1e4) {
|
|
640
|
+
throw new AccessError("bad_request", "require.token.minBps must be an integer 1..10000");
|
|
641
|
+
}
|
|
642
|
+
out.token.minBps = t.minBps;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (req.identityLevel !== void 0) {
|
|
646
|
+
if (!Number.isInteger(req.identityLevel) || req.identityLevel < 1 || req.identityLevel > 10) {
|
|
647
|
+
throw new AccessError("bad_request", "require.identityLevel must be an integer 1..10");
|
|
648
|
+
}
|
|
649
|
+
out.identityLevel = req.identityLevel;
|
|
650
|
+
}
|
|
651
|
+
if (req.kyc !== void 0) {
|
|
652
|
+
if (typeof req.kyc !== "boolean") throw new AccessError("bad_request", "require.kyc must be a boolean");
|
|
653
|
+
if (req.kyc) out.kyc = true;
|
|
654
|
+
}
|
|
655
|
+
if (!out.token && !out.identityLevel && !out.kyc) throw new AccessError("bad_request", "require is empty");
|
|
656
|
+
return out;
|
|
657
|
+
}
|
|
658
|
+
function parseAccessChallenge(s) {
|
|
659
|
+
if (typeof s !== "string" || s.length > 4096) throw new AccessError("bad_request", "challenge must be a string");
|
|
660
|
+
let c;
|
|
661
|
+
try {
|
|
662
|
+
c = JSON.parse(s);
|
|
663
|
+
} catch {
|
|
664
|
+
throw new AccessError("bad_request", "challenge is not JSON");
|
|
665
|
+
}
|
|
666
|
+
if (!c || typeof c !== "object" || c.v !== 1) throw new AccessError("bad_request", "unsupported challenge version");
|
|
667
|
+
if (c.kind !== "401" && c.kind !== "402" && c.kind !== "403") throw new AccessError("bad_request", "bad kind");
|
|
668
|
+
if (typeof c.origin !== "string" || typeof c.nonce !== "string" || !/^[A-Za-z0-9_-]{16,64}$/.test(c.nonce)) {
|
|
669
|
+
throw new AccessError("bad_request", "bad origin or nonce");
|
|
670
|
+
}
|
|
671
|
+
if (!Number.isInteger(c.iat) || !Number.isInteger(c.exp) || c.exp <= c.iat) throw new AccessError("bad_request", "bad iat/exp");
|
|
672
|
+
if (c.sub !== void 0 && (typeof c.sub !== "string" || !/^0[23][0-9a-f]{64}$/.test(c.sub))) throw new AccessError("bad_request", "bad sub");
|
|
673
|
+
c.require = normaliseRequirement(c.require);
|
|
674
|
+
if (canonicalJson(c) !== s) throw new AccessError("bad_request", "challenge is not canonical");
|
|
675
|
+
return c;
|
|
676
|
+
}
|
|
677
|
+
function accessErrorMessage(code, e = {}) {
|
|
678
|
+
switch (code) {
|
|
679
|
+
case "not_installed":
|
|
680
|
+
return "No bWalletX (or other BRC-100 wallet) was found.";
|
|
681
|
+
case "user_rejected":
|
|
682
|
+
return "The request was declined in the wallet.";
|
|
683
|
+
case "wrong_account":
|
|
684
|
+
return "The wallet is signed in to a different account than this site expects.";
|
|
685
|
+
case "insufficient_tokens":
|
|
686
|
+
return `Not enough tokens: holding ${e.have ?? 0}, need ${e.need ?? "?"}.`;
|
|
687
|
+
case "identity_level_too_low":
|
|
688
|
+
return `Identity level ${e.have ?? 0} is below the required level ${e.need ?? "?"}.`;
|
|
689
|
+
case "kyc_required":
|
|
690
|
+
return "A verified identity (KYC) is required.";
|
|
691
|
+
case "indexer_unavailable":
|
|
692
|
+
return "Holdings could not be checked right now. Try again shortly.";
|
|
693
|
+
case "expired":
|
|
694
|
+
return "The request expired. Try again.";
|
|
695
|
+
case "replay":
|
|
696
|
+
return "That request was already used. Try again.";
|
|
697
|
+
case "invalid_signature":
|
|
698
|
+
return "The wallet signature did not verify.";
|
|
699
|
+
case "bad_request":
|
|
700
|
+
return "The request was malformed.";
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/access-client.ts
|
|
705
|
+
var ONESAT = [0, "onesat"];
|
|
706
|
+
var LEGACY_ONESAT = [0, "p 1sat"];
|
|
707
|
+
function toAccessError(e) {
|
|
708
|
+
if (e instanceof AccessError) return e;
|
|
709
|
+
const err = e;
|
|
710
|
+
const msg = String(err?.message ?? e ?? "");
|
|
711
|
+
if (err?.code === "NOT_FOUND") return new AccessError("not_installed", msg);
|
|
712
|
+
if (/reject|denied|declin|cancel|user/i.test(msg) || err?.code === "ERR_USER_REJECTED" || err?.status === 4001) {
|
|
713
|
+
return new AccessError("user_rejected", msg);
|
|
714
|
+
}
|
|
715
|
+
return new AccessError("bad_request", msg || "access failed");
|
|
716
|
+
}
|
|
717
|
+
function throwIfAccessError(body) {
|
|
718
|
+
const b = body;
|
|
719
|
+
if (b && typeof b.error === "string") throw new AccessError(b.error, b.message, { have: b.have, need: b.need });
|
|
720
|
+
}
|
|
721
|
+
function hash160FromP2pkh(script) {
|
|
722
|
+
const m = /^76a914([0-9a-f]{40})88ac$/i.exec(script.slice(-50));
|
|
723
|
+
return m ? m[1].toLowerCase() : null;
|
|
724
|
+
}
|
|
725
|
+
async function proveHoldings(wallet, challenge, tokenId, basket = "bsv21") {
|
|
726
|
+
const bare = tokenId.replace(/^bsv21:/, "");
|
|
727
|
+
const list = await wallet.listOutputs({ basket, include: "locking scripts", includeCustomInstructions: true, limit: 1e4 });
|
|
728
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
729
|
+
const data = challengeBytes(challenge);
|
|
730
|
+
for (const out of list.outputs) {
|
|
731
|
+
if (byKey.size >= MAX_HOLDING_PROOFS) break;
|
|
732
|
+
let ci = {};
|
|
733
|
+
try {
|
|
734
|
+
ci = JSON.parse(out.customInstructions ?? "{}");
|
|
735
|
+
} catch {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (ci.id !== bare || !ci.keyID) continue;
|
|
739
|
+
const want = out.lockingScript ? hash160FromP2pkh(out.lockingScript) : null;
|
|
740
|
+
const counterparty = ci.counterparty ?? "self";
|
|
741
|
+
for (const protocolID of ci.protocolID ? [ci.protocolID] : [ONESAT, LEGACY_ONESAT]) {
|
|
742
|
+
const { publicKey } = await wallet.getPublicKey({ protocolID, keyID: ci.keyID, counterparty });
|
|
743
|
+
const h = import_sdk3.Utils.toHex(import_sdk3.Hash.hash160(import_sdk3.PublicKey.fromString(publicKey).encode(true)));
|
|
744
|
+
if (want && h !== want) continue;
|
|
745
|
+
const existing = byKey.get(publicKey);
|
|
746
|
+
if (existing) {
|
|
747
|
+
existing.outpoints.push(out.outpoint.replace(".", "_"));
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
const { signature } = await wallet.createSignature({ protocolID, keyID: ci.keyID, counterparty, data });
|
|
751
|
+
byKey.set(publicKey, { pubkey: publicKey, signature: Array.from(signature), outpoints: [out.outpoint.replace(".", "_")] });
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return [...byKey.values()];
|
|
756
|
+
}
|
|
757
|
+
async function requestAccess(o) {
|
|
758
|
+
let wallet;
|
|
759
|
+
try {
|
|
760
|
+
({ wallet } = await connectBwalletX({ wallet: o.wallet, pairing: o.pairing, discoverMs: o.discoverMs }));
|
|
761
|
+
} catch (e) {
|
|
762
|
+
throw e?.code === "NOT_FOUND" ? new AccessError("not_installed") : toAccessError(e);
|
|
763
|
+
}
|
|
764
|
+
try {
|
|
765
|
+
const { publicKey: identityKey } = await wallet.getPublicKey({ identityKey: true });
|
|
766
|
+
const { challenge } = await o.challenge(identityKey);
|
|
767
|
+
const c = parseAccessChallenge(challenge);
|
|
768
|
+
if (c.sub && c.sub !== identityKey.toLowerCase()) throw new AccessError("wrong_account");
|
|
769
|
+
if (Date.now() / 1e3 >= c.exp) throw new AccessError("expired");
|
|
770
|
+
const { signature } = await wallet.createSignature({
|
|
771
|
+
protocolID: ACCESS_PROTOCOL,
|
|
772
|
+
keyID: c.nonce,
|
|
773
|
+
counterparty: "anyone",
|
|
774
|
+
data: challengeBytes(challenge)
|
|
775
|
+
});
|
|
776
|
+
const response = { v: 1, challenge, identityKey, signature: Array.from(signature) };
|
|
777
|
+
if (c.require.token) response.holdings = await proveHoldings(wallet, challenge, c.require.token.id, o.tokenBasket);
|
|
778
|
+
let verified;
|
|
779
|
+
if (o.verify) {
|
|
780
|
+
verified = await o.verify(response);
|
|
781
|
+
throwIfAccessError(verified);
|
|
782
|
+
}
|
|
783
|
+
return { response, verified, wallet };
|
|
784
|
+
} catch (e) {
|
|
785
|
+
throw toAccessError(e);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
518
789
|
// src/element.ts
|
|
519
790
|
var SIGNIN_TAG = "bwalletx-signin";
|
|
520
791
|
var GET_URL = "https://bwalletx.com/get";
|
|
@@ -610,6 +881,10 @@ async function postJson(url, body) {
|
|
|
610
881
|
if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
|
|
611
882
|
return data;
|
|
612
883
|
}
|
|
884
|
+
async function postAccess(url, body) {
|
|
885
|
+
const res = await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
886
|
+
return res.json().catch(() => ({ error: "bad_request", message: `HTTP ${res.status}` }));
|
|
887
|
+
}
|
|
613
888
|
var OBSERVED = ["size", "theme", "label", "brand", "subtitle", "loading", "disabled", "full-width", "aria-label", "get-url", "web-url"];
|
|
614
889
|
function createClass() {
|
|
615
890
|
return class BwalletxSigninElement extends HTMLElement {
|
|
@@ -692,6 +967,26 @@ function createClass() {
|
|
|
692
967
|
if (!found) {
|
|
693
968
|
this.#notFound = true;
|
|
694
969
|
this.#emit("bwalletx-not-found");
|
|
970
|
+
if (this.#attr("require-token")) this.#emit("bwalletx-access-denied", { code: "not_installed", message: "No wallet found" });
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
const requireToken = this.#attr("require-token");
|
|
974
|
+
if (requireToken) {
|
|
975
|
+
const verifyUrl2 = this.#attr("verify-url");
|
|
976
|
+
const min = this.#attr("min");
|
|
977
|
+
const minBps = this.#attr("min-bps");
|
|
978
|
+
const hint = { token: { id: requireToken, ...minBps ? { minBps: Number(minBps) } : { min: min ?? "1" } } };
|
|
979
|
+
try {
|
|
980
|
+
const a = await requestAccess({
|
|
981
|
+
wallet: found.wallet,
|
|
982
|
+
challenge: (identityKey) => postJson(challengeUrl, { identityKey, require: hint }),
|
|
983
|
+
verify: verifyUrl2 ? (response2) => postAccess(verifyUrl2, response2) : void 0
|
|
984
|
+
});
|
|
985
|
+
this.#emit("bwalletx-access-granted", { identityKey: a.response.identityKey, response: a.verified });
|
|
986
|
+
} catch (e) {
|
|
987
|
+
const err = e instanceof AccessError ? e : new AccessError("bad_request", String(e));
|
|
988
|
+
this.#emit("bwalletx-access-denied", { code: err.code, message: err.message, have: err.have, need: err.need });
|
|
989
|
+
}
|
|
695
990
|
return;
|
|
696
991
|
}
|
|
697
992
|
const r = await signInWithBwalletX({ wallet: found.wallet, challenge: (identityKey) => postJson(challengeUrl, { identityKey }) });
|
|
@@ -714,29 +1009,435 @@ function defineBwalletxSignin(tag = SIGNIN_TAG) {
|
|
|
714
1009
|
customElements.define(tag, C);
|
|
715
1010
|
return C;
|
|
716
1011
|
}
|
|
1012
|
+
|
|
1013
|
+
// src/pay.ts
|
|
1014
|
+
var import_sdk4 = require("@bsv/sdk");
|
|
1015
|
+
var BRC29_PROTOCOL = [2, "3241645161d8"];
|
|
1016
|
+
var PaymentError = class extends Error {
|
|
1017
|
+
constructor(code, message, maybeSent = false, cause) {
|
|
1018
|
+
super(message);
|
|
1019
|
+
this.code = code;
|
|
1020
|
+
this.maybeSent = maybeSent;
|
|
1021
|
+
this.cause = cause;
|
|
1022
|
+
this.name = "PaymentError";
|
|
1023
|
+
}
|
|
1024
|
+
code;
|
|
1025
|
+
maybeSent;
|
|
1026
|
+
cause;
|
|
1027
|
+
};
|
|
1028
|
+
var isLegacy = (s) => typeof s.pay === "function" && typeof s.createAction !== "function";
|
|
1029
|
+
var isIdentityKeyHex = (s) => /^0[23][0-9a-fA-F]{64}$/.test(s);
|
|
1030
|
+
var isPaymail = (s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
|
|
1031
|
+
function randomB64(n) {
|
|
1032
|
+
const b = new Uint8Array(n);
|
|
1033
|
+
globalThis.crypto.getRandomValues(b);
|
|
1034
|
+
return import_sdk4.Utils.toBase64(Array.from(b));
|
|
1035
|
+
}
|
|
1036
|
+
function opReturn(text) {
|
|
1037
|
+
const d = import_sdk4.Utils.toArray(text, "utf8");
|
|
1038
|
+
const len = d.length;
|
|
1039
|
+
const push = len < 76 ? [len] : len <= 255 ? [76, len] : [77, len & 255, len >> 8];
|
|
1040
|
+
return import_sdk4.Utils.toHex([0, 106, ...push, ...d]);
|
|
1041
|
+
}
|
|
1042
|
+
var clip = (s, n) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
1043
|
+
var describe = (s) => clip(s.length < 5 ? `${s} payment`.trim() : s, 50);
|
|
1044
|
+
async function usdRate(o) {
|
|
1045
|
+
if (typeof o.usdPerBsv === "number") return o.usdPerBsv;
|
|
1046
|
+
try {
|
|
1047
|
+
if (typeof o.usdPerBsv === "function") return await o.usdPerBsv();
|
|
1048
|
+
const r = await (o.fetch ?? fetch)("https://api.whatsonchain.com/v1/bsv/main/exchangerate");
|
|
1049
|
+
const j = await r.json();
|
|
1050
|
+
if (!r.ok || !(Number(j.rate) > 0)) throw new Error(`status ${r.status}`);
|
|
1051
|
+
return Number(j.rate);
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
throw new PaymentError("RATE_FAILED", `Could not get the BSV/USD rate: ${e.message}`, false, e);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
async function amountInSats(req, o = {}) {
|
|
1057
|
+
if (req.sats != null) {
|
|
1058
|
+
if (!Number.isSafeInteger(req.sats) || req.sats <= 0) throw new PaymentError("BAD_REQUEST", "sats must be a positive integer");
|
|
1059
|
+
return req.sats;
|
|
1060
|
+
}
|
|
1061
|
+
if (req.usd != null) {
|
|
1062
|
+
if (!(req.usd > 0)) throw new PaymentError("BAD_REQUEST", "usd must be positive");
|
|
1063
|
+
return Math.max(1, Math.ceil(req.usd / await usdRate(o) * 1e8));
|
|
1064
|
+
}
|
|
1065
|
+
throw new PaymentError("BAD_REQUEST", "Give sats or usd");
|
|
1066
|
+
}
|
|
1067
|
+
async function resolvePaymailP2P(paymail, sats, f = fetch) {
|
|
1068
|
+
const [alias, domain] = paymail.split("@");
|
|
1069
|
+
const fail = (m, e) => new PaymentError("RESOLVE_FAILED", `Could not resolve ${paymail}: ${m}`, false, e);
|
|
1070
|
+
let caps;
|
|
1071
|
+
try {
|
|
1072
|
+
const r = await f(`https://${domain}/.well-known/bsvalias`);
|
|
1073
|
+
caps = (await r.json()).capabilities;
|
|
1074
|
+
} catch (e) {
|
|
1075
|
+
throw fail("no bsvalias document (or CORS blocked it; resolve on your server and pass resolvePaymail)", e);
|
|
1076
|
+
}
|
|
1077
|
+
const fill = (u) => u.replace("{alias}", encodeURIComponent(alias)).replace("{domain.tld}", domain);
|
|
1078
|
+
const dest = caps?.["2a40af698840"];
|
|
1079
|
+
if (!dest) throw fail("it does not offer P2P payment destinations");
|
|
1080
|
+
let body;
|
|
1081
|
+
try {
|
|
1082
|
+
const r = await f(fill(dest), { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ satoshis: sats }) });
|
|
1083
|
+
if (!r.ok) throw new Error(`status ${r.status}`);
|
|
1084
|
+
body = await r.json();
|
|
1085
|
+
} catch (e) {
|
|
1086
|
+
throw fail(e.message, e);
|
|
1087
|
+
}
|
|
1088
|
+
if (!body.outputs?.length) throw fail("no outputs returned");
|
|
1089
|
+
const receive = caps["5f1323cddf31"];
|
|
1090
|
+
return {
|
|
1091
|
+
outputs: body.outputs.map((o) => ({ script: o.script, satoshis: o.satoshis })),
|
|
1092
|
+
reference: body.reference,
|
|
1093
|
+
submit: receive ? async (hex) => {
|
|
1094
|
+
await f(fill(receive), {
|
|
1095
|
+
method: "POST",
|
|
1096
|
+
headers: { "content-type": "application/json" },
|
|
1097
|
+
body: JSON.stringify({ hex, reference: body.reference, metadata: {} })
|
|
1098
|
+
});
|
|
1099
|
+
} : void 0
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
async function runAction(wallet, description, outputs, labels) {
|
|
1103
|
+
try {
|
|
1104
|
+
return await wallet.createAction({
|
|
1105
|
+
description: describe(description),
|
|
1106
|
+
outputs: outputs.map((o) => ({ lockingScript: o.script, satoshis: o.satoshis, outputDescription: describe(o.description ?? "payment") })),
|
|
1107
|
+
labels: labels?.length ? labels : void 0,
|
|
1108
|
+
options: { randomizeOutputs: false }
|
|
1109
|
+
});
|
|
1110
|
+
} catch (e) {
|
|
1111
|
+
const msg = e?.message ?? String(e);
|
|
1112
|
+
const rejected = /denied|reject|cancel|abort|user/i.test(msg) || e?.code === 4001;
|
|
1113
|
+
throw new PaymentError(rejected ? "WALLET_REJECTED" : "WALLET_ERROR", rejected ? "The payment was declined in the wallet." : `The wallet could not make the payment: ${msg}`, !rejected, e);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
async function pay(source, req, o = {}) {
|
|
1117
|
+
const to = (req.to ?? "").trim();
|
|
1118
|
+
if (!to) throw new PaymentError("BAD_REQUEST", "Give a recipient (to)");
|
|
1119
|
+
if (req.memo && req.memo.length > 1e3) throw new PaymentError("BAD_REQUEST", "memo is too long");
|
|
1120
|
+
const sats = await amountInSats(req, o);
|
|
1121
|
+
if (isLegacy(source)) return source.pay({ ...req, to, sats });
|
|
1122
|
+
let outputs;
|
|
1123
|
+
let remittance;
|
|
1124
|
+
let submit;
|
|
1125
|
+
if (isIdentityKeyHex(to)) {
|
|
1126
|
+
const derivationPrefix = randomB64(8);
|
|
1127
|
+
const derivationSuffix = randomB64(8);
|
|
1128
|
+
let pk;
|
|
1129
|
+
let me;
|
|
1130
|
+
try {
|
|
1131
|
+
({ publicKey: pk } = await source.getPublicKey({ protocolID: BRC29_PROTOCOL, keyID: `${derivationPrefix} ${derivationSuffix}`, counterparty: to.toLowerCase() }));
|
|
1132
|
+
({ publicKey: me } = await source.getPublicKey({ identityKey: true }));
|
|
1133
|
+
} catch (e) {
|
|
1134
|
+
throw new PaymentError("WALLET_ERROR", `The wallet could not derive the payment key: ${e.message}`, false, e);
|
|
1135
|
+
}
|
|
1136
|
+
outputs = [{ script: new import_sdk4.P2PKH().lock(import_sdk4.PublicKey.fromString(pk).toHash()).toHex(), satoshis: sats, description: "payment" }];
|
|
1137
|
+
remittance = { senderIdentityKey: me, derivationPrefix, derivationSuffix, outputIndex: 0 };
|
|
1138
|
+
} else if (isPaymail(to)) {
|
|
1139
|
+
const r2 = await (o.resolvePaymail ?? ((p, s) => resolvePaymailP2P(p, s, o.fetch ?? fetch)))(to, sats);
|
|
1140
|
+
outputs = r2.outputs;
|
|
1141
|
+
submit = r2.submit;
|
|
1142
|
+
} else {
|
|
1143
|
+
try {
|
|
1144
|
+
outputs = [{ script: new import_sdk4.P2PKH().lock(to).toHex(), satoshis: sats, description: "payment" }];
|
|
1145
|
+
} catch {
|
|
1146
|
+
throw new PaymentError("BAD_REQUEST", `Not a paymail, address or identity key: ${to}`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
if (req.memo && req.memoOnChain) outputs = [...outputs, { script: opReturn(req.memo), satoshis: 0, description: "memo" }];
|
|
1150
|
+
const r = await runAction(source, req.memo || `Pay ${clip(to, 30)}`, outputs, req.labels);
|
|
1151
|
+
if (!r.txid) throw new PaymentError("WALLET_ERROR", "The wallet did not return a txid", true);
|
|
1152
|
+
const tx = r.tx ? Array.from(r.tx) : void 0;
|
|
1153
|
+
if (submit && tx) {
|
|
1154
|
+
await submit(import_sdk4.Transaction.fromAtomicBEEF(tx).toHex()).catch(() => {
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return { txid: r.txid, tx, sats, outputs, remittance };
|
|
1158
|
+
}
|
|
1159
|
+
function tip(source, req, o = {}) {
|
|
1160
|
+
return pay(source, { memo: "Tip", ...req, labels: [...req.labels ?? [], "tip"] }, o);
|
|
1161
|
+
}
|
|
1162
|
+
async function buyToken(source, req) {
|
|
1163
|
+
if (isLegacy(source)) throw new PaymentError("NOT_SUPPORTED", "This wallet cannot buy tokens. Use a BRC-100 wallet such as bWalletX.");
|
|
1164
|
+
const q = await req.quote({ token: req.token, amount: req.amount });
|
|
1165
|
+
if (!q?.outputs?.length) throw new PaymentError("BAD_REQUEST", "The quote returned no outputs");
|
|
1166
|
+
const sats = q.outputs.reduce((n, x) => n + x.satoshis, 0);
|
|
1167
|
+
const r = await runAction(source, q.description ?? `Buy ${clip(req.token, 30)}`, q.outputs, [...req.labels ?? [], "token-buy"]);
|
|
1168
|
+
if (!r.txid) throw new PaymentError("WALLET_ERROR", "The wallet did not return a txid", true);
|
|
1169
|
+
const res = { txid: r.txid, tx: r.tx ? Array.from(r.tx) : void 0, sats, outputs: q.outputs };
|
|
1170
|
+
await q.onPaid?.(res);
|
|
1171
|
+
return res;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// src/wallets.ts
|
|
1175
|
+
var import_sdk5 = require("@bsv/sdk");
|
|
1176
|
+
async function brc100Connected(wallet, o) {
|
|
1177
|
+
await wallet.waitForAuthentication({}).catch(() => {
|
|
1178
|
+
});
|
|
1179
|
+
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
1180
|
+
return { wallet, payer: wallet, identityKey: publicKey, optionId: o.id, kind: o.kind };
|
|
1181
|
+
}
|
|
1182
|
+
function brc100Option(wallet, info) {
|
|
1183
|
+
const opt = {
|
|
1184
|
+
id: info.id,
|
|
1185
|
+
name: info.name,
|
|
1186
|
+
icon: info.icon,
|
|
1187
|
+
rdns: info.rdns,
|
|
1188
|
+
kind: "brc100",
|
|
1189
|
+
isBwalletX: info.isBwalletX ?? false,
|
|
1190
|
+
connect: () => brc100Connected(wallet, opt)
|
|
1191
|
+
};
|
|
1192
|
+
return opt;
|
|
1193
|
+
}
|
|
1194
|
+
function pairingOption(pairing) {
|
|
1195
|
+
const opt = {
|
|
1196
|
+
id: "bwalletx-pairing",
|
|
1197
|
+
name: "bWalletX on your phone",
|
|
1198
|
+
kind: "pairing",
|
|
1199
|
+
isBwalletX: true,
|
|
1200
|
+
async connect() {
|
|
1201
|
+
const p = typeof pairing === "function" ? pairing() : pairing;
|
|
1202
|
+
const w = await restorePairing(p) ?? await pairBwalletX(p);
|
|
1203
|
+
return brc100Connected(w, opt);
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
return opt;
|
|
1207
|
+
}
|
|
1208
|
+
function handcashAdapter(o) {
|
|
1209
|
+
const opt = {
|
|
1210
|
+
id: "handcash",
|
|
1211
|
+
name: "HandCash",
|
|
1212
|
+
icon: o.icon,
|
|
1213
|
+
kind: "handcash",
|
|
1214
|
+
isBwalletX: false,
|
|
1215
|
+
async connect() {
|
|
1216
|
+
const p = await o.login();
|
|
1217
|
+
return {
|
|
1218
|
+
wallet: null,
|
|
1219
|
+
payer: o.pay ? { pay: o.pay } : null,
|
|
1220
|
+
identityKey: p.identityKey ?? null,
|
|
1221
|
+
handle: p.handle,
|
|
1222
|
+
paymail: p.paymail ?? `${p.handle.replace(/^\$/, "")}@handcash.io`,
|
|
1223
|
+
optionId: opt.id,
|
|
1224
|
+
kind: "handcash",
|
|
1225
|
+
transitional: true
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
return opt;
|
|
1230
|
+
}
|
|
1231
|
+
var NAMES = { [RDNS_EXTENSION]: "bWalletX", "space.bwallet.mobile": "bWalletX" };
|
|
1232
|
+
async function probe(substrate, ms) {
|
|
1233
|
+
try {
|
|
1234
|
+
const w = new import_sdk5.WalletClient(substrate);
|
|
1235
|
+
const ok = await Promise.race([w.isAuthenticated({}).then(() => true), new Promise((r) => setTimeout(() => r(false), ms))]);
|
|
1236
|
+
return ok ? w : null;
|
|
1237
|
+
} catch {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async function discoverWallets(o = {}) {
|
|
1242
|
+
const out = [];
|
|
1243
|
+
const ms = o.ms ?? 1200;
|
|
1244
|
+
if (typeof window !== "undefined") {
|
|
1245
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1246
|
+
const onAnnounce = (e) => {
|
|
1247
|
+
const d = e.detail;
|
|
1248
|
+
if (!d?.wallet) return;
|
|
1249
|
+
const rdns = d.info?.rdns ?? d.info?.uuid ?? `wallet-${seen.size}`;
|
|
1250
|
+
if (seen.has(rdns)) return;
|
|
1251
|
+
const isB = BWALLETX_RDNS.includes(rdns);
|
|
1252
|
+
seen.set(rdns, brc100Option(d.wallet, { id: rdns, rdns, name: NAMES[rdns] ?? d.info?.name ?? rdns, icon: d.info?.icon, isBwalletX: isB }));
|
|
1253
|
+
};
|
|
1254
|
+
window.addEventListener("brc100:announceWallet", onAnnounce);
|
|
1255
|
+
window.dispatchEvent(new Event("brc100:requestWallet"));
|
|
1256
|
+
const local = o.probeLocal ? Promise.all([probe("json-api", ms), probe("Cicada", ms)]) : Promise.resolve([null, null]);
|
|
1257
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
1258
|
+
window.removeEventListener("brc100:announceWallet", onAnnounce);
|
|
1259
|
+
out.push(...seen.values());
|
|
1260
|
+
const cwi = window.CWI;
|
|
1261
|
+
if (cwi && ![...seen.values()].some((x) => x.rdns === "space.bwallet.mobile")) {
|
|
1262
|
+
out.push(brc100Option(cwi, { id: "window.CWI", name: "This app\u2019s wallet", isBwalletX: false }));
|
|
1263
|
+
}
|
|
1264
|
+
const [json, cicada] = await local;
|
|
1265
|
+
if (json) out.push(brc100Option(json, { id: "localhost:3321", name: "Desktop wallet (localhost:3321)" }));
|
|
1266
|
+
if (cicada) out.push(brc100Option(cicada, { id: "localhost:3301", name: "Desktop wallet (localhost:3301)" }));
|
|
1267
|
+
}
|
|
1268
|
+
if (o.pairing) out.push(pairingOption(o.pairing));
|
|
1269
|
+
out.push(...o.extra ?? []);
|
|
1270
|
+
const rank = (w) => w.isBwalletX ? 0 : w.kind === "handcash" ? 2 : 1;
|
|
1271
|
+
return out.map((w, i) => ({ w, i })).sort((a, b) => rank(a.w) - rank(b.w) || a.i - b.i).map((x) => x.w);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// src/modal.ts
|
|
1275
|
+
var import_qrcode_generator = __toESM(require("qrcode-generator"), 1);
|
|
1276
|
+
var CSS2 = `
|
|
1277
|
+
.bwx-ov{position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;padding:16px;font:15px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
|
|
1278
|
+
.bwx-box{background:#0b0b0b;color:#f4f1e8;border:1px solid #3a3220;border-radius:16px;width:min(360px,100%);padding:20px;box-shadow:0 20px 60px rgba(0,0,0,.5)}
|
|
1279
|
+
.bwx-box h2{margin:0 0 6px;font-size:18px;color:#e8c766}
|
|
1280
|
+
.bwx-box p{margin:0 0 14px;color:#bdb6a3;font-size:14px}
|
|
1281
|
+
.bwx-qr{background:#fff;border-radius:12px;padding:12px;display:flex;justify-content:center}
|
|
1282
|
+
.bwx-qr svg{width:100%;height:auto;max-width:260px}
|
|
1283
|
+
.bwx-code{margin:14px 0 0;text-align:center;font-size:28px;letter-spacing:.3em;color:#e8c766;font-variant-numeric:tabular-nums}
|
|
1284
|
+
.bwx-row{display:flex;gap:8px;margin-top:14px}
|
|
1285
|
+
.bwx-btn{flex:1;border:1px solid #3a3220;background:#15130e;color:#f4f1e8;border-radius:10px;padding:10px 12px;font:inherit;cursor:pointer}
|
|
1286
|
+
.bwx-btn:hover{border-color:#e8c766}
|
|
1287
|
+
.bwx-list{display:flex;flex-direction:column;gap:8px}
|
|
1288
|
+
.bwx-opt{display:flex;align-items:center;gap:10px;text-align:left}
|
|
1289
|
+
.bwx-opt img{width:24px;height:24px;border-radius:6px}
|
|
1290
|
+
.bwx-tag{margin-left:auto;font-size:11px;color:#8d866f}
|
|
1291
|
+
`;
|
|
1292
|
+
function mount() {
|
|
1293
|
+
const root = document.createElement("div");
|
|
1294
|
+
root.className = "bwx-ov";
|
|
1295
|
+
root.setAttribute("role", "dialog");
|
|
1296
|
+
root.setAttribute("aria-modal", "true");
|
|
1297
|
+
const style = document.createElement("style");
|
|
1298
|
+
style.textContent = CSS2;
|
|
1299
|
+
const box = document.createElement("div");
|
|
1300
|
+
box.className = "bwx-box";
|
|
1301
|
+
root.append(style, box);
|
|
1302
|
+
document.body.appendChild(root);
|
|
1303
|
+
return { root, box, close: () => root.remove() };
|
|
1304
|
+
}
|
|
1305
|
+
function el(tag, cls, text) {
|
|
1306
|
+
const e = document.createElement(tag);
|
|
1307
|
+
if (cls) e.className = cls;
|
|
1308
|
+
if (text != null) e.textContent = text;
|
|
1309
|
+
return e;
|
|
1310
|
+
}
|
|
1311
|
+
function qrSvg(text) {
|
|
1312
|
+
const q = (0, import_qrcode_generator.default)(0, "M");
|
|
1313
|
+
q.addData(text);
|
|
1314
|
+
q.make();
|
|
1315
|
+
return q.createSvgTag({ cellSize: 4, margin: 2, scalable: true });
|
|
1316
|
+
}
|
|
1317
|
+
function pairingModal(o = {}) {
|
|
1318
|
+
const ac = new AbortController();
|
|
1319
|
+
let ui = null;
|
|
1320
|
+
let codeEl = null;
|
|
1321
|
+
const close = () => {
|
|
1322
|
+
ui?.close();
|
|
1323
|
+
ui = null;
|
|
1324
|
+
};
|
|
1325
|
+
ac.signal.addEventListener("abort", close);
|
|
1326
|
+
return {
|
|
1327
|
+
signal: ac.signal,
|
|
1328
|
+
close,
|
|
1329
|
+
onLink(link) {
|
|
1330
|
+
if (typeof document === "undefined") return;
|
|
1331
|
+
close();
|
|
1332
|
+
ui = mount();
|
|
1333
|
+
const h = el("h2", "", o.title ?? "Connect bWalletX");
|
|
1334
|
+
const p = el("p", "", "Scan with bWalletX on your phone, or paste the link into web.bwalletx.com.");
|
|
1335
|
+
const qr = el("div", "bwx-qr");
|
|
1336
|
+
qr.innerHTML = qrSvg(link);
|
|
1337
|
+
codeEl = el("div", "bwx-code", "");
|
|
1338
|
+
codeEl.setAttribute("aria-live", "polite");
|
|
1339
|
+
const row = el("div", "bwx-row");
|
|
1340
|
+
const copy = el("button", "bwx-btn", "Copy link");
|
|
1341
|
+
copy.onclick = () => void navigator.clipboard?.writeText(link).then(() => copy.textContent = "Copied");
|
|
1342
|
+
const cancel = el("button", "bwx-btn", "Cancel");
|
|
1343
|
+
cancel.onclick = () => {
|
|
1344
|
+
o.onCancel?.();
|
|
1345
|
+
ac.abort();
|
|
1346
|
+
};
|
|
1347
|
+
row.append(copy, cancel);
|
|
1348
|
+
ui.box.append(h, p, qr, codeEl, row);
|
|
1349
|
+
},
|
|
1350
|
+
onCode(code) {
|
|
1351
|
+
if (codeEl) {
|
|
1352
|
+
codeEl.textContent = code;
|
|
1353
|
+
const p = ui?.box.querySelector("p");
|
|
1354
|
+
if (p) p.textContent = "Check bWalletX shows the same code, then tap Connect.";
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
function pickWallet(options, o = {}) {
|
|
1360
|
+
if (typeof document === "undefined") return Promise.resolve(null);
|
|
1361
|
+
return new Promise((resolve) => {
|
|
1362
|
+
const ui = mount();
|
|
1363
|
+
const done = (w) => {
|
|
1364
|
+
ui.close();
|
|
1365
|
+
resolve(w);
|
|
1366
|
+
};
|
|
1367
|
+
ui.box.append(el("h2", "", o.title ?? "Choose a wallet"));
|
|
1368
|
+
if (!options.length) ui.box.append(el("p", "", "No wallet found. Get bWalletX at bwalletx.com."));
|
|
1369
|
+
const list = el("div", "bwx-list");
|
|
1370
|
+
for (const w of options) {
|
|
1371
|
+
const b = el("button", "bwx-btn bwx-opt");
|
|
1372
|
+
if (w.icon) {
|
|
1373
|
+
const img = el("img");
|
|
1374
|
+
img.src = w.icon;
|
|
1375
|
+
img.alt = "";
|
|
1376
|
+
b.append(img);
|
|
1377
|
+
}
|
|
1378
|
+
b.append(el("span", "", w.name));
|
|
1379
|
+
if (w.kind === "handcash") b.append(el("span", "bwx-tag", "until BRC-100"));
|
|
1380
|
+
b.onclick = () => done(w);
|
|
1381
|
+
list.append(b);
|
|
1382
|
+
}
|
|
1383
|
+
const cancel = el("button", "bwx-btn", "Cancel");
|
|
1384
|
+
cancel.onclick = () => done(null);
|
|
1385
|
+
ui.root.onclick = (e) => e.target === ui.root && done(null);
|
|
1386
|
+
ui.box.append(list, el("div", "bwx-row"));
|
|
1387
|
+
ui.box.lastChild.append(cancel);
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
717
1390
|
// Annotate the CommonJS export names for ESM import in node:
|
|
718
1391
|
0 && (module.exports = {
|
|
1392
|
+
ACCESS_ERROR_STATUS,
|
|
1393
|
+
ACCESS_PROTOCOL,
|
|
1394
|
+
ACCESS_VERSION,
|
|
1395
|
+
AccessError,
|
|
1396
|
+
BRC29_PROTOCOL,
|
|
719
1397
|
BWALLETX_RDNS,
|
|
720
1398
|
GET_URL,
|
|
721
1399
|
LOGIN_PROTOCOL,
|
|
722
1400
|
LOGO_URL,
|
|
1401
|
+
PROTOCOL_MEDIA,
|
|
1402
|
+
PaymentError,
|
|
723
1403
|
RDNS_EXTENSION,
|
|
724
1404
|
RDNS_MOBILE,
|
|
725
1405
|
SIGNIN_CSS,
|
|
726
1406
|
SIGNIN_TAG,
|
|
727
1407
|
WEB_WALLET_URL,
|
|
1408
|
+
accessErrorMessage,
|
|
1409
|
+
amountInSats,
|
|
1410
|
+
brc100Option,
|
|
728
1411
|
buildAuthorizeUrl,
|
|
1412
|
+
buyToken,
|
|
1413
|
+
canonicalJson,
|
|
729
1414
|
clearPairing,
|
|
730
1415
|
connectBwalletX,
|
|
731
1416
|
defineBwalletxSignin,
|
|
732
1417
|
discoverBwalletX,
|
|
1418
|
+
discoverWallets,
|
|
733
1419
|
findBwalletX,
|
|
1420
|
+
handcashAdapter,
|
|
734
1421
|
inOrder,
|
|
1422
|
+
isIdentityKeyHex,
|
|
1423
|
+
isPaymail,
|
|
735
1424
|
markSvg,
|
|
736
1425
|
pairBwalletX,
|
|
1426
|
+
pairingModal,
|
|
1427
|
+
pairingOption,
|
|
1428
|
+
pay,
|
|
1429
|
+
pickWallet,
|
|
1430
|
+
protocolMediaUrl,
|
|
1431
|
+
proveHoldings,
|
|
1432
|
+
qrSvg,
|
|
737
1433
|
renderButton,
|
|
1434
|
+
requestAccess,
|
|
1435
|
+
resolvePaymailP2P,
|
|
738
1436
|
restorePairing,
|
|
739
1437
|
signInWithBwalletX,
|
|
740
|
-
signinText
|
|
1438
|
+
signinText,
|
|
1439
|
+
throwIfAccessError,
|
|
1440
|
+
tip,
|
|
1441
|
+
toAccessError
|
|
741
1442
|
});
|
|
742
1443
|
//# sourceMappingURL=index.cjs.map
|