@pollar/react 0.11.4 → 0.12.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/index.css +390 -32
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +264 -11
- package/dist/index.d.ts +264 -11
- package/dist/index.js +1949 -186
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1942 -188
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { AUTH_ERROR_CODES, WalletType, isPollarClient, PollarClient, isInteractiveAuthAdapter,
|
|
2
|
+
import { AUTH_ERROR_CODES, describeRampAction, mergeRampCountries, transactionErrorMessage, WalletType, isPollarApiError, isPollarClient, PollarClient, isInteractiveAuthAdapter, mergeRampSnapshot, toBaseUnits, fromBaseUnits } from '@pollar/core';
|
|
3
3
|
import { forwardRef, createContext, useState, useCallback, useEffect, useMemo, useRef, useContext, Component } from 'react';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
|
|
@@ -1000,6 +1000,10 @@ var require_QRCode = __commonJS({
|
|
|
1000
1000
|
|
|
1001
1001
|
// src/constants.ts
|
|
1002
1002
|
var LOGO_POLLAR = "https://pollar.xyz/assets/logo_pollar.png";
|
|
1003
|
+
var LOGO_POLLAR_WORDMARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-light.png";
|
|
1004
|
+
var LOGO_POLLAR_WORDMARK_ON_DARK = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-dark.png";
|
|
1005
|
+
var LOGO_POLLAR_MARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-mark-on-light.png";
|
|
1006
|
+
var LOGO_POLLAR_MARK_ON_DARK = "https://www.pollar.xyz/brandkit/pollar-mark-on-dark.png";
|
|
1003
1007
|
var LOGO_FREIGHTER = "https://pollar.xyz/assets/logo_freighter.png";
|
|
1004
1008
|
var LOGO_ALBEDO = "https://pollar.xyz/assets/logo_albedo.svg";
|
|
1005
1009
|
var _modalLog = console;
|
|
@@ -1025,22 +1029,26 @@ var ModalErrorBoundary = class extends Component {
|
|
|
1025
1029
|
return this.props.children;
|
|
1026
1030
|
}
|
|
1027
1031
|
};
|
|
1028
|
-
var PollarModalFooter = (
|
|
1032
|
+
var PollarModalFooter = () => {
|
|
1029
1033
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-footer", children: [
|
|
1030
1034
|
/* @__PURE__ */ jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
|
|
1031
|
-
/* @__PURE__ */
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
/* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
|
|
1037
|
-
/* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
|
|
1038
|
-
"v",
|
|
1039
|
-
"0.11.4"
|
|
1040
|
-
] })
|
|
1035
|
+
/* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_LIGHT, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-light" }),
|
|
1036
|
+
/* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_DARK, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-dark" }),
|
|
1037
|
+
/* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
|
|
1038
|
+
"v",
|
|
1039
|
+
"0.12.0-rc.2"
|
|
1041
1040
|
] })
|
|
1042
1041
|
] });
|
|
1043
1042
|
};
|
|
1043
|
+
function explorerTxUrl(chain, hash, network) {
|
|
1044
|
+
if (chain === "SOLANA") {
|
|
1045
|
+
return `https://explorer.solana.com/tx/${hash}${network === "testnet" ? "?cluster=devnet" : ""}`;
|
|
1046
|
+
}
|
|
1047
|
+
if (chain === "POLYGON") {
|
|
1048
|
+
return `https://${network === "testnet" ? "amoy." : ""}polygonscan.com/tx/${hash}`;
|
|
1049
|
+
}
|
|
1050
|
+
return `https://stellar.expert/explorer/${network === "testnet" ? "testnet" : "public"}/tx/${hash}`;
|
|
1051
|
+
}
|
|
1044
1052
|
function RefreshIcon({ spinning = false, className = "" }) {
|
|
1045
1053
|
return /* @__PURE__ */ jsxs(
|
|
1046
1054
|
"svg",
|
|
@@ -1232,6 +1240,8 @@ function buildModalCssVars(theme, accentColor, overrides = {}, variant = "compac
|
|
|
1232
1240
|
"--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
|
|
1233
1241
|
"--pollar-error-text": isDark ? "#f87171" : "#dc2626",
|
|
1234
1242
|
"--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
|
|
1243
|
+
"--pollar-logo-on-light-display": isDark ? "none" : "block",
|
|
1244
|
+
"--pollar-logo-on-dark-display": isDark ? "block" : "none",
|
|
1235
1245
|
"--pollar-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
|
|
1236
1246
|
"--pollar-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS),
|
|
1237
1247
|
"--pollar-buttons-height": "44px",
|
|
@@ -1903,7 +1913,7 @@ function KycStatus({ status, label, className }) {
|
|
|
1903
1913
|
|
|
1904
1914
|
// src/components/kyc-modal/kyc-messages.ts
|
|
1905
1915
|
function kycErrorMessage(error, phase) {
|
|
1906
|
-
const code = error
|
|
1916
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
1907
1917
|
switch (code) {
|
|
1908
1918
|
case "SDK_KYC_NOT_CONFIGURED":
|
|
1909
1919
|
return "Identity verification is not available for this app yet.";
|
|
@@ -2148,7 +2158,13 @@ function KycModal({
|
|
|
2148
2158
|
finish("approved");
|
|
2149
2159
|
return;
|
|
2150
2160
|
}
|
|
2151
|
-
|
|
2161
|
+
const { sessionId } = result;
|
|
2162
|
+
if (sessionId === void 0) throw new Error("The KYC provider answered without a session.");
|
|
2163
|
+
setSession({
|
|
2164
|
+
sessionId,
|
|
2165
|
+
...result.kycUrl !== void 0 ? { kycUrl: result.kycUrl } : {},
|
|
2166
|
+
...result.fields !== void 0 ? { fields: result.fields } : {}
|
|
2167
|
+
});
|
|
2152
2168
|
setStep("verifying");
|
|
2153
2169
|
} catch (e) {
|
|
2154
2170
|
const code = isPollarApiError(e) ? e.code : void 0;
|
|
@@ -2335,10 +2351,14 @@ function fieldErrorMessage(code, language) {
|
|
|
2335
2351
|
}
|
|
2336
2352
|
function fieldErrorsOf(error) {
|
|
2337
2353
|
if (!error || typeof error !== "object") return null;
|
|
2338
|
-
|
|
2339
|
-
|
|
2354
|
+
if (!("code" in error) || error.code !== "KYC_FORM_INVALID_ANSWERS") return null;
|
|
2355
|
+
const body = "body" in error ? error.body : void 0;
|
|
2356
|
+
const list = typeof body === "object" && body !== null && "errors" in body ? body.errors : void 0;
|
|
2357
|
+
if (!Array.isArray(list)) return null;
|
|
2358
|
+
const items = list;
|
|
2340
2359
|
const errors = {};
|
|
2341
|
-
for (const item of
|
|
2360
|
+
for (const item of items) {
|
|
2361
|
+
if (typeof item !== "object" || item === null || !("key" in item) || !("code" in item)) continue;
|
|
2342
2362
|
if (typeof item.key === "string" && typeof item.code === "string") errors[item.key] = item.code;
|
|
2343
2363
|
}
|
|
2344
2364
|
return Object.keys(errors).length ? errors : null;
|
|
@@ -3237,7 +3257,11 @@ function LoginModal({ onClose }) {
|
|
|
3237
3257
|
const [email, setEmail] = useState("");
|
|
3238
3258
|
const { getClient, styles, appConfig: config, configStatus, retryConfig } = usePollar();
|
|
3239
3259
|
const [authState, setAuthState] = useState(() => getClient().getAuthState());
|
|
3240
|
-
const
|
|
3260
|
+
const servedChains = config?.application?.chains;
|
|
3261
|
+
const walletAdapters = useMemo(
|
|
3262
|
+
() => getClient().listWalletAdapters().filter((a) => !servedChains || servedChains.includes(a.chain)),
|
|
3263
|
+
[getClient, servedChains]
|
|
3264
|
+
);
|
|
3241
3265
|
const [codeInputKey, setCodeInputKey] = useState(0);
|
|
3242
3266
|
const pendingEmail = useRef(null);
|
|
3243
3267
|
const [interactiveAdapter, setInteractiveAdapter] = useState(null);
|
|
@@ -3384,13 +3408,200 @@ function LoginModal({ onClose }) {
|
|
|
3384
3408
|
) });
|
|
3385
3409
|
}
|
|
3386
3410
|
|
|
3411
|
+
// src/lib/qr-code/index.tsx
|
|
3412
|
+
var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
|
|
3413
|
+
var import_QRCode = __toESM(require_QRCode());
|
|
3414
|
+
var QRCodeSvg = forwardRef(function QRCodeSvg2({ bgColor, bgD, fgD, fgColor, size, title, viewBoxSize, xmlns = "http://www.w3.org/2000/svg", ...props }, ref) {
|
|
3415
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, height: size, ref, viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, width: size, xmlns, children: [
|
|
3416
|
+
title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
|
|
3417
|
+
/* @__PURE__ */ jsx("path", { d: bgD, fill: bgColor }),
|
|
3418
|
+
/* @__PURE__ */ jsx("path", { d: fgD, fill: fgColor })
|
|
3419
|
+
] });
|
|
3420
|
+
});
|
|
3421
|
+
QRCodeSvg.displayName = "QRCodeSvg";
|
|
3422
|
+
function bytesToBinaryString(bytes) {
|
|
3423
|
+
return bytes.map((b) => String.fromCharCode(b & 255)).join("");
|
|
3424
|
+
}
|
|
3425
|
+
function encodeStringToUtf8Bytes(input) {
|
|
3426
|
+
return Array.from(new TextEncoder().encode(input));
|
|
3427
|
+
}
|
|
3428
|
+
var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#000000", level = "L", size = 256, value, ...props }, ref) {
|
|
3429
|
+
const qrcode = new import_QRCode.default(-1, import_ErrorCorrectLevel.default[level]);
|
|
3430
|
+
const utf8Bytes = encodeStringToUtf8Bytes(value);
|
|
3431
|
+
const binaryString = bytesToBinaryString(utf8Bytes);
|
|
3432
|
+
qrcode.addData(binaryString, "Byte");
|
|
3433
|
+
qrcode.make();
|
|
3434
|
+
const cells = qrcode.modules;
|
|
3435
|
+
return /* @__PURE__ */ jsx(
|
|
3436
|
+
QRCodeSvg,
|
|
3437
|
+
{
|
|
3438
|
+
...props,
|
|
3439
|
+
bgColor,
|
|
3440
|
+
bgD: cells.map(
|
|
3441
|
+
(row, rowIndex) => row.map((cell, cellIndex) => !cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
3442
|
+
).join(" "),
|
|
3443
|
+
fgColor,
|
|
3444
|
+
fgD: cells.map(
|
|
3445
|
+
(row, rowIndex) => row.map((cell, cellIndex) => cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
3446
|
+
).join(" "),
|
|
3447
|
+
ref,
|
|
3448
|
+
size,
|
|
3449
|
+
viewBoxSize: cells.length
|
|
3450
|
+
}
|
|
3451
|
+
);
|
|
3452
|
+
});
|
|
3453
|
+
function RampWorkflow({
|
|
3454
|
+
client,
|
|
3455
|
+
snapshot,
|
|
3456
|
+
onChange,
|
|
3457
|
+
onBusyChange
|
|
3458
|
+
}) {
|
|
3459
|
+
const model = describeRampAction(snapshot);
|
|
3460
|
+
const locked = useRef(false);
|
|
3461
|
+
useEffect(() => {
|
|
3462
|
+
setFields({});
|
|
3463
|
+
setError(null);
|
|
3464
|
+
}, [model.action?.actionId]);
|
|
3465
|
+
const [busy, setBusy] = useState(false);
|
|
3466
|
+
const [error, setError] = useState(null);
|
|
3467
|
+
const [fields, setFields] = useState({});
|
|
3468
|
+
const fieldsComplete = model.action?.kind !== "collect_information" || model.action.fields.every((field) => {
|
|
3469
|
+
const value = (fields[field.key] ?? "").trim();
|
|
3470
|
+
if (!value) return field.optional;
|
|
3471
|
+
if (field.type === "email") return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
3472
|
+
return field.type !== "select" || field.options?.some((option) => option.value === value);
|
|
3473
|
+
});
|
|
3474
|
+
async function continueAction() {
|
|
3475
|
+
if (!model.action || locked.current || !fieldsComplete) return;
|
|
3476
|
+
locked.current = true;
|
|
3477
|
+
setBusy(true);
|
|
3478
|
+
onBusyChange?.(true);
|
|
3479
|
+
setError(null);
|
|
3480
|
+
try {
|
|
3481
|
+
onChange(
|
|
3482
|
+
model.canSign ? await client.signRampAction(snapshot.txId, snapshot) : await client.continueRamp(snapshot.txId, {
|
|
3483
|
+
actionId: model.action.actionId,
|
|
3484
|
+
transactionVersion: snapshot.transactionVersion,
|
|
3485
|
+
...model.action.kind === "collect_information" ? { fields } : {}
|
|
3486
|
+
})
|
|
3487
|
+
);
|
|
3488
|
+
} catch (e) {
|
|
3489
|
+
setError(e instanceof Error ? e.message : "Unable to continue.");
|
|
3490
|
+
} finally {
|
|
3491
|
+
locked.current = false;
|
|
3492
|
+
setBusy(false);
|
|
3493
|
+
onBusyChange?.(false);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
return /* @__PURE__ */ jsxs(
|
|
3497
|
+
"section",
|
|
3498
|
+
{
|
|
3499
|
+
className: "pollar-ramp-payment pollar-ramp-workflow",
|
|
3500
|
+
"aria-label": "Ramp progress",
|
|
3501
|
+
"aria-live": "polite",
|
|
3502
|
+
"aria-busy": busy,
|
|
3503
|
+
children: [
|
|
3504
|
+
/* @__PURE__ */ jsx("h3", { className: "pollar-ramp-payment-title", children: model.title }),
|
|
3505
|
+
snapshot.terms && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-terms", children: [
|
|
3506
|
+
snapshot.terms.fiatAmount,
|
|
3507
|
+
" ",
|
|
3508
|
+
snapshot.terms.fiatCurrency,
|
|
3509
|
+
" \xB7 ",
|
|
3510
|
+
snapshot.terms.cryptoAmount,
|
|
3511
|
+
" ",
|
|
3512
|
+
snapshot.terms.assetCode,
|
|
3513
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
3514
|
+
"Includes ",
|
|
3515
|
+
snapshot.terms.feeAmount,
|
|
3516
|
+
" ",
|
|
3517
|
+
snapshot.terms.feeCurrency,
|
|
3518
|
+
" fee"
|
|
3519
|
+
] }),
|
|
3520
|
+
model.details.map((item, index) => /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-workflow-detail", children: [
|
|
3521
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
3522
|
+
item.label,
|
|
3523
|
+
": "
|
|
3524
|
+
] }),
|
|
3525
|
+
item.value
|
|
3526
|
+
] }, index)),
|
|
3527
|
+
model.links.map((link, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", { className: "pollar-btn-primary pollar-ramp-hosted-link", href: link.url, target: "_blank", rel: "noopener noreferrer", children: link.label }) }, index)),
|
|
3528
|
+
model.qr && /* @__PURE__ */ jsx(QRCode, { value: model.qr, size: 180 }),
|
|
3529
|
+
model.qr && /* @__PURE__ */ jsx(
|
|
3530
|
+
"button",
|
|
3531
|
+
{
|
|
3532
|
+
className: "pollar-btn-secondary",
|
|
3533
|
+
type: "button",
|
|
3534
|
+
onClick: () => void navigator.clipboard.writeText(model.qr).catch(() => setError("Unable to copy. Select the payment code to copy it manually.")),
|
|
3535
|
+
children: "Copy payment code"
|
|
3536
|
+
}
|
|
3537
|
+
),
|
|
3538
|
+
model.action?.kind === "collect_information" && model.action.fields.map((field) => /* @__PURE__ */ jsxs("label", { className: "pollar-ramp-field", children: [
|
|
3539
|
+
/* @__PURE__ */ jsxs("span", { className: "pollar-ramp-label", children: [
|
|
3540
|
+
field.label,
|
|
3541
|
+
field.optional ? " (optional)" : ""
|
|
3542
|
+
] }),
|
|
3543
|
+
field.type === "select" ? /* @__PURE__ */ jsxs(
|
|
3544
|
+
"select",
|
|
3545
|
+
{
|
|
3546
|
+
className: "pollar-ramp-input",
|
|
3547
|
+
disabled: busy,
|
|
3548
|
+
value: fields[field.key] ?? "",
|
|
3549
|
+
onChange: (e) => setFields((previous) => ({ ...previous, [field.key]: e.target.value })),
|
|
3550
|
+
children: [
|
|
3551
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Choose" }),
|
|
3552
|
+
field.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
3553
|
+
]
|
|
3554
|
+
}
|
|
3555
|
+
) : /* @__PURE__ */ jsx(
|
|
3556
|
+
"input",
|
|
3557
|
+
{
|
|
3558
|
+
className: "pollar-ramp-input",
|
|
3559
|
+
disabled: busy,
|
|
3560
|
+
type: field.type,
|
|
3561
|
+
placeholder: field.placeholder,
|
|
3562
|
+
value: fields[field.key] ?? "",
|
|
3563
|
+
onChange: (e) => setFields((previous) => ({ ...previous, [field.key]: e.target.value }))
|
|
3564
|
+
}
|
|
3565
|
+
),
|
|
3566
|
+
field.hint && /* @__PURE__ */ jsx("small", { className: "pollar-ramp-field-hint", children: field.hint })
|
|
3567
|
+
] }, field.key)),
|
|
3568
|
+
(model.canContinue || model.canSign) && /* @__PURE__ */ jsx(
|
|
3569
|
+
"button",
|
|
3570
|
+
{
|
|
3571
|
+
className: "pollar-btn-primary",
|
|
3572
|
+
type: "button",
|
|
3573
|
+
disabled: busy || !fieldsComplete,
|
|
3574
|
+
onClick: () => void continueAction(),
|
|
3575
|
+
children: busy ? model.canSign ? "Waiting for your wallet\u2026" : "Continuing\u2026" : model.canSign ? "Authorize" : "Continue"
|
|
3576
|
+
}
|
|
3577
|
+
),
|
|
3578
|
+
snapshot.milestones?.map((milestone, index) => /* @__PURE__ */ jsxs("p", { children: [
|
|
3579
|
+
milestone.kind.replace(/_/g, " "),
|
|
3580
|
+
" \xB7 ",
|
|
3581
|
+
milestone.verifiedAt
|
|
3582
|
+
] }, index)),
|
|
3583
|
+
error && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-field-error", role: "alert", children: error }),
|
|
3584
|
+
/* @__PURE__ */ jsxs("small", { className: "pollar-ramp-reference", children: [
|
|
3585
|
+
"Reference: ",
|
|
3586
|
+
snapshot.txId
|
|
3587
|
+
] })
|
|
3588
|
+
]
|
|
3589
|
+
}
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3387
3593
|
// src/components/ramp-widget/ramp-kyc.ts
|
|
3388
3594
|
var nonEmpty = (value) => typeof value === "string" && !!value.trim();
|
|
3389
3595
|
function requiredRampKyc(error) {
|
|
3390
3596
|
if (!error || typeof error !== "object") return null;
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3597
|
+
if (!("code" in error) || error.code !== "SDK_RAMPS_KYC_REQUIRED") return null;
|
|
3598
|
+
const body = "body" in error ? error.body : void 0;
|
|
3599
|
+
if (typeof body !== "object" || body === null) return null;
|
|
3600
|
+
const rampProviderId = "rampProviderId" in body ? body.rampProviderId : void 0;
|
|
3601
|
+
const corridorId = "corridorId" in body ? body.corridorId : void 0;
|
|
3602
|
+
const requirementType = "requirementType" in body ? body.requirementType : void 0;
|
|
3603
|
+
const optionId = "optionId" in body ? body.optionId : void 0;
|
|
3604
|
+
const kycProviderId = "kycProviderId" in body ? body.kycProviderId : void 0;
|
|
3394
3605
|
if (!nonEmpty(rampProviderId) || !nonEmpty(corridorId)) return null;
|
|
3395
3606
|
if ((requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") && nonEmpty(optionId)) {
|
|
3396
3607
|
return { rampProviderId, corridorId, type: requirementType, optionId };
|
|
@@ -3457,13 +3668,14 @@ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
|
|
|
3457
3668
|
children: [
|
|
3458
3669
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
|
|
3459
3670
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
|
|
3460
|
-
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" :
|
|
3671
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" : [
|
|
3672
|
+
quote.route ? `${quote.route.asset.code} on ${quote.route.asset.chain}` : null,
|
|
3673
|
+
RAIL_LABELS[quote.rail] ?? quote.rail,
|
|
3674
|
+
quote.estimatedTime
|
|
3675
|
+
].filter(Boolean).join(" \xB7 ") })
|
|
3461
3676
|
] }),
|
|
3462
3677
|
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: busy ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3463
|
-
/* @__PURE__ */
|
|
3464
|
-
quote.fee,
|
|
3465
|
-
"% fee"
|
|
3466
|
-
] }),
|
|
3678
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-fee", children: quote.terms ? `${quote.terms.fiatAmount} ${quote.terms.fiatCurrency} \xB7 ${quote.terms.cryptoAmount} ${quote.terms.assetCode} \xB7 Includes ${quote.terms.feeAmount} ${quote.terms.feeCurrency}` : `${quote.fee}% fee` }),
|
|
3467
3679
|
quote.recommended && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
|
|
3468
3680
|
] }) })
|
|
3469
3681
|
]
|
|
@@ -3522,6 +3734,8 @@ function placeholderFor(field, fields, values) {
|
|
|
3522
3734
|
}
|
|
3523
3735
|
return field.placeholder;
|
|
3524
3736
|
}
|
|
3737
|
+
var CHAIN_NAME = { STELLAR: "Stellar", POLYGON: "Polygon", SOLANA: "Solana" };
|
|
3738
|
+
var EXPLORER_NAME = { STELLAR: "Stellar Expert", POLYGON: "Polygonscan", SOLANA: "Solana Explorer" };
|
|
3525
3739
|
var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
|
|
3526
3740
|
function flagEmoji(code) {
|
|
3527
3741
|
const cc = code.trim().toUpperCase();
|
|
@@ -3541,6 +3755,8 @@ function displayValue(field) {
|
|
|
3541
3755
|
return Number.isNaN(at.getTime()) ? field.value : at.toLocaleString();
|
|
3542
3756
|
}
|
|
3543
3757
|
function RampWidgetTemplate({
|
|
3758
|
+
workflowContent,
|
|
3759
|
+
routeSelector,
|
|
3544
3760
|
theme,
|
|
3545
3761
|
accentColor,
|
|
3546
3762
|
styleOverrides,
|
|
@@ -3568,10 +3784,20 @@ function RampWidgetTemplate({
|
|
|
3568
3784
|
onboardingStatus,
|
|
3569
3785
|
kycJustApproved,
|
|
3570
3786
|
stellarTxHash,
|
|
3787
|
+
txHash,
|
|
3788
|
+
txChain,
|
|
3571
3789
|
explorerUrl,
|
|
3790
|
+
fundsInTransit = false,
|
|
3791
|
+
retrying = false,
|
|
3792
|
+
onRetryTransit,
|
|
3572
3793
|
depositInstructions,
|
|
3573
3794
|
canComplete,
|
|
3574
3795
|
completing,
|
|
3796
|
+
savedRamps = [],
|
|
3797
|
+
onResumeRamp,
|
|
3798
|
+
onDismissRamp,
|
|
3799
|
+
onDismissCurrentRamp,
|
|
3800
|
+
onExploreRoutes,
|
|
3575
3801
|
errorMsg,
|
|
3576
3802
|
noticeMsg,
|
|
3577
3803
|
onDirectionChange,
|
|
@@ -3590,6 +3816,8 @@ function RampWidgetTemplate({
|
|
|
3590
3816
|
onRefresh,
|
|
3591
3817
|
onClose
|
|
3592
3818
|
}) {
|
|
3819
|
+
const settledHash = txHash ?? stellarTxHash;
|
|
3820
|
+
const chainName = CHAIN_NAME[txChain ?? "STELLAR"];
|
|
3593
3821
|
const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
|
|
3594
3822
|
const stepTitle = {
|
|
3595
3823
|
input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
|
|
@@ -3608,7 +3836,7 @@ function RampWidgetTemplate({
|
|
|
3608
3836
|
// money, not who you are. Say which, rather than asserting the Bridge case
|
|
3609
3837
|
// over every provider.
|
|
3610
3838
|
contact: `${provider || "This provider"} needs ${listOf(requiredFields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
|
|
3611
|
-
status: `Finish the flow at ${provider || "the provider"} to continue`,
|
|
3839
|
+
status: workflowContent ? "Follow the next step below to continue" : `Finish the flow at ${provider || "the provider"} to continue`,
|
|
3612
3840
|
error: "Please try again"
|
|
3613
3841
|
};
|
|
3614
3842
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
|
|
@@ -3662,7 +3890,39 @@ function RampWidgetTemplate({
|
|
|
3662
3890
|
}
|
|
3663
3891
|
)
|
|
3664
3892
|
] }),
|
|
3893
|
+
step === "input" && savedRamps.length > 0 && /* @__PURE__ */ jsxs("section", { className: "pollar-ramp-payment pollar-ramp-saved", "aria-label": "Pending operations", children: [
|
|
3894
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your pending operations are saved. Starting another route does not cancel them." }),
|
|
3895
|
+
savedRamps.map((ramp) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-saved-row", children: [
|
|
3896
|
+
/* @__PURE__ */ jsxs(
|
|
3897
|
+
"button",
|
|
3898
|
+
{
|
|
3899
|
+
type: "button",
|
|
3900
|
+
className: "pollar-btn-secondary",
|
|
3901
|
+
disabled: isLoading,
|
|
3902
|
+
onClick: () => onResumeRamp?.(ramp.txId),
|
|
3903
|
+
children: [
|
|
3904
|
+
"Resume ",
|
|
3905
|
+
ramp.provider,
|
|
3906
|
+
" \xB7 ",
|
|
3907
|
+
ramp.txId.slice(-8)
|
|
3908
|
+
]
|
|
3909
|
+
}
|
|
3910
|
+
),
|
|
3911
|
+
onDismissRamp && /* @__PURE__ */ jsx(
|
|
3912
|
+
"button",
|
|
3913
|
+
{
|
|
3914
|
+
type: "button",
|
|
3915
|
+
className: "pollar-btn-secondary",
|
|
3916
|
+
disabled: isLoading,
|
|
3917
|
+
"aria-label": `Remove ${ramp.provider} ${ramp.txId.slice(-8)} from pending list`,
|
|
3918
|
+
onClick: () => onDismissRamp(ramp.txId),
|
|
3919
|
+
children: "Remove"
|
|
3920
|
+
}
|
|
3921
|
+
)
|
|
3922
|
+
] }, ramp.txId))
|
|
3923
|
+
] }),
|
|
3665
3924
|
step === "input" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3925
|
+
noticeMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", role: "status", children: noticeMsg }),
|
|
3666
3926
|
/* @__PURE__ */ jsxs("div", { className: "pollar-tabs", children: [
|
|
3667
3927
|
/* @__PURE__ */ jsx(
|
|
3668
3928
|
"button",
|
|
@@ -3686,25 +3946,36 @@ function RampWidgetTemplate({
|
|
|
3686
3946
|
)
|
|
3687
3947
|
] }),
|
|
3688
3948
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
|
|
3689
|
-
/* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", children: "Country" }),
|
|
3949
|
+
/* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-country", children: "Country" }),
|
|
3690
3950
|
countriesLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-input pollar-select-loading", children: [
|
|
3691
3951
|
/* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
|
|
3692
3952
|
/* @__PURE__ */ jsx("span", { children: "Loading countries\u2026" })
|
|
3693
|
-
] }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsx(
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3953
|
+
] }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsx(
|
|
3954
|
+
"select",
|
|
3955
|
+
{
|
|
3956
|
+
id: "pollar-ramp-country",
|
|
3957
|
+
className: "pollar-ramp-input",
|
|
3958
|
+
value: country,
|
|
3959
|
+
onChange: (e) => onCountryChange(e.target.value),
|
|
3960
|
+
children: countries.map((c) => /* @__PURE__ */ jsxs("option", { value: c.code, children: [
|
|
3961
|
+
flagEmoji(c.code),
|
|
3962
|
+
" ",
|
|
3963
|
+
c.code,
|
|
3964
|
+
c.currency ? ` \u2014 ${c.currency}` : ""
|
|
3965
|
+
] }, c.code))
|
|
3966
|
+
}
|
|
3967
|
+
)
|
|
3699
3968
|
] }),
|
|
3969
|
+
routeSelector,
|
|
3700
3970
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
|
|
3701
|
-
/* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", children: [
|
|
3971
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-amount", children: [
|
|
3702
3972
|
"Amount",
|
|
3703
3973
|
currency ? ` (${currency})` : ""
|
|
3704
3974
|
] }),
|
|
3705
3975
|
/* @__PURE__ */ jsx(
|
|
3706
3976
|
"input",
|
|
3707
3977
|
{
|
|
3978
|
+
id: "pollar-ramp-amount",
|
|
3708
3979
|
type: "number",
|
|
3709
3980
|
className: "pollar-ramp-input",
|
|
3710
3981
|
placeholder: "0.00",
|
|
@@ -3810,7 +4081,16 @@ function RampWidgetTemplate({
|
|
|
3810
4081
|
)
|
|
3811
4082
|
] })
|
|
3812
4083
|
] }),
|
|
3813
|
-
step === "status" &&
|
|
4084
|
+
step === "status" && workflowContent,
|
|
4085
|
+
step === "status" && onDismissCurrentRamp && txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-explore", children: [
|
|
4086
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", disabled: completing, onClick: onDismissCurrentRamp, children: "Remove from pending list" }),
|
|
4087
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "This only hides the operation in this tab. To request cancellation, open the provider link above when available or contact the provider\u2019s support. Payments and refunds must be confirmed by the provider." })
|
|
4088
|
+
] }),
|
|
4089
|
+
step === "status" && onExploreRoutes && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-explore", children: [
|
|
4090
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", disabled: completing, onClick: onExploreRoutes, children: txStatus === "completed" || txStatus === "failed" ? "Start another operation" : "Try another route" }),
|
|
4091
|
+
txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your current operation stays saved so you can resume it later." })
|
|
4092
|
+
] }),
|
|
4093
|
+
step === "status" && !workflowContent && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment", children: [
|
|
3814
4094
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
3815
4095
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Provider" }),
|
|
3816
4096
|
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsx("code", { children: provider }) })
|
|
@@ -3822,15 +4102,23 @@ function RampWidgetTemplate({
|
|
|
3822
4102
|
txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-label": "Checking status" })
|
|
3823
4103
|
] })
|
|
3824
4104
|
] }),
|
|
3825
|
-
|
|
3826
|
-
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "
|
|
4105
|
+
fundsInTransit && txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
4106
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Funds in transit" }),
|
|
4107
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your money is safe in your own wallet, waiting for the next step of the transfer." }),
|
|
4108
|
+
onRetryTransit && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetryTransit, disabled: retrying, children: retrying ? "Retrying..." : "Retry now" })
|
|
4109
|
+
] }),
|
|
4110
|
+
settledHash && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
4111
|
+
/* @__PURE__ */ jsxs("span", { className: "pollar-ramp-payment-label", children: [
|
|
4112
|
+
chainName,
|
|
4113
|
+
" tx"
|
|
4114
|
+
] }),
|
|
3827
4115
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-value", style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
3828
4116
|
/* @__PURE__ */ jsxs("code", { style: { flex: 1, wordBreak: "break-all" }, children: [
|
|
3829
|
-
|
|
4117
|
+
settledHash.slice(0, 8),
|
|
3830
4118
|
"\u2026",
|
|
3831
|
-
|
|
4119
|
+
settledHash.slice(-8)
|
|
3832
4120
|
] }),
|
|
3833
|
-
/* @__PURE__ */ jsx(CopyButton, { value:
|
|
4121
|
+
/* @__PURE__ */ jsx(CopyButton, { value: settledHash, label: "Copy transaction hash" }),
|
|
3834
4122
|
explorerUrl && /* @__PURE__ */ jsx(
|
|
3835
4123
|
"a",
|
|
3836
4124
|
{
|
|
@@ -3838,8 +4126,8 @@ function RampWidgetTemplate({
|
|
|
3838
4126
|
target: "_blank",
|
|
3839
4127
|
rel: "noopener noreferrer",
|
|
3840
4128
|
className: "pollar-copy-btn",
|
|
3841
|
-
"aria-label":
|
|
3842
|
-
title:
|
|
4129
|
+
"aria-label": `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
|
|
4130
|
+
title: `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
|
|
3843
4131
|
children: /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
|
|
3844
4132
|
/* @__PURE__ */ jsx(
|
|
3845
4133
|
"path",
|
|
@@ -3934,7 +4222,7 @@ function RampWidgetTemplate({
|
|
|
3934
4222
|
canComplete && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3935
4223
|
/* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
|
|
3936
4224
|
"Submitting\u2026"
|
|
3937
|
-
] }) : "
|
|
4225
|
+
] }) : "Check verification and continue" }),
|
|
3938
4226
|
errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
|
|
3939
4227
|
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: txStatus === "completed" ? "Done" : "Close" })
|
|
3940
4228
|
] }),
|
|
@@ -4100,19 +4388,26 @@ var SHARED_FIELD_LABELS = {
|
|
|
4100
4388
|
country_code: "Pa\xEDs de residencia"
|
|
4101
4389
|
}
|
|
4102
4390
|
};
|
|
4103
|
-
var errorCode = (error) =>
|
|
4391
|
+
var errorCode = (error) => typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
4392
|
+
var errorBody = (error) => {
|
|
4393
|
+
const body = typeof error === "object" && error !== null && "body" in error ? error.body : void 0;
|
|
4394
|
+
return typeof body === "object" && body !== null ? body : void 0;
|
|
4395
|
+
};
|
|
4104
4396
|
function missingFieldsOf(error) {
|
|
4105
|
-
const body = error
|
|
4106
|
-
|
|
4397
|
+
const body = errorBody(error);
|
|
4398
|
+
const missing = body && "missing" in body ? body.missing : void 0;
|
|
4399
|
+
return Array.isArray(missing) ? missing.filter((item) => typeof item === "string") : [];
|
|
4107
4400
|
}
|
|
4108
4401
|
function registrationFixOf(error) {
|
|
4109
|
-
const body = error
|
|
4402
|
+
const body = errorBody(error);
|
|
4403
|
+
const invalid = body && "invalid" in body ? body.invalid : void 0;
|
|
4404
|
+
const formList = body && "forms" in body ? body.forms : void 0;
|
|
4110
4405
|
const strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
4111
|
-
const forms = Array.isArray(
|
|
4112
|
-
|
|
4113
|
-
return
|
|
4406
|
+
const forms = Array.isArray(formList) ? formList.flatMap((item) => {
|
|
4407
|
+
if (typeof item !== "object" || item === null || !("formId" in item) || typeof item.formId !== "string") return [];
|
|
4408
|
+
return [{ formId: item.formId, keys: strings("keys" in item ? item.keys : void 0) }];
|
|
4114
4409
|
}) : [];
|
|
4115
|
-
return { invalid: strings(
|
|
4410
|
+
return { invalid: strings(invalid), forms };
|
|
4116
4411
|
}
|
|
4117
4412
|
function RegistryCheckModal({ optionId, progress, onClose, onApproved }) {
|
|
4118
4413
|
const { getClient, styles } = usePollar();
|
|
@@ -4432,9 +4727,51 @@ function ProviderRegistrationModal({
|
|
|
4432
4727
|
}
|
|
4433
4728
|
) });
|
|
4434
4729
|
}
|
|
4730
|
+
|
|
4731
|
+
// src/components/ramp-widget/ramp-quotes.ts
|
|
4732
|
+
function unavailableRampMessage(country, unavailable) {
|
|
4733
|
+
if (!unavailable.length) return `No ramp providers available for ${country} yet.`;
|
|
4734
|
+
return unavailable.map(
|
|
4735
|
+
({ provider, code }) => code === "SDK_RAMPS_PROVIDER_NOT_CONFIGURED" ? `${provider} is not configured correctly. Contact the app administrator to enable this provider.` : `${provider} is temporarily unavailable. Please try again later.`
|
|
4736
|
+
).join(" ");
|
|
4737
|
+
}
|
|
4738
|
+
async function quoteRampOptions(client, query, routes) {
|
|
4739
|
+
const matching = routes.filter(
|
|
4740
|
+
(route) => route.country === query.country && route.direction === query.direction && route.fiatCurrency === query.currency
|
|
4741
|
+
);
|
|
4742
|
+
const responses = await Promise.allSettled([
|
|
4743
|
+
client.getRampsQuote(query),
|
|
4744
|
+
...matching.map((route) => client.getRampsQuote({ ...query, routeId: route.routeId, chain: route.asset.chain }))
|
|
4745
|
+
]);
|
|
4746
|
+
const results = responses.flatMap((response) => response.status === "fulfilled" ? [response.value] : []);
|
|
4747
|
+
if (!results.length) throw responses[0].reason;
|
|
4748
|
+
const quotes = /* @__PURE__ */ new Map();
|
|
4749
|
+
const requirements = /* @__PURE__ */ new Map();
|
|
4750
|
+
const unavailable = /* @__PURE__ */ new Map();
|
|
4751
|
+
for (const result of results) {
|
|
4752
|
+
for (const quote of result.quotes) {
|
|
4753
|
+
const key = JSON.stringify([quote.provider, quote.route?.routeId, quote.route?.asset.chain, quote.rail, quote.protocol]);
|
|
4754
|
+
if (!quotes.has(key)) quotes.set(key, quote);
|
|
4755
|
+
}
|
|
4756
|
+
for (const requirement of result.requirementsRequired ?? [])
|
|
4757
|
+
requirements.set(`${requirement.rampProviderId}:${requirement.corridorId}`, requirement);
|
|
4758
|
+
for (const item of result.unavailable ?? []) unavailable.set(item.provider, item);
|
|
4759
|
+
}
|
|
4760
|
+
const failed = responses.find((response) => response.status === "rejected");
|
|
4761
|
+
if (!quotes.size && !requirements.size && failed?.status === "rejected") throw failed.reason;
|
|
4762
|
+
return {
|
|
4763
|
+
quotes: [...quotes.values()],
|
|
4764
|
+
requirementsRequired: [...requirements.values()],
|
|
4765
|
+
unavailable: [...unavailable.values()]
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4435
4768
|
var TERMINAL = ["completed", "failed"];
|
|
4769
|
+
var BANK_TYPES = ["CLABE", "PIX", "PSE", "ACH", "BREB"];
|
|
4770
|
+
function isBankType(value) {
|
|
4771
|
+
return BANK_TYPES.some((type) => type === value);
|
|
4772
|
+
}
|
|
4436
4773
|
function requiredFieldsOf(quote) {
|
|
4437
|
-
return quote.requiredFields
|
|
4774
|
+
return "requiredFields" in quote ? quote.requiredFields : [];
|
|
4438
4775
|
}
|
|
4439
4776
|
function flowStepsOf(quotes, selected) {
|
|
4440
4777
|
const needsDetails = selected != null ? requiredFieldsOf(selected).length > 0 : quotes.some((q) => requiredFieldsOf(q).length > 0);
|
|
@@ -4448,7 +4785,8 @@ var STEP_LABEL = {
|
|
|
4448
4785
|
status: "Complete"
|
|
4449
4786
|
};
|
|
4450
4787
|
function brokenLimitOf(amount, quote) {
|
|
4451
|
-
const
|
|
4788
|
+
const minAmount = "minAmount" in quote ? quote.minAmount : void 0;
|
|
4789
|
+
const maxAmount = "maxAmount" in quote ? quote.maxAmount : void 0;
|
|
4452
4790
|
if (!Number.isFinite(amount)) return null;
|
|
4453
4791
|
if (minAmount != null && amount < minAmount) return { limit: "min", value: minAmount };
|
|
4454
4792
|
if (maxAmount != null && amount > maxAmount) return { limit: "max", value: maxAmount };
|
|
@@ -4472,19 +4810,58 @@ var RAMP_ERROR_MESSAGES = {
|
|
|
4472
4810
|
SDK_RAMPS_ONCHAIN_SUBMIT_FAILED: "The network rejected the transaction, so nothing was sent. The wallet may need XLM for the fee."
|
|
4473
4811
|
};
|
|
4474
4812
|
function rampErrorMessage(e, fallback) {
|
|
4475
|
-
const
|
|
4476
|
-
const code = e?.code;
|
|
4813
|
+
const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
|
|
4477
4814
|
if (typeof code !== "string") return e instanceof Error ? e.message : fallback;
|
|
4478
|
-
const
|
|
4479
|
-
const
|
|
4480
|
-
const
|
|
4815
|
+
const raw = typeof e === "object" && e !== null && "body" in e ? e.body : void 0;
|
|
4816
|
+
const body = typeof raw === "object" && raw !== null ? raw : {};
|
|
4817
|
+
const limit = "limit" in body ? body.limit : void 0;
|
|
4818
|
+
const value = "limitAmount" in body ? body.limitAmount : void 0;
|
|
4819
|
+
const currency = "limitCurrency" in body ? body.limitCurrency : void 0;
|
|
4481
4820
|
if ((limit === "min" || limit === "max") && typeof value === "number" && typeof currency === "string") {
|
|
4482
4821
|
return limitMessage(limit, value, currency);
|
|
4483
4822
|
}
|
|
4484
4823
|
return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
|
|
4485
4824
|
}
|
|
4825
|
+
function readRampStorage(key) {
|
|
4826
|
+
try {
|
|
4827
|
+
return typeof window === "undefined" ? null : window.sessionStorage.getItem(key);
|
|
4828
|
+
} catch {
|
|
4829
|
+
return null;
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
function writeRampStorage(key, value) {
|
|
4833
|
+
try {
|
|
4834
|
+
if (typeof window === "undefined") return;
|
|
4835
|
+
if (value === null) window.sessionStorage.removeItem(key);
|
|
4836
|
+
else window.sessionStorage.setItem(key, value);
|
|
4837
|
+
} catch {
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
function savedRampsOf(key) {
|
|
4841
|
+
try {
|
|
4842
|
+
const value = JSON.parse(readRampStorage(`${key}:saved`) ?? "[]");
|
|
4843
|
+
return Array.isArray(value) ? value.filter((item) => typeof item?.txId === "string" && typeof item?.provider === "string") : [];
|
|
4844
|
+
} catch {
|
|
4845
|
+
return [];
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
function hiddenRampsOf(key) {
|
|
4849
|
+
try {
|
|
4850
|
+
const value = JSON.parse(readRampStorage(`${key}:hidden`) ?? "[]");
|
|
4851
|
+
return Array.isArray(value) ? value.filter((id) => typeof id === "string") : [];
|
|
4852
|
+
} catch {
|
|
4853
|
+
return [];
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
function explorerTxUrl2(chain, hash, network) {
|
|
4857
|
+
if (!hash) return null;
|
|
4858
|
+
const testnet = network === "testnet";
|
|
4859
|
+
if (chain === "SOLANA") return `https://explorer.solana.com/tx/${hash}${testnet ? "?cluster=devnet" : ""}`;
|
|
4860
|
+
if (chain === "POLYGON") return `https://${testnet ? "amoy." : ""}polygonscan.com/tx/${hash}`;
|
|
4861
|
+
return `https://stellar.expert/explorer/${testnet ? "testnet" : "public"}/tx/${hash}`;
|
|
4862
|
+
}
|
|
4486
4863
|
function RampWidget({ onClose }) {
|
|
4487
|
-
const { getClient,
|
|
4864
|
+
const { getClient, wallet, styles, network } = usePollar();
|
|
4488
4865
|
const walletAddress = wallet?.address ?? "";
|
|
4489
4866
|
const client = getClient();
|
|
4490
4867
|
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
@@ -4495,7 +4872,7 @@ function RampWidget({ onClose }) {
|
|
|
4495
4872
|
const [country, setCountry] = useState("");
|
|
4496
4873
|
const [fieldValues, setFieldValues] = useState({});
|
|
4497
4874
|
const setFieldValue = (key, value) => setFieldValues((v) => ({ ...v, [key]: value }));
|
|
4498
|
-
const [
|
|
4875
|
+
const [legacyCountries, setCountries] = useState([]);
|
|
4499
4876
|
const [countriesLoading, setCountriesLoading] = useState(true);
|
|
4500
4877
|
const [refreshing, setRefreshing] = useState(false);
|
|
4501
4878
|
const [quotes, setQuotes] = useState([]);
|
|
@@ -4520,21 +4897,129 @@ function RampWidget({ onClose }) {
|
|
|
4520
4897
|
const [onboardingStatus, setOnboardingStatus] = useState(void 0);
|
|
4521
4898
|
const [txStatus, setTxStatus] = useState(null);
|
|
4522
4899
|
const [stellarTxHash, setStellarTxHash] = useState(null);
|
|
4900
|
+
const [txHash, setTxHash] = useState(null);
|
|
4901
|
+
const [txChain, setTxChain] = useState(null);
|
|
4902
|
+
const [fundsInTransit, setFundsInTransit] = useState(false);
|
|
4903
|
+
const [retrying, setRetrying] = useState(false);
|
|
4523
4904
|
const [depositInstructions, setDepositInstructions] = useState(null);
|
|
4905
|
+
const [workflow, setWorkflow] = useState(null);
|
|
4906
|
+
const workflowRef = useRef(null);
|
|
4907
|
+
const storageKey = `pollar:ramp:${client.apiKey}:${walletAddress}`;
|
|
4908
|
+
const [savedRamps, setSavedRamps] = useState([]);
|
|
4909
|
+
const [legacySignature, setLegacySignature] = useState(void 0);
|
|
4910
|
+
function acceptWorkflow(incoming) {
|
|
4911
|
+
if (hiddenRampsOf(storageKey).includes(incoming.txId)) return false;
|
|
4912
|
+
const next = mergeRampSnapshot(workflowRef.current, incoming);
|
|
4913
|
+
workflowRef.current = next;
|
|
4914
|
+
setWorkflow(next);
|
|
4915
|
+
if (next === incoming) {
|
|
4916
|
+
const pending = savedRampsOf(storageKey).filter((item) => item.txId !== incoming.txId);
|
|
4917
|
+
if (!["completed", "failed", "refunded"].includes(incoming.lifecycleState ?? incoming.status))
|
|
4918
|
+
pending.push({
|
|
4919
|
+
txId: incoming.txId,
|
|
4920
|
+
provider: "provider" in incoming && typeof incoming.provider === "string" ? incoming.provider : provider || "Ramp"
|
|
4921
|
+
});
|
|
4922
|
+
writeRampStorage(`${storageKey}:saved`, JSON.stringify(pending));
|
|
4923
|
+
writeRampStorage(storageKey, incoming.txId);
|
|
4924
|
+
writeRampStorage(`${storageKey}:selection`, null);
|
|
4925
|
+
setSavedRamps(pending);
|
|
4926
|
+
}
|
|
4927
|
+
return next === incoming;
|
|
4928
|
+
}
|
|
4929
|
+
const [routes, setRoutes] = useState([]);
|
|
4930
|
+
const countries = mergeRampCountries(legacyCountries, routes);
|
|
4931
|
+
useEffect(() => {
|
|
4932
|
+
if (!country && countries[0]) {
|
|
4933
|
+
setCountry(countries[0].code);
|
|
4934
|
+
setCurrency(countries[0].currency ?? "");
|
|
4935
|
+
}
|
|
4936
|
+
}, [countries, country]);
|
|
4937
|
+
useEffect(() => {
|
|
4938
|
+
let active = true;
|
|
4939
|
+
void client.getRampRoutes().then((result) => {
|
|
4940
|
+
if (active) setRoutes(result.routes);
|
|
4941
|
+
}).catch(() => {
|
|
4942
|
+
});
|
|
4943
|
+
return () => {
|
|
4944
|
+
active = false;
|
|
4945
|
+
};
|
|
4946
|
+
}, [client]);
|
|
4524
4947
|
const [completing, setCompleting] = useState(false);
|
|
4948
|
+
const completionLocked = useRef(false);
|
|
4525
4949
|
const [errorMsg, setErrorMsg] = useState(null);
|
|
4950
|
+
useEffect(() => {
|
|
4951
|
+
if (typeof window === "undefined" || !walletAddress) return;
|
|
4952
|
+
setSavedRamps(savedRampsOf(storageKey));
|
|
4953
|
+
if (readRampStorage(`${storageKey}:selection`) === "1") return;
|
|
4954
|
+
const hidden = hiddenRampsOf(storageKey);
|
|
4955
|
+
const savedId = [new URLSearchParams(window.location.search).get("rampTxId"), readRampStorage(storageKey)].find(
|
|
4956
|
+
(id) => id && !hidden.includes(id)
|
|
4957
|
+
);
|
|
4958
|
+
if (!savedId) return;
|
|
4959
|
+
let active = true;
|
|
4960
|
+
void client.getRampTransaction(savedId).then(async (result) => {
|
|
4961
|
+
if (!active || result.transactionVersion === void 0) return;
|
|
4962
|
+
setDirection(result.direction);
|
|
4963
|
+
await applyResult(result);
|
|
4964
|
+
}).catch(() => {
|
|
4965
|
+
if (active)
|
|
4966
|
+
setErrorMsg("Could not restore the saved ramp. Retry opening the modal; do not create a replacement order.");
|
|
4967
|
+
});
|
|
4968
|
+
return () => {
|
|
4969
|
+
active = false;
|
|
4970
|
+
};
|
|
4971
|
+
}, [client, storageKey, walletAddress]);
|
|
4972
|
+
async function resumeSavedRamp(id) {
|
|
4973
|
+
if (completionLocked.current) return;
|
|
4974
|
+
setIsLoading(true);
|
|
4975
|
+
setErrorMsg(null);
|
|
4976
|
+
try {
|
|
4977
|
+
const result = await client.getRampTransaction(id);
|
|
4978
|
+
if (result.transactionVersion === void 0) throw new Error("This saved ramp cannot be restored.");
|
|
4979
|
+
setDirection(result.direction);
|
|
4980
|
+
await applyResult(result);
|
|
4981
|
+
} catch (error) {
|
|
4982
|
+
setErrorMsg(rampErrorMessage(error, "Could not resume the saved ramp."));
|
|
4983
|
+
} finally {
|
|
4984
|
+
setIsLoading(false);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4526
4987
|
const directionRef = useRef(direction);
|
|
4527
4988
|
directionRef.current = direction;
|
|
4989
|
+
function applySettlement(tx) {
|
|
4990
|
+
if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
|
|
4991
|
+
if (tx.txHash) setTxHash(tx.txHash);
|
|
4992
|
+
if (tx.chain) setTxChain(tx.chain);
|
|
4993
|
+
setFundsInTransit(tx.fundsInTransit === true);
|
|
4994
|
+
}
|
|
4995
|
+
async function handleRetryTransit() {
|
|
4996
|
+
if (!txId || retrying) return;
|
|
4997
|
+
setRetrying(true);
|
|
4998
|
+
setErrorMsg(null);
|
|
4999
|
+
try {
|
|
5000
|
+
const tx = await client.retryRampTransaction(txId);
|
|
5001
|
+
setTxStatus(tx.status);
|
|
5002
|
+
applySettlement(tx);
|
|
5003
|
+
} catch (e) {
|
|
5004
|
+
setErrorMsg(e instanceof Error && e.message ? e.message : "Could not retry the transfer. Try again in a moment.");
|
|
5005
|
+
} finally {
|
|
5006
|
+
setRetrying(false);
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
const workflowTerminal = !!workflow && ["completed", "failed", "refunded"].includes(workflow.lifecycleState ?? workflow.status);
|
|
4528
5010
|
useEffect(() => {
|
|
4529
5011
|
if (step !== "status" || !txId) return;
|
|
4530
|
-
if (txStatus && TERMINAL.includes(txStatus)) return;
|
|
5012
|
+
if (workflowTerminal || txStatus && TERMINAL.includes(txStatus)) return;
|
|
4531
5013
|
let active = true;
|
|
4532
5014
|
const id = setInterval(async () => {
|
|
4533
5015
|
try {
|
|
4534
5016
|
const tx = await client.getRampTransaction(txId);
|
|
4535
5017
|
if (!active) return;
|
|
5018
|
+
if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
|
|
4536
5019
|
setTxStatus(tx.status);
|
|
4537
|
-
|
|
5020
|
+
applySettlement(tx);
|
|
5021
|
+
const pending = "pendingSignature" in tx ? tx.pendingSignature : void 0;
|
|
5022
|
+
if (pending) setLegacySignature(pending);
|
|
4538
5023
|
if (tx.kycUrl) setKycUrl(tx.kycUrl);
|
|
4539
5024
|
if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
|
|
4540
5025
|
if (TERMINAL.includes(tx.status)) clearInterval(id);
|
|
@@ -4545,9 +5030,9 @@ function RampWidget({ onClose }) {
|
|
|
4545
5030
|
active = false;
|
|
4546
5031
|
clearInterval(id);
|
|
4547
5032
|
};
|
|
4548
|
-
}, [step, txId, txStatus, client]);
|
|
5033
|
+
}, [step, txId, txStatus, workflowTerminal, client]);
|
|
4549
5034
|
useEffect(() => {
|
|
4550
|
-
if (step !== "status" || !kycPending || kycApproved) return;
|
|
5035
|
+
if (workflow || step !== "status" || !kycPending || kycApproved || provider.toLowerCase() !== "abroad") return;
|
|
4551
5036
|
if (onboardingStatus === "awaiting_provider") return;
|
|
4552
5037
|
let active = true;
|
|
4553
5038
|
const check = async () => {
|
|
@@ -4563,14 +5048,15 @@ function RampWidget({ onClose }) {
|
|
|
4563
5048
|
active = false;
|
|
4564
5049
|
clearInterval(id);
|
|
4565
5050
|
};
|
|
4566
|
-
}, [step, kycPending, kycApproved, onboardingStatus, client]);
|
|
5051
|
+
}, [step, kycPending, kycApproved, onboardingStatus, workflow, client, provider]);
|
|
4567
5052
|
async function loadCountries(resetSelection) {
|
|
4568
5053
|
setCountriesLoading(true);
|
|
4569
5054
|
try {
|
|
4570
5055
|
const { countries: list } = await client.getRampCountries();
|
|
4571
5056
|
setCountries(list);
|
|
4572
|
-
const
|
|
4573
|
-
const
|
|
5057
|
+
const available = mergeRampCountries(list, routes);
|
|
5058
|
+
const first = available[0];
|
|
5059
|
+
const stillValid = available.some((c) => c.code === country);
|
|
4574
5060
|
if (first && (resetSelection || !stillValid)) {
|
|
4575
5061
|
setCountry(first.code);
|
|
4576
5062
|
if (first.currency) setCurrency(first.currency);
|
|
@@ -4595,13 +5081,24 @@ function RampWidget({ onClose }) {
|
|
|
4595
5081
|
try {
|
|
4596
5082
|
await loadCountries(false);
|
|
4597
5083
|
if (step === "select_route") {
|
|
4598
|
-
const result = await
|
|
5084
|
+
const result = await quoteRampOptions(
|
|
5085
|
+
client,
|
|
5086
|
+
{
|
|
5087
|
+
country,
|
|
5088
|
+
amount: Number(amount),
|
|
5089
|
+
amountExact: amount,
|
|
5090
|
+
currency,
|
|
5091
|
+
direction
|
|
5092
|
+
},
|
|
5093
|
+
routes
|
|
5094
|
+
);
|
|
4599
5095
|
setQuotes(result.quotes ?? []);
|
|
4600
5096
|
setRequirementsRequired(result.requirementsRequired ?? []);
|
|
4601
5097
|
} else if (step === "status" && txId) {
|
|
4602
5098
|
const tx = await client.getRampTransaction(txId);
|
|
5099
|
+
if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
|
|
4603
5100
|
setTxStatus(tx.status);
|
|
4604
|
-
|
|
5101
|
+
applySettlement(tx);
|
|
4605
5102
|
if (tx.kycUrl) setKycUrl(tx.kycUrl);
|
|
4606
5103
|
if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
|
|
4607
5104
|
}
|
|
@@ -4611,25 +5108,50 @@ function RampWidget({ onClose }) {
|
|
|
4611
5108
|
}
|
|
4612
5109
|
}
|
|
4613
5110
|
function resetToInput({ keepMessage = false } = {}) {
|
|
5111
|
+
writeRampStorage(storageKey, null);
|
|
5112
|
+
writeRampStorage(`${storageKey}:selection`, "1");
|
|
4614
5113
|
setStep("input");
|
|
4615
5114
|
setQuotes([]);
|
|
4616
5115
|
setRequirementsRequired([]);
|
|
4617
5116
|
setSelectedQuote(null);
|
|
4618
5117
|
setFieldValues({});
|
|
5118
|
+
setWorkflow(null);
|
|
5119
|
+
workflowRef.current = null;
|
|
5120
|
+
setLegacySignature(void 0);
|
|
4619
5121
|
setTxId(null);
|
|
4620
5122
|
setProvider("");
|
|
4621
5123
|
setKycUrl(null);
|
|
4622
5124
|
setTosUrl(null);
|
|
4623
5125
|
setTxStatus(null);
|
|
4624
5126
|
setStellarTxHash(null);
|
|
5127
|
+
setTxHash(null);
|
|
5128
|
+
setTxChain(null);
|
|
5129
|
+
setFundsInTransit(false);
|
|
4625
5130
|
setDepositInstructions(null);
|
|
4626
5131
|
setKycPending(false);
|
|
4627
5132
|
setKycApproved(false);
|
|
4628
5133
|
setNoticeMsg(null);
|
|
4629
5134
|
if (!keepMessage) setErrorMsg(null);
|
|
4630
5135
|
}
|
|
5136
|
+
function dismissSavedRamp(id) {
|
|
5137
|
+
if (completionLocked.current) return;
|
|
5138
|
+
const pending = savedRampsOf(storageKey).filter((ramp) => ramp.txId !== id);
|
|
5139
|
+
writeRampStorage(`${storageKey}:saved`, JSON.stringify(pending));
|
|
5140
|
+
writeRampStorage(`${storageKey}:hidden`, JSON.stringify([.../* @__PURE__ */ new Set([...hiddenRampsOf(storageKey), id])]));
|
|
5141
|
+
if (readRampStorage(storageKey) === id) writeRampStorage(storageKey, null);
|
|
5142
|
+
setSavedRamps(pending);
|
|
5143
|
+
if (txId === id) resetToInput();
|
|
5144
|
+
setNoticeMsg(
|
|
5145
|
+
"Removed from this tab\u2019s pending list. The operation may still be active at the provider; no payment was cancelled or refunded."
|
|
5146
|
+
);
|
|
5147
|
+
}
|
|
4631
5148
|
async function resumeWithSignature(id, ps) {
|
|
4632
|
-
const outcome = await signTx(ps.unsignedXdr);
|
|
5149
|
+
const outcome = await client.signTx(ps.unsignedXdr, { skipSponsorship: ps.action === "sep10" });
|
|
5150
|
+
if (outcome.status === "cosign_pending") {
|
|
5151
|
+
setErrorMsg("Approve the signature on your device, then resume.");
|
|
5152
|
+
setStep("error");
|
|
5153
|
+
return;
|
|
5154
|
+
}
|
|
4633
5155
|
if (outcome.status !== "signed") {
|
|
4634
5156
|
setErrorMsg(outcome.message ?? outcome.details ?? "Signing was cancelled.");
|
|
4635
5157
|
setStep("error");
|
|
@@ -4642,12 +5164,10 @@ function RampWidget({ onClose }) {
|
|
|
4642
5164
|
await applyResult(result);
|
|
4643
5165
|
}
|
|
4644
5166
|
async function applyResult(result) {
|
|
5167
|
+
if ("transactionVersion" in result && result.transactionVersion !== void 0 && !acceptWorkflow(result)) return;
|
|
4645
5168
|
setTxId(result.txId);
|
|
4646
5169
|
setProvider(result.provider);
|
|
4647
|
-
|
|
4648
|
-
await resumeWithSignature(result.txId, result.pendingSignature);
|
|
4649
|
-
return;
|
|
4650
|
-
}
|
|
5170
|
+
setLegacySignature(result.pendingSignature);
|
|
4651
5171
|
setKycUrl(result.kycUrl ?? null);
|
|
4652
5172
|
setTosUrl(result.tosUrl ?? null);
|
|
4653
5173
|
setOnboardingStatus(result.onboardingStatus);
|
|
@@ -4659,14 +5179,21 @@ function RampWidget({ onClose }) {
|
|
|
4659
5179
|
setStep("status");
|
|
4660
5180
|
}
|
|
4661
5181
|
async function loadQuotes() {
|
|
4662
|
-
const result = await
|
|
5182
|
+
const result = await quoteRampOptions(
|
|
5183
|
+
client,
|
|
5184
|
+
{
|
|
5185
|
+
country,
|
|
5186
|
+
amount: Number(amount),
|
|
5187
|
+
amountExact: amount,
|
|
5188
|
+
currency,
|
|
5189
|
+
direction
|
|
5190
|
+
},
|
|
5191
|
+
routes
|
|
5192
|
+
);
|
|
4663
5193
|
const list = result.quotes ?? [];
|
|
4664
5194
|
const locked = result.requirementsRequired ?? [];
|
|
4665
5195
|
if (list.length === 0 && locked.length === 0) {
|
|
4666
|
-
|
|
4667
|
-
setErrorMsg(
|
|
4668
|
-
down.length > 0 ? `${down.join(", ")} ${down.length === 1 ? "is" : "are"} temporarily unavailable. Please try again later.` : `No ramp providers available for ${country} yet.`
|
|
4669
|
-
);
|
|
5196
|
+
setErrorMsg(unavailableRampMessage(country, result.unavailable ?? []));
|
|
4670
5197
|
setStep("error");
|
|
4671
5198
|
return false;
|
|
4672
5199
|
}
|
|
@@ -4712,19 +5239,40 @@ function RampWidget({ onClose }) {
|
|
|
4712
5239
|
setIsLoading(true);
|
|
4713
5240
|
setErrorMsg(null);
|
|
4714
5241
|
try {
|
|
4715
|
-
const
|
|
4716
|
-
|
|
4717
|
-
|
|
5242
|
+
const body = {
|
|
5243
|
+
quoteId: quote.quoteId,
|
|
5244
|
+
amount: quote.fiatAmount ?? Number(amount),
|
|
5245
|
+
...quote.terms ? { amountExact: quote.terms.fiatAmount } : {},
|
|
5246
|
+
currency,
|
|
5247
|
+
country,
|
|
5248
|
+
...walletAddress ? { walletAddress } : {}
|
|
5249
|
+
};
|
|
4718
5250
|
const extraFields = {};
|
|
4719
5251
|
for (const f of requiredFieldsOf(quote)) {
|
|
4720
5252
|
const val = (fieldValues[f.key] ?? "").trim();
|
|
4721
5253
|
if (!val) continue;
|
|
4722
|
-
if (f.bankType)
|
|
4723
|
-
|
|
5254
|
+
if (f.bankType) {
|
|
5255
|
+
if (isBankType(f.bankType)) body.bankDetails = { type: f.bankType, value: val };
|
|
5256
|
+
} else if (f.key === "email" || f.key === "fullName" || f.key === "taxId" || f.key === "qrCode") body[f.key] = val;
|
|
4724
5257
|
else extraFields[f.key] = val;
|
|
4725
5258
|
}
|
|
4726
|
-
if (
|
|
4727
|
-
|
|
5259
|
+
if (quote.route)
|
|
5260
|
+
body.fields = Object.fromEntries(
|
|
5261
|
+
requiredFieldsOf(quote).map((field) => [field.key, (fieldValues[field.key] ?? "").trim()]).filter(([, value]) => value)
|
|
5262
|
+
);
|
|
5263
|
+
else if (Object.keys(extraFields).length > 0) body.fields = extraFields;
|
|
5264
|
+
const onrampBody = {
|
|
5265
|
+
quoteId: body.quoteId,
|
|
5266
|
+
amount: body.amount,
|
|
5267
|
+
currency: body.currency,
|
|
5268
|
+
country: body.country,
|
|
5269
|
+
...body.amountExact !== void 0 ? { amountExact: body.amountExact } : {},
|
|
5270
|
+
...body.walletAddress !== void 0 ? { walletAddress: body.walletAddress } : {},
|
|
5271
|
+
...body.email !== void 0 ? { email: body.email } : {},
|
|
5272
|
+
...body.fullName !== void 0 ? { fullName: body.fullName } : {},
|
|
5273
|
+
...body.fields !== void 0 ? { fields: body.fields } : {}
|
|
5274
|
+
};
|
|
5275
|
+
const result = direction === "onramp" ? await client.createOnRamp(onrampBody) : await client.createOffRamp(body);
|
|
4728
5276
|
await applyResult(result);
|
|
4729
5277
|
} catch (e) {
|
|
4730
5278
|
const requirement = requiredRampKyc(e);
|
|
@@ -4774,8 +5322,24 @@ function RampWidget({ onClose }) {
|
|
|
4774
5322
|
function handleOpenTos() {
|
|
4775
5323
|
if (tosUrl) window.open(tosUrl, "_blank", "noopener,noreferrer");
|
|
4776
5324
|
}
|
|
5325
|
+
async function handleLegacySignature() {
|
|
5326
|
+
if (!txId || !legacySignature || completionLocked.current) return;
|
|
5327
|
+
completionLocked.current = true;
|
|
5328
|
+
setCompleting(true);
|
|
5329
|
+
setErrorMsg(null);
|
|
5330
|
+
try {
|
|
5331
|
+
await resumeWithSignature(txId, legacySignature);
|
|
5332
|
+
} catch (e) {
|
|
5333
|
+
setErrorMsg(rampErrorMessage(e, "Failed to submit the signature."));
|
|
5334
|
+
setStep("error");
|
|
5335
|
+
} finally {
|
|
5336
|
+
completionLocked.current = false;
|
|
5337
|
+
setCompleting(false);
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
4777
5340
|
async function handleCompleteWithdraw() {
|
|
4778
|
-
if (!txId) return;
|
|
5341
|
+
if (!txId || completionLocked.current) return;
|
|
5342
|
+
completionLocked.current = true;
|
|
4779
5343
|
setCompleting(true);
|
|
4780
5344
|
setErrorMsg(null);
|
|
4781
5345
|
try {
|
|
@@ -4792,11 +5356,12 @@ function RampWidget({ onClose }) {
|
|
|
4792
5356
|
msg.includes("KYC") ? "Finish KYC at the provider first, then try again." : msg || "Failed to complete the withdrawal."
|
|
4793
5357
|
);
|
|
4794
5358
|
} finally {
|
|
5359
|
+
completionLocked.current = false;
|
|
4795
5360
|
setCompleting(false);
|
|
4796
5361
|
}
|
|
4797
5362
|
}
|
|
4798
5363
|
const kycBlocking = kycPending && !kycApproved;
|
|
4799
|
-
const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycPending;
|
|
5364
|
+
const canComplete = !workflow && !legacySignature && direction === "offramp" && step === "status" && txStatus !== "completed" && txStatus !== "failed" && !stellarTxHash && !txHash && !kycPending;
|
|
4800
5365
|
const flowSteps = flowStepsOf(quotes, selectedQuote);
|
|
4801
5366
|
const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
|
|
4802
5367
|
if (pendingKyc?.type === "FORM") {
|
|
@@ -4854,6 +5419,20 @@ function RampWidget({ onClose }) {
|
|
|
4854
5419
|
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
|
|
4855
5420
|
RampWidgetTemplate,
|
|
4856
5421
|
{
|
|
5422
|
+
workflowContent: workflow ? /* @__PURE__ */ jsx(
|
|
5423
|
+
RampWorkflow,
|
|
5424
|
+
{
|
|
5425
|
+
client,
|
|
5426
|
+
snapshot: workflow,
|
|
5427
|
+
onBusyChange: (busy) => {
|
|
5428
|
+
completionLocked.current = busy;
|
|
5429
|
+
setCompleting(busy);
|
|
5430
|
+
},
|
|
5431
|
+
onChange: (next) => {
|
|
5432
|
+
if (acceptWorkflow(next)) setTxStatus(next.status);
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
) : legacySignature && step === "status" && txStatus !== "completed" && txStatus !== "failed" ? /* @__PURE__ */ jsx("button", { type: "button", disabled: completing, onClick: () => void handleLegacySignature(), children: "Authorize wallet request" }) : void 0,
|
|
4857
5436
|
theme,
|
|
4858
5437
|
accentColor,
|
|
4859
5438
|
styleOverrides,
|
|
@@ -4881,13 +5460,27 @@ function RampWidget({ onClose }) {
|
|
|
4881
5460
|
onboardingStatus: onboardingStatus ?? null,
|
|
4882
5461
|
kycJustApproved: kycPending && kycApproved,
|
|
4883
5462
|
stellarTxHash,
|
|
4884
|
-
|
|
5463
|
+
txHash,
|
|
5464
|
+
txChain,
|
|
5465
|
+
explorerUrl: explorerTxUrl2(txChain ?? "STELLAR", txHash ?? stellarTxHash, network),
|
|
5466
|
+
fundsInTransit,
|
|
5467
|
+
retrying,
|
|
5468
|
+
onRetryTransit: handleRetryTransit,
|
|
4885
5469
|
depositInstructions,
|
|
4886
5470
|
canComplete,
|
|
4887
5471
|
completing,
|
|
5472
|
+
savedRamps,
|
|
5473
|
+
onResumeRamp: resumeSavedRamp,
|
|
5474
|
+
onDismissRamp: dismissSavedRamp,
|
|
5475
|
+
onDismissCurrentRamp: workflow ? () => dismissSavedRamp(workflow.txId) : void 0,
|
|
5476
|
+
onExploreRoutes: workflow ? () => {
|
|
5477
|
+
if (!completionLocked.current) resetToInput();
|
|
5478
|
+
} : void 0,
|
|
4888
5479
|
errorMsg,
|
|
4889
5480
|
noticeMsg,
|
|
4890
|
-
onDirectionChange:
|
|
5481
|
+
onDirectionChange: (next) => {
|
|
5482
|
+
setDirection(next);
|
|
5483
|
+
},
|
|
4891
5484
|
onAmountChange: (next) => {
|
|
4892
5485
|
setAmount(next);
|
|
4893
5486
|
setErrorMsg(null);
|
|
@@ -4920,49 +5513,6 @@ function walletNotReadyReason(wallet, chain) {
|
|
|
4920
5513
|
}
|
|
4921
5514
|
return null;
|
|
4922
5515
|
}
|
|
4923
|
-
|
|
4924
|
-
// src/lib/qr-code/index.tsx
|
|
4925
|
-
var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
|
|
4926
|
-
var import_QRCode = __toESM(require_QRCode());
|
|
4927
|
-
var QRCodeSvg = forwardRef(function QRCodeSvg2({ bgColor, bgD, fgD, fgColor, size, title, viewBoxSize, xmlns = "http://www.w3.org/2000/svg", ...props }, ref) {
|
|
4928
|
-
return /* @__PURE__ */ jsxs("svg", { ...props, height: size, ref, viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, width: size, xmlns, children: [
|
|
4929
|
-
title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
|
|
4930
|
-
/* @__PURE__ */ jsx("path", { d: bgD, fill: bgColor }),
|
|
4931
|
-
/* @__PURE__ */ jsx("path", { d: fgD, fill: fgColor })
|
|
4932
|
-
] });
|
|
4933
|
-
});
|
|
4934
|
-
QRCodeSvg.displayName = "QRCodeSvg";
|
|
4935
|
-
function bytesToBinaryString(bytes) {
|
|
4936
|
-
return bytes.map((b) => String.fromCharCode(b & 255)).join("");
|
|
4937
|
-
}
|
|
4938
|
-
function encodeStringToUtf8Bytes(input) {
|
|
4939
|
-
return Array.from(new TextEncoder().encode(input));
|
|
4940
|
-
}
|
|
4941
|
-
var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#000000", level = "L", size = 256, value, ...props }, ref) {
|
|
4942
|
-
const qrcode = new import_QRCode.default(-1, import_ErrorCorrectLevel.default[level]);
|
|
4943
|
-
const utf8Bytes = encodeStringToUtf8Bytes(value);
|
|
4944
|
-
const binaryString = bytesToBinaryString(utf8Bytes);
|
|
4945
|
-
qrcode.addData(binaryString, "Byte");
|
|
4946
|
-
qrcode.make();
|
|
4947
|
-
const cells = qrcode.modules;
|
|
4948
|
-
return /* @__PURE__ */ jsx(
|
|
4949
|
-
QRCodeSvg,
|
|
4950
|
-
{
|
|
4951
|
-
...props,
|
|
4952
|
-
bgColor,
|
|
4953
|
-
bgD: cells.map(
|
|
4954
|
-
(row, rowIndex) => row.map((cell, cellIndex) => !cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
4955
|
-
).join(" "),
|
|
4956
|
-
fgColor,
|
|
4957
|
-
fgD: cells.map(
|
|
4958
|
-
(row, rowIndex) => row.map((cell, cellIndex) => cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
4959
|
-
).join(" "),
|
|
4960
|
-
ref,
|
|
4961
|
-
size,
|
|
4962
|
-
viewBoxSize: cells.length
|
|
4963
|
-
}
|
|
4964
|
-
);
|
|
4965
|
-
});
|
|
4966
5516
|
function PollarLogo(props) {
|
|
4967
5517
|
return /* @__PURE__ */ jsxs("svg", { width: "45", height: "48", viewBox: "0 0 45 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
|
|
4968
5518
|
/* @__PURE__ */ jsx(
|
|
@@ -4988,7 +5538,7 @@ function PollarLogo(props) {
|
|
|
4988
5538
|
)
|
|
4989
5539
|
] });
|
|
4990
5540
|
}
|
|
4991
|
-
var
|
|
5541
|
+
var CHAIN_NAME2 = {
|
|
4992
5542
|
STELLAR: "Stellar",
|
|
4993
5543
|
POLYGON: "Polygon",
|
|
4994
5544
|
SOLANA: "Solana"
|
|
@@ -5003,11 +5553,13 @@ function ReceiveModalTemplate({
|
|
|
5003
5553
|
onSelectChain,
|
|
5004
5554
|
copied,
|
|
5005
5555
|
notReadyReason,
|
|
5556
|
+
recoveryLocked,
|
|
5557
|
+
onSetupRecovery,
|
|
5006
5558
|
onCopy,
|
|
5007
5559
|
onClose
|
|
5008
5560
|
}) {
|
|
5009
5561
|
const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
|
|
5010
|
-
const chainName = selectedChain ?
|
|
5562
|
+
const chainName = selectedChain ? CHAIN_NAME2[selectedChain] ?? selectedChain : "wallet";
|
|
5011
5563
|
return /* @__PURE__ */ jsxs(
|
|
5012
5564
|
"div",
|
|
5013
5565
|
{
|
|
@@ -5021,7 +5573,10 @@ function ReceiveModalTemplate({
|
|
|
5021
5573
|
/* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) })
|
|
5022
5574
|
] }),
|
|
5023
5575
|
chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
|
|
5024
|
-
|
|
5576
|
+
recoveryLocked ? /* @__PURE__ */ jsxs("div", { className: "pollar-receive-locked", role: "alert", children: [
|
|
5577
|
+
/* @__PURE__ */ jsx("p", { children: "Set up a way to recover this wallet before receiving funds. Without one, losing this device means losing whatever you receive, and nobody can get it back." }),
|
|
5578
|
+
onSetupRecovery && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-receive-copy-btn", onClick: onSetupRecovery, children: "Set up recovery" })
|
|
5579
|
+
] }) : walletAddress ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5025
5580
|
/* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr", children: [
|
|
5026
5581
|
/* @__PURE__ */ jsxs("div", { className: "pollar-receive-network-badge", children: [
|
|
5027
5582
|
/* @__PURE__ */ jsx("span", { className: "pollar-receive-network-dot", "aria-hidden": true }),
|
|
@@ -5089,13 +5644,13 @@ function ReceiveModalTemplate({
|
|
|
5089
5644
|
] })
|
|
5090
5645
|
] })
|
|
5091
5646
|
] }) : /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No wallet connected." }),
|
|
5092
|
-
/* @__PURE__ */ jsx(PollarModalFooter, {
|
|
5647
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
5093
5648
|
]
|
|
5094
5649
|
}
|
|
5095
5650
|
);
|
|
5096
5651
|
}
|
|
5097
5652
|
function ReceiveModal({ onClose }) {
|
|
5098
|
-
const { wallet, wallets, styles } = usePollar();
|
|
5653
|
+
const { wallet, wallets, styles, recoveryLocked, openRecoveryModal } = usePollar();
|
|
5099
5654
|
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
5100
5655
|
const [copied, setCopied] = useState(false);
|
|
5101
5656
|
const copyTimerRef = useRef(null);
|
|
@@ -5137,6 +5692,11 @@ function ReceiveModal({ onClose }) {
|
|
|
5137
5692
|
setSelectedChain(chain);
|
|
5138
5693
|
},
|
|
5139
5694
|
notReadyReason,
|
|
5695
|
+
recoveryLocked,
|
|
5696
|
+
onSetupRecovery: () => {
|
|
5697
|
+
onClose();
|
|
5698
|
+
openRecoveryModal();
|
|
5699
|
+
},
|
|
5140
5700
|
copied,
|
|
5141
5701
|
onCopy: handleCopy,
|
|
5142
5702
|
onClose
|
|
@@ -5152,6 +5712,7 @@ var STATUS_MESSAGES = {
|
|
|
5152
5712
|
submitting: "Submitting transaction\u2026",
|
|
5153
5713
|
submitted: "Submitted \u2014 waiting for confirmation\u2026",
|
|
5154
5714
|
"signing-submitting": "Signing and submitting transaction\u2026",
|
|
5715
|
+
awaiting_cosign: "Waiting for your approval on this device\u2026",
|
|
5155
5716
|
"building-signing-submitting": "Processing transaction\u2026",
|
|
5156
5717
|
success: "Transaction sent successfully",
|
|
5157
5718
|
error: "Transaction failed"
|
|
@@ -5187,14 +5748,22 @@ function TxStatusView({
|
|
|
5187
5748
|
}) {
|
|
5188
5749
|
const buildData = "buildData" in transaction ? transaction.buildData : null;
|
|
5189
5750
|
const hash = transaction.step === "success" ? transaction.hash : null;
|
|
5190
|
-
const errorDetails = transaction.step === "error" ? transaction.details
|
|
5751
|
+
const errorDetails = transaction.step === "error" ? transactionErrorMessage(transaction.code, transaction.details) : null;
|
|
5191
5752
|
const isBuilt = transaction.step === "built";
|
|
5192
5753
|
const isInFlight = IN_FLIGHT_STEPS.has(transaction.step);
|
|
5193
5754
|
const isSuccess = transaction.step === "success";
|
|
5194
5755
|
const isError = transaction.step === "error";
|
|
5195
5756
|
const showDetails = buildData !== null && SHOW_DETAILS_STEPS.has(transaction.step);
|
|
5196
|
-
const walletImg = walletType === WalletType.FREIGHTER ? LOGO_FREIGHTER : walletType === WalletType.ALBEDO ? LOGO_ALBEDO :
|
|
5757
|
+
const walletImg = walletType === WalletType.FREIGHTER ? LOGO_FREIGHTER : walletType === WalletType.ALBEDO ? LOGO_ALBEDO : null;
|
|
5197
5758
|
const walletAlt = walletType === WalletType.FREIGHTER ? "Freighter" : walletType === WalletType.ALBEDO ? "Albedo" : "Pollar";
|
|
5759
|
+
const statusBanner = /* @__PURE__ */ jsx(
|
|
5760
|
+
ModalStatusBanner,
|
|
5761
|
+
{
|
|
5762
|
+
message: STATUS_MESSAGES[transaction.step],
|
|
5763
|
+
status: isError ? "ERROR" : isInFlight ? "LOADING" : isSuccess ? "SUCCESS" : "NONE"
|
|
5764
|
+
}
|
|
5765
|
+
);
|
|
5766
|
+
const showSpinner = isInFlight || isSuccess || isError;
|
|
5198
5767
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5199
5768
|
showDetails && buildData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5200
5769
|
/* @__PURE__ */ jsxs("div", { className: "pollar-tx-summary", children: [
|
|
@@ -5235,7 +5804,7 @@ function TxStatusView({
|
|
|
5235
5804
|
/* @__PURE__ */ jsx("pre", { className: "pollar-tx-error-details-content", children: errorDetails })
|
|
5236
5805
|
] }),
|
|
5237
5806
|
isBuilt && /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
|
|
5238
|
-
|
|
5807
|
+
showSpinner && /* @__PURE__ */ jsxs("div", { className: "pollar-tx-wallet-spinner", children: [
|
|
5239
5808
|
/* @__PURE__ */ jsxs("div", { className: "pollar-tx-spinner-ring", children: [
|
|
5240
5809
|
/* @__PURE__ */ jsxs(
|
|
5241
5810
|
"svg",
|
|
@@ -5265,8 +5834,12 @@ function TxStatusView({
|
|
|
5265
5834
|
]
|
|
5266
5835
|
}
|
|
5267
5836
|
),
|
|
5268
|
-
/* @__PURE__ */ jsx("div", { className: "pollar-tx-wallet-icon", children: /* @__PURE__ */ jsx("img", { src: walletImg, alt: walletAlt, className: "pollar-tx-wallet-img" })
|
|
5837
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-tx-wallet-icon", children: walletImg ? /* @__PURE__ */ jsx("img", { src: walletImg, alt: walletAlt, className: "pollar-tx-wallet-img" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5838
|
+
/* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_LIGHT, alt: walletAlt, className: "pollar-tx-wallet-img pollar-logo-on-light" }),
|
|
5839
|
+
/* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_DARK, alt: walletAlt, className: "pollar-tx-wallet-img pollar-logo-on-dark" })
|
|
5840
|
+
] }) })
|
|
5269
5841
|
] }),
|
|
5842
|
+
statusBanner,
|
|
5270
5843
|
isInFlight && /* @__PURE__ */ jsx("p", { className: "pollar-tx-spinner-label", children: walletType === WalletType.FREIGHTER ? "Waiting for Freighter\u2026" : walletType === WalletType.ALBEDO ? "Waiting for Albedo\u2026" : "Signing and sending\u2026" }),
|
|
5271
5844
|
isError && onRetry && "buildData" in transaction && transaction.buildData && /* @__PURE__ */ jsx("button", { className: "pollar-btn-secondary pollar-tx-retry-btn", onClick: onRetry, children: "Try again" })
|
|
5272
5845
|
] }),
|
|
@@ -5331,13 +5904,7 @@ function TxStatusView({
|
|
|
5331
5904
|
] })
|
|
5332
5905
|
] }),
|
|
5333
5906
|
isSuccess && /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onDone, children: "Done" }),
|
|
5334
|
-
|
|
5335
|
-
ModalStatusBanner,
|
|
5336
|
-
{
|
|
5337
|
-
message: STATUS_MESSAGES[transaction.step],
|
|
5338
|
-
status: isError ? "ERROR" : isInFlight ? "LOADING" : isSuccess ? "SUCCESS" : "NONE"
|
|
5339
|
-
}
|
|
5340
|
-
)
|
|
5907
|
+
!showSpinner && statusBanner
|
|
5341
5908
|
] });
|
|
5342
5909
|
}
|
|
5343
5910
|
function formatBalance(balance) {
|
|
@@ -5354,6 +5921,11 @@ function chainLabel(chain) {
|
|
|
5354
5921
|
if (chain === "POLYGON") return "Polygon";
|
|
5355
5922
|
return "network";
|
|
5356
5923
|
}
|
|
5924
|
+
function destinationPlaceholder(chain) {
|
|
5925
|
+
if (chain === "SOLANA") return "Solana address";
|
|
5926
|
+
if (chain === "POLYGON") return "0x\u2026";
|
|
5927
|
+
return "G\u2026";
|
|
5928
|
+
}
|
|
5357
5929
|
function AssetIcon({ code }) {
|
|
5358
5930
|
return /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
|
|
5359
5931
|
}
|
|
@@ -5471,6 +6043,10 @@ function SendModalTemplate({
|
|
|
5471
6043
|
onMax,
|
|
5472
6044
|
onPaste,
|
|
5473
6045
|
onDestinationChange,
|
|
6046
|
+
memo = "",
|
|
6047
|
+
memoType = "text",
|
|
6048
|
+
onMemoChange,
|
|
6049
|
+
onMemoTypeChange,
|
|
5474
6050
|
onSubmit,
|
|
5475
6051
|
onSignAndSend,
|
|
5476
6052
|
onToggleXdr,
|
|
@@ -5578,7 +6154,7 @@ function SendModalTemplate({
|
|
|
5578
6154
|
{
|
|
5579
6155
|
className: "pollar-input pollar-send-destination-input",
|
|
5580
6156
|
type: "text",
|
|
5581
|
-
placeholder:
|
|
6157
|
+
placeholder: destinationPlaceholder(selectedChain),
|
|
5582
6158
|
value: destination,
|
|
5583
6159
|
onChange: (e) => onDestinationChange(e.target.value),
|
|
5584
6160
|
"aria-label": "Destination wallet"
|
|
@@ -5587,6 +6163,39 @@ function SendModalTemplate({
|
|
|
5587
6163
|
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-send-inline-action", onClick: onPaste, children: "Paste" })
|
|
5588
6164
|
] })
|
|
5589
6165
|
] }),
|
|
6166
|
+
selectedChain === "STELLAR" && onMemoChange && /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
|
|
6167
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-send-label", htmlFor: "pollar-send-memo", children: [
|
|
6168
|
+
"Memo ",
|
|
6169
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-send-label-optional", children: "(optional)" })
|
|
6170
|
+
] }),
|
|
6171
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-send-memo-row", children: [
|
|
6172
|
+
/* @__PURE__ */ jsxs(
|
|
6173
|
+
"select",
|
|
6174
|
+
{
|
|
6175
|
+
className: "pollar-input pollar-send-memo-type",
|
|
6176
|
+
value: memoType,
|
|
6177
|
+
onChange: (e) => onMemoTypeChange?.(e.target.value),
|
|
6178
|
+
"aria-label": "Memo type",
|
|
6179
|
+
children: [
|
|
6180
|
+
/* @__PURE__ */ jsx("option", { value: "text", children: "Text" }),
|
|
6181
|
+
/* @__PURE__ */ jsx("option", { value: "id", children: "ID" })
|
|
6182
|
+
]
|
|
6183
|
+
}
|
|
6184
|
+
),
|
|
6185
|
+
/* @__PURE__ */ jsx(
|
|
6186
|
+
"input",
|
|
6187
|
+
{
|
|
6188
|
+
id: "pollar-send-memo",
|
|
6189
|
+
className: "pollar-input pollar-send-memo-input",
|
|
6190
|
+
type: "text",
|
|
6191
|
+
inputMode: memoType === "id" ? "numeric" : "text",
|
|
6192
|
+
placeholder: memoType === "id" ? "123456789" : "Up to 28 characters",
|
|
6193
|
+
value: memo,
|
|
6194
|
+
onChange: (e) => onMemoChange(e.target.value)
|
|
6195
|
+
}
|
|
6196
|
+
)
|
|
6197
|
+
] })
|
|
6198
|
+
] }),
|
|
5590
6199
|
formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
|
|
5591
6200
|
/* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: "Continue" }) })
|
|
5592
6201
|
] }),
|
|
@@ -5610,6 +6219,21 @@ function SendModalTemplate({
|
|
|
5610
6219
|
}
|
|
5611
6220
|
);
|
|
5612
6221
|
}
|
|
6222
|
+
var MAX_MEMO_ID = 18446744073709551615n;
|
|
6223
|
+
var SOLANA_FEE_MARGIN_LAMPORTS = 10000n;
|
|
6224
|
+
function solanaMaxAfterFee(available) {
|
|
6225
|
+
const lamports = BigInt(toBaseUnits(available, 9)) - SOLANA_FEE_MARGIN_LAMPORTS;
|
|
6226
|
+
return fromBaseUnits((lamports > 0n ? lamports : 0n).toString(), 9);
|
|
6227
|
+
}
|
|
6228
|
+
function parseMemo(type, raw) {
|
|
6229
|
+
const value = type === "id" ? raw.trim() : raw;
|
|
6230
|
+
if (value.trim() === "") return void 0;
|
|
6231
|
+
if (type === "text") {
|
|
6232
|
+
return new TextEncoder().encode(value).length > 28 ? "The memo text can be at most 28 bytes." : { type, value };
|
|
6233
|
+
}
|
|
6234
|
+
if (!/^\d+$/.test(value) || BigInt(value) > MAX_MEMO_ID) return "The memo ID must be a whole number up to 2^64 - 1.";
|
|
6235
|
+
return { type, value };
|
|
6236
|
+
}
|
|
5613
6237
|
function assetParam(record) {
|
|
5614
6238
|
if (record.type === "native") return { type: "native" };
|
|
5615
6239
|
if (record.type === "credit_alphanum4") {
|
|
@@ -5634,6 +6258,8 @@ function SendModal({ onClose }) {
|
|
|
5634
6258
|
const [step, setStep] = useState("form");
|
|
5635
6259
|
const [amount, setAmount] = useState("");
|
|
5636
6260
|
const [destination, setDestination] = useState("");
|
|
6261
|
+
const [memo, setMemo] = useState("");
|
|
6262
|
+
const [memoType, setMemoType] = useState("text");
|
|
5637
6263
|
const [selectedAsset, setSelectedAsset] = useState(null);
|
|
5638
6264
|
const [showXdr, setShowXdr] = useState(false);
|
|
5639
6265
|
const [copied, setCopied] = useState(false);
|
|
@@ -5644,9 +6270,10 @@ function SendModal({ onClose }) {
|
|
|
5644
6270
|
useEffect(() => {
|
|
5645
6271
|
if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
|
|
5646
6272
|
}, [chains, selectedChain]);
|
|
5647
|
-
const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA";
|
|
6273
|
+
const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA" || selectedChain === "POLYGON";
|
|
5648
6274
|
const notReadyReason = walletNotReadyReason(wallet, selectedChain);
|
|
5649
|
-
const isBaseUnitChain = selectedChain === "SOLANA";
|
|
6275
|
+
const isBaseUnitChain = selectedChain === "SOLANA" || selectedChain === "POLYGON";
|
|
6276
|
+
const nativeDecimals = selectedChain === "POLYGON" ? 18 : 9;
|
|
5650
6277
|
useEffect(() => {
|
|
5651
6278
|
void refreshWalletBalance();
|
|
5652
6279
|
}, [refreshWalletBalance]);
|
|
@@ -5674,7 +6301,7 @@ function SendModal({ onClose }) {
|
|
|
5674
6301
|
const hash = transaction.step === "success" ? transaction.hash : null;
|
|
5675
6302
|
const buildData = "buildData" in transaction ? transaction.buildData : null;
|
|
5676
6303
|
const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : buildData ? "public" : network === "testnet" ? "testnet" : "public";
|
|
5677
|
-
const explorerUrl = !hash ? null : selectedChain === "SOLANA" ? `https://explorer.solana.com/tx/${hash}${explorerNetwork === "testnet" ? "?cluster=devnet" : ""}` : `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}`;
|
|
6304
|
+
const explorerUrl = !hash ? null : selectedChain === "SOLANA" ? `https://explorer.solana.com/tx/${hash}${explorerNetwork === "testnet" ? "?cluster=devnet" : ""}` : selectedChain === "POLYGON" ? `https://${explorerNetwork === "testnet" ? "amoy." : ""}polygonscan.com/tx/${hash}` : `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}`;
|
|
5678
6305
|
const IN_FLIGHT_STEPS3 = [
|
|
5679
6306
|
"building",
|
|
5680
6307
|
"signing",
|
|
@@ -5713,33 +6340,42 @@ function SendModal({ onClose }) {
|
|
|
5713
6340
|
setFormError("Enter a destination address");
|
|
5714
6341
|
return;
|
|
5715
6342
|
}
|
|
6343
|
+
const parsedMemo = selectedChain === "STELLAR" ? parseMemo(memoType, memo) : void 0;
|
|
6344
|
+
if (typeof parsedMemo === "string") {
|
|
6345
|
+
setFormError(parsedMemo);
|
|
6346
|
+
return;
|
|
6347
|
+
}
|
|
5716
6348
|
const isNativeAsset = selectedAsset.type === "native";
|
|
5717
6349
|
if (isBaseUnitChain && !isNativeAsset && (!selectedAsset.issuer || selectedAsset.decimals === void 0)) {
|
|
5718
|
-
setFormError("This token is missing its
|
|
6350
|
+
setFormError("This token is missing its contract or decimals and cannot be sent.");
|
|
5719
6351
|
return;
|
|
5720
6352
|
}
|
|
5721
6353
|
let sendAmount = amount;
|
|
5722
6354
|
if (isBaseUnitChain) {
|
|
5723
6355
|
try {
|
|
5724
|
-
sendAmount = toBaseUnits(amount, selectedAsset.decimals ??
|
|
6356
|
+
sendAmount = toBaseUnits(amount, selectedAsset.decimals ?? nativeDecimals);
|
|
5725
6357
|
} catch (e) {
|
|
5726
6358
|
setFormError(e instanceof Error ? e.message : "Invalid amount");
|
|
5727
6359
|
return;
|
|
5728
6360
|
}
|
|
5729
6361
|
}
|
|
5730
6362
|
setStep("tx");
|
|
5731
|
-
if (selectedChain === "SOLANA") {
|
|
6363
|
+
if (selectedChain === "SOLANA" || selectedChain === "POLYGON") {
|
|
5732
6364
|
await sendPayment({
|
|
5733
|
-
chain:
|
|
6365
|
+
chain: selectedChain,
|
|
5734
6366
|
destination: destination.trim(),
|
|
5735
6367
|
amount: sendAmount,
|
|
5736
|
-
//
|
|
5737
|
-
// which the guard above proved is present.
|
|
6368
|
+
// A native coin carries no token id; an SPL mint or ERC-20 contract rides
|
|
6369
|
+
// in `issuer`, which the guard above proved is present.
|
|
5738
6370
|
...isNativeAsset ? {} : { mint: selectedAsset.issuer }
|
|
5739
6371
|
});
|
|
5740
6372
|
return;
|
|
5741
6373
|
}
|
|
5742
|
-
await buildTx(
|
|
6374
|
+
await buildTx(
|
|
6375
|
+
"payment",
|
|
6376
|
+
{ destination: destination.trim(), amount, asset: assetParam(selectedAsset) },
|
|
6377
|
+
parsedMemo ? { memo: parsedMemo } : void 0
|
|
6378
|
+
);
|
|
5743
6379
|
}
|
|
5744
6380
|
function handleSignAndSend() {
|
|
5745
6381
|
if (transaction.step === "built") {
|
|
@@ -5748,7 +6384,15 @@ function SendModal({ onClose }) {
|
|
|
5748
6384
|
}
|
|
5749
6385
|
function handleMax() {
|
|
5750
6386
|
if (selectedAsset?.available !== null && selectedAsset?.available !== void 0) {
|
|
5751
|
-
|
|
6387
|
+
const paysOwnSolFee = selectedChain === "SOLANA" && selectedAsset.type === "native" && selectedAsset.sponsored !== true;
|
|
6388
|
+
let max = selectedAsset.available;
|
|
6389
|
+
if (paysOwnSolFee) {
|
|
6390
|
+
try {
|
|
6391
|
+
max = solanaMaxAfterFee(selectedAsset.available);
|
|
6392
|
+
} catch {
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
setAmount(max);
|
|
5752
6396
|
setFormError("");
|
|
5753
6397
|
}
|
|
5754
6398
|
}
|
|
@@ -5824,6 +6468,10 @@ function SendModal({ onClose }) {
|
|
|
5824
6468
|
onMax: handleMax,
|
|
5825
6469
|
onPaste: handlePaste,
|
|
5826
6470
|
onDestinationChange: setDestination,
|
|
6471
|
+
memo,
|
|
6472
|
+
memoType,
|
|
6473
|
+
onMemoChange: setMemo,
|
|
6474
|
+
onMemoTypeChange: setMemoType,
|
|
5827
6475
|
onSubmit: () => void handleSubmit(),
|
|
5828
6476
|
onSignAndSend: handleSignAndSend,
|
|
5829
6477
|
onToggleXdr: () => setShowXdr((v) => !v),
|
|
@@ -6231,7 +6879,7 @@ function SwapModalTemplate({
|
|
|
6231
6879
|
onDone
|
|
6232
6880
|
}
|
|
6233
6881
|
),
|
|
6234
|
-
/* @__PURE__ */ jsx(PollarModalFooter, {
|
|
6882
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
6235
6883
|
]
|
|
6236
6884
|
}
|
|
6237
6885
|
);
|
|
@@ -6943,9 +7591,12 @@ function EarnModal({ onClose }) {
|
|
|
6943
7591
|
var nonEmpty2 = (value) => typeof value === "string" && !!value.trim();
|
|
6944
7592
|
function requiredCardStep(error) {
|
|
6945
7593
|
if (!error || typeof error !== "object") return null;
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
7594
|
+
if (!("code" in error) || error.code !== "SDK_CARDS_REQUIREMENT_REQUIRED") return null;
|
|
7595
|
+
const body = "body" in error ? error.body : void 0;
|
|
7596
|
+
if (typeof body !== "object" || body === null) return null;
|
|
7597
|
+
const cardProviderId = "cardProviderId" in body ? body.cardProviderId : void 0;
|
|
7598
|
+
const requirementType = "requirementType" in body ? body.requirementType : void 0;
|
|
7599
|
+
const optionId = "optionId" in body ? body.optionId : void 0;
|
|
6949
7600
|
if (!nonEmpty2(cardProviderId) || !nonEmpty2(optionId)) return null;
|
|
6950
7601
|
if (requirementType === "KYC" || requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") {
|
|
6951
7602
|
return { cardProviderId, type: requirementType, optionId };
|
|
@@ -7599,15 +8250,17 @@ function CardModal({ onClose }) {
|
|
|
7599
8250
|
transactions === null && /* @__PURE__ */ jsx("p", { className: "pollar-card-note", children: "Loading..." }),
|
|
7600
8251
|
transactions?.length === 0 && /* @__PURE__ */ jsx("p", { className: "pollar-card-note", children: "No movements yet." }),
|
|
7601
8252
|
transactions?.map((tx) => {
|
|
7602
|
-
const
|
|
7603
|
-
const
|
|
8253
|
+
const localAmount = typeof tx.purchase?.localAmount === "number" ? tx.purchase.localAmount : null;
|
|
8254
|
+
const localCurrency = typeof tx.purchase?.localCurrency === "string" ? tx.purchase.localCurrency : null;
|
|
8255
|
+
const purchaseStatus = typeof tx.purchase?.status === "string" ? tx.purchase.status : null;
|
|
8256
|
+
const local = localAmount && localCurrency && localCurrency !== "USD" ? money(String(localAmount), localCurrency) : null;
|
|
7604
8257
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-card-tx", children: [
|
|
7605
8258
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
7606
8259
|
tx.merchantName ?? TX_LABELS[tx.type] ?? tx.type,
|
|
7607
8260
|
/* @__PURE__ */ jsxs("span", { className: "pollar-card-tx-meta", children: [
|
|
7608
8261
|
new Date(tx.occurredAt).toLocaleDateString(),
|
|
7609
8262
|
tx.merchantName ? ` \xB7 ${TX_LABELS[tx.type] ?? tx.type}` : "",
|
|
7610
|
-
|
|
8263
|
+
purchaseStatus && purchaseStatus !== "completed" ? ` \xB7 ${purchaseStatus}` : ""
|
|
7611
8264
|
] })
|
|
7612
8265
|
] }),
|
|
7613
8266
|
/* @__PURE__ */ jsxs("div", { className: "pollar-card-tx-amount", "data-direction": tx.direction ?? void 0, children: [
|
|
@@ -7977,6 +8630,843 @@ function SessionsModal({ onClose }) {
|
|
|
7977
8630
|
}
|
|
7978
8631
|
) });
|
|
7979
8632
|
}
|
|
8633
|
+
function useCosign() {
|
|
8634
|
+
const { getClient, isAuthenticated } = usePollar();
|
|
8635
|
+
const [state, setState] = useState({ pending: [] });
|
|
8636
|
+
const [loaded, setLoaded] = useState(false);
|
|
8637
|
+
useEffect(() => getClient().onCosignStateChange(setState), [getClient]);
|
|
8638
|
+
const refresh = useCallback(async () => {
|
|
8639
|
+
if (!isAuthenticated) return;
|
|
8640
|
+
try {
|
|
8641
|
+
await getClient().cosign.pending();
|
|
8642
|
+
} finally {
|
|
8643
|
+
setLoaded(true);
|
|
8644
|
+
}
|
|
8645
|
+
}, [getClient, isAuthenticated]);
|
|
8646
|
+
useEffect(() => {
|
|
8647
|
+
void refresh();
|
|
8648
|
+
}, [refresh]);
|
|
8649
|
+
return {
|
|
8650
|
+
pending: state.pending,
|
|
8651
|
+
...state.lastResolved ? { lastResolved: state.lastResolved } : {},
|
|
8652
|
+
loaded,
|
|
8653
|
+
refresh,
|
|
8654
|
+
approve: useCallback((request) => getClient().cosign.approve(request), [getClient]),
|
|
8655
|
+
reject: useCallback((request) => getClient().cosign.reject(request), [getClient]),
|
|
8656
|
+
verify: useCallback((request) => getClient().cosign.verify(request), [getClient]),
|
|
8657
|
+
inspectQuorum: useCallback(() => getClient().cosign.inspectQuorum(), [getClient]),
|
|
8658
|
+
devices: useCallback(() => getClient().cosign.devices(), [getClient]),
|
|
8659
|
+
revokeDevice: useCallback(
|
|
8660
|
+
(deviceId, options) => getClient().cosign.revokeDevice(deviceId, options),
|
|
8661
|
+
[getClient]
|
|
8662
|
+
),
|
|
8663
|
+
switchCustody: useCallback((to) => getClient().cosign.switchCustody(to), [getClient])
|
|
8664
|
+
};
|
|
8665
|
+
}
|
|
8666
|
+
var OPERATION_LABELS = {
|
|
8667
|
+
payment: "Send",
|
|
8668
|
+
path_payment_strict_send: "Swap",
|
|
8669
|
+
change_trust: "Add asset",
|
|
8670
|
+
create_account: "Create account",
|
|
8671
|
+
invoke_contract: "Contract call",
|
|
8672
|
+
manage_data: "Manage data"
|
|
8673
|
+
};
|
|
8674
|
+
function labelOf(request) {
|
|
8675
|
+
const op = request.operation ?? "";
|
|
8676
|
+
return OPERATION_LABELS[op] ?? (op ? op.replace(/_/g, " ") : "Transaction");
|
|
8677
|
+
}
|
|
8678
|
+
function expiresIn(request) {
|
|
8679
|
+
const ms = new Date(request.expiresAt).getTime() - Date.now();
|
|
8680
|
+
if (!Number.isFinite(ms) || ms <= 0) return "expired";
|
|
8681
|
+
const h = Math.floor(ms / 36e5);
|
|
8682
|
+
const m = Math.floor(ms % 36e5 / 6e4);
|
|
8683
|
+
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
8684
|
+
}
|
|
8685
|
+
function readableAmount(details) {
|
|
8686
|
+
const { amount, decimals, symbol } = details;
|
|
8687
|
+
if (typeof amount !== "string" || typeof decimals !== "number" || typeof symbol !== "string") return null;
|
|
8688
|
+
try {
|
|
8689
|
+
return { amount: fromBaseUnits(amount, decimals), symbol };
|
|
8690
|
+
} catch {
|
|
8691
|
+
return null;
|
|
8692
|
+
}
|
|
8693
|
+
}
|
|
8694
|
+
function detailRows(request) {
|
|
8695
|
+
const details = request.details ?? {};
|
|
8696
|
+
const rows = [];
|
|
8697
|
+
const push = (label, value) => {
|
|
8698
|
+
if (value === void 0 || value === null || value === "") return;
|
|
8699
|
+
rows.push([
|
|
8700
|
+
label,
|
|
8701
|
+
typeof value === "string" && value.length > 24 && /^[GC][A-Z2-7]{55}$/.test(value) ? cropAddress(value) : String(value)
|
|
8702
|
+
]);
|
|
8703
|
+
};
|
|
8704
|
+
push("Destination", details.destination);
|
|
8705
|
+
const readable = readableAmount(details);
|
|
8706
|
+
push("Amount", readable ? `${readable.amount} ${readable.symbol}` : details.amount);
|
|
8707
|
+
push("Asset", details.asset ?? details.assetCode ?? readable?.symbol);
|
|
8708
|
+
push("Network", request.network);
|
|
8709
|
+
return rows;
|
|
8710
|
+
}
|
|
8711
|
+
function CloseIcon2() {
|
|
8712
|
+
return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
|
|
8713
|
+
}
|
|
8714
|
+
function CosignModalTemplate({
|
|
8715
|
+
theme,
|
|
8716
|
+
accentColor,
|
|
8717
|
+
styleOverrides,
|
|
8718
|
+
pending,
|
|
8719
|
+
current,
|
|
8720
|
+
verification,
|
|
8721
|
+
busy,
|
|
8722
|
+
error,
|
|
8723
|
+
approveBlocked = false,
|
|
8724
|
+
lastResolved,
|
|
8725
|
+
explorerUrl,
|
|
8726
|
+
onSelect,
|
|
8727
|
+
onApprove,
|
|
8728
|
+
onReject,
|
|
8729
|
+
onClose
|
|
8730
|
+
}) {
|
|
8731
|
+
const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
|
|
8732
|
+
const verified = verification?.ok === true;
|
|
8733
|
+
const canVote = !!current && verified && !busy && !approveBlocked;
|
|
8734
|
+
return /* @__PURE__ */ jsxs(
|
|
8735
|
+
"div",
|
|
8736
|
+
{
|
|
8737
|
+
className: "pollar-modal-card pollar-cosign-modal",
|
|
8738
|
+
"data-theme": theme,
|
|
8739
|
+
style: cssVars,
|
|
8740
|
+
onClick: (e) => e.stopPropagation(),
|
|
8741
|
+
children: [
|
|
8742
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
|
|
8743
|
+
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm with your device" }),
|
|
8744
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon2, {}) }) })
|
|
8745
|
+
] }),
|
|
8746
|
+
pending.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-empty", children: lastResolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8747
|
+
lastResolved.status === "APPROVED" && /* @__PURE__ */ jsxs("svg", { className: "pollar-cosign-approved-icon", width: "44", height: "44", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
|
|
8748
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
|
|
8749
|
+
/* @__PURE__ */ jsx(
|
|
8750
|
+
"path",
|
|
8751
|
+
{
|
|
8752
|
+
d: "M3.5 7l2.5 2.5 4.5-5",
|
|
8753
|
+
stroke: "white",
|
|
8754
|
+
strokeWidth: "1.2",
|
|
8755
|
+
strokeLinecap: "round",
|
|
8756
|
+
strokeLinejoin: "round"
|
|
8757
|
+
}
|
|
8758
|
+
)
|
|
8759
|
+
] }),
|
|
8760
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-empty-title", children: lastResolved.status === "APPROVED" ? "Approved" : lastResolved.status === "REJECTED" ? "Rejected" : lastResolved.status.toLowerCase() }),
|
|
8761
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: lastResolved.submit?.hash ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8762
|
+
"Sent. Transaction",
|
|
8763
|
+
" ",
|
|
8764
|
+
explorerUrl ? /* @__PURE__ */ jsx("a", { className: "pollar-cosign-tx-link", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: cropAddress(lastResolved.submit.hash) }) : cropAddress(lastResolved.submit.hash)
|
|
8765
|
+
] }) : lastResolved.signedXdr ? "Signed. The app will send it." : "Nothing was signed." })
|
|
8766
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Nothing waits for your approval." }) }),
|
|
8767
|
+
pending.length > 1 && /* @__PURE__ */ jsx("ul", { className: "pollar-cosign-list", children: pending.map((r) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
8768
|
+
"button",
|
|
8769
|
+
{
|
|
8770
|
+
type: "button",
|
|
8771
|
+
className: `pollar-cosign-item${current?.id === r.id ? " pollar-cosign-item-active" : ""}`,
|
|
8772
|
+
onClick: () => onSelect(r),
|
|
8773
|
+
children: [
|
|
8774
|
+
/* @__PURE__ */ jsx("span", { children: labelOf(r) }),
|
|
8775
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-cosign-muted", children: expiresIn(r) })
|
|
8776
|
+
]
|
|
8777
|
+
}
|
|
8778
|
+
) }, r.id)) }),
|
|
8779
|
+
current && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
|
|
8780
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf(current) }),
|
|
8781
|
+
current.summary && /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: current.summary }),
|
|
8782
|
+
/* @__PURE__ */ jsxs("dl", { className: "pollar-cosign-rows", children: [
|
|
8783
|
+
detailRows(current).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
|
|
8784
|
+
/* @__PURE__ */ jsx("dt", { children: k }),
|
|
8785
|
+
/* @__PURE__ */ jsx("dd", { children: v })
|
|
8786
|
+
] }, k)),
|
|
8787
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
|
|
8788
|
+
/* @__PURE__ */ jsx("dt", { children: "Expires in" }),
|
|
8789
|
+
/* @__PURE__ */ jsx("dd", { children: expiresIn(current) })
|
|
8790
|
+
] })
|
|
8791
|
+
] }),
|
|
8792
|
+
/* @__PURE__ */ jsxs(
|
|
8793
|
+
"div",
|
|
8794
|
+
{
|
|
8795
|
+
className: `pollar-cosign-verify pollar-cosign-verify-${verification ? verification.ok ? "ok" : "bad" : "pending"}`,
|
|
8796
|
+
children: [
|
|
8797
|
+
!verification && "Checking the transaction against Turnkey...",
|
|
8798
|
+
verification?.ok && "Verified: this is exactly what will be signed.",
|
|
8799
|
+
verification && !verification.ok && `Not verified: ${verification.reason}`
|
|
8800
|
+
]
|
|
8801
|
+
}
|
|
8802
|
+
),
|
|
8803
|
+
error && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-error", children: error }),
|
|
8804
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-cosign-actions", children: [
|
|
8805
|
+
/* @__PURE__ */ jsx(
|
|
8806
|
+
"button",
|
|
8807
|
+
{
|
|
8808
|
+
type: "button",
|
|
8809
|
+
className: "pollar-cosign-btn pollar-cosign-btn-secondary",
|
|
8810
|
+
disabled: !!busy,
|
|
8811
|
+
onClick: () => onReject(current),
|
|
8812
|
+
children: busy === "rejecting" ? "Rejecting..." : "Reject"
|
|
8813
|
+
}
|
|
8814
|
+
),
|
|
8815
|
+
/* @__PURE__ */ jsx(
|
|
8816
|
+
"button",
|
|
8817
|
+
{
|
|
8818
|
+
type: "button",
|
|
8819
|
+
className: "pollar-cosign-btn pollar-cosign-btn-primary",
|
|
8820
|
+
disabled: !canVote,
|
|
8821
|
+
onClick: () => onApprove(current),
|
|
8822
|
+
children: busy === "approving" ? "Approving..." : "Approve"
|
|
8823
|
+
}
|
|
8824
|
+
)
|
|
8825
|
+
] })
|
|
8826
|
+
] }),
|
|
8827
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
8828
|
+
]
|
|
8829
|
+
}
|
|
8830
|
+
);
|
|
8831
|
+
}
|
|
8832
|
+
function CosignModal({ onClose }) {
|
|
8833
|
+
const { getClient, styles, network } = usePollar();
|
|
8834
|
+
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
8835
|
+
const { pending, lastResolved, approve, reject, verify, inspectQuorum } = useCosign();
|
|
8836
|
+
const [selectedId, setSelectedId] = useState(null);
|
|
8837
|
+
const [verification, setVerification] = useState(null);
|
|
8838
|
+
const [busy, setBusy] = useState(null);
|
|
8839
|
+
const [error, setError] = useState(null);
|
|
8840
|
+
const [approveFailedFor, setApproveFailedFor] = useState(null);
|
|
8841
|
+
const [quorumFailure, setQuorumFailure] = useState(null);
|
|
8842
|
+
useEffect(() => {
|
|
8843
|
+
let cancelled = false;
|
|
8844
|
+
inspectQuorum().then((q) => {
|
|
8845
|
+
if (!cancelled && !q.ok) setQuorumFailure(q.reason);
|
|
8846
|
+
}).catch(() => void 0);
|
|
8847
|
+
return () => {
|
|
8848
|
+
cancelled = true;
|
|
8849
|
+
};
|
|
8850
|
+
}, [inspectQuorum]);
|
|
8851
|
+
const current = pending.find((r) => r.id === selectedId) ?? pending[0] ?? null;
|
|
8852
|
+
const currentRef = useRef(current);
|
|
8853
|
+
currentRef.current = current;
|
|
8854
|
+
const currentId = current?.id ?? null;
|
|
8855
|
+
useEffect(() => {
|
|
8856
|
+
const request = currentRef.current;
|
|
8857
|
+
if (!request) return;
|
|
8858
|
+
setVerification(null);
|
|
8859
|
+
setError(null);
|
|
8860
|
+
setApproveFailedFor(null);
|
|
8861
|
+
let cancelled = false;
|
|
8862
|
+
verify(request).then((v) => {
|
|
8863
|
+
if (!cancelled) setVerification(v);
|
|
8864
|
+
}).catch((err) => {
|
|
8865
|
+
if (!cancelled) setVerification({ ok: false, reason: err instanceof Error ? err.message : String(err) });
|
|
8866
|
+
});
|
|
8867
|
+
return () => {
|
|
8868
|
+
cancelled = true;
|
|
8869
|
+
};
|
|
8870
|
+
}, [currentId, verify]);
|
|
8871
|
+
const onCloseRef = useRef(onClose);
|
|
8872
|
+
onCloseRef.current = onClose;
|
|
8873
|
+
useEffect(() => {
|
|
8874
|
+
return getClient().onAuthStateChange((authState) => {
|
|
8875
|
+
if (authState.step === "idle") onCloseRef.current();
|
|
8876
|
+
});
|
|
8877
|
+
}, [getClient]);
|
|
8878
|
+
const run = useCallback(
|
|
8879
|
+
async (kind, request, action) => {
|
|
8880
|
+
setBusy(kind);
|
|
8881
|
+
setError(null);
|
|
8882
|
+
try {
|
|
8883
|
+
await action(request);
|
|
8884
|
+
} catch (err) {
|
|
8885
|
+
setError(transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null));
|
|
8886
|
+
if (kind === "approving") setApproveFailedFor(request.id);
|
|
8887
|
+
} finally {
|
|
8888
|
+
setBusy(null);
|
|
8889
|
+
}
|
|
8890
|
+
},
|
|
8891
|
+
[]
|
|
8892
|
+
);
|
|
8893
|
+
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
|
|
8894
|
+
CosignModalTemplate,
|
|
8895
|
+
{
|
|
8896
|
+
theme,
|
|
8897
|
+
accentColor,
|
|
8898
|
+
styleOverrides,
|
|
8899
|
+
pending,
|
|
8900
|
+
current,
|
|
8901
|
+
verification: current ? quorumFailure ? { ok: false, reason: quorumFailure } : verification : null,
|
|
8902
|
+
busy,
|
|
8903
|
+
error,
|
|
8904
|
+
approveBlocked: !!current && approveFailedFor === current.id,
|
|
8905
|
+
...lastResolved ? { lastResolved } : {},
|
|
8906
|
+
explorerUrl: lastResolved?.submit?.hash ? explorerTxUrl(lastResolved.network, lastResolved.submit.hash, network) : null,
|
|
8907
|
+
onSelect: (r) => setSelectedId(r.id),
|
|
8908
|
+
onApprove: (r) => void run("approving", r, approve),
|
|
8909
|
+
onReject: (r) => void run("rejecting", r, reject),
|
|
8910
|
+
onClose
|
|
8911
|
+
}
|
|
8912
|
+
) });
|
|
8913
|
+
}
|
|
8914
|
+
var CHAIN_NAME3 = { STELLAR: "Stellar", SOLANA: "Solana", POLYGON: "Polygon" };
|
|
8915
|
+
var short = (address) => `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
8916
|
+
var SELF_CUSTODY_RISKS = "In a self-custody wallet only your device can sign. If you lose this device and every recovery method, nobody can recover the wallet or its funds, not the app and not Pollar. Nobody can undo or freeze a transaction you sign.";
|
|
8917
|
+
function CustodyMigrationModalTemplate(p) {
|
|
8918
|
+
const cssVars = buildModalCssVars(p.theme, p.accentColor, p.styleOverrides);
|
|
8919
|
+
const [pick, setPick] = useState(p.state.choice ? null : p.state.toCustody);
|
|
8920
|
+
const [risksAccepted, setRisksAccepted] = useState(false);
|
|
8921
|
+
const custody = p.state.choice ? pick : p.state.toCustody;
|
|
8922
|
+
const canMove = !!custody && (custody !== "SELF_CUSTODY" || risksAccepted) && !p.busy;
|
|
8923
|
+
const moving = p.state.wallets.filter((w) => w.required);
|
|
8924
|
+
const changing = moving.filter((w) => w.addressChanges);
|
|
8925
|
+
const failed = moving.some((w) => w.status === "FAILED");
|
|
8926
|
+
return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-recovery-modal", "data-theme": p.theme, style: cssVars, children: [
|
|
8927
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-modal-header", children: /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Move your wallets to this device" }) }),
|
|
8928
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-recovery-body", children: [
|
|
8929
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "Until now Pollar held the keys of your wallets. This app now keeps wallets on its users' devices, so they move here, with everything they hold." }),
|
|
8930
|
+
/* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
8931
|
+
/* @__PURE__ */ jsx("h3", { children: "What moves" }),
|
|
8932
|
+
moving.map((w) => /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-item", children: [
|
|
8933
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
8934
|
+
CHAIN_NAME3[w.chain] ?? w.chain,
|
|
8935
|
+
" \xB7 ",
|
|
8936
|
+
short(w.address)
|
|
8937
|
+
] }),
|
|
8938
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-recovery-tag", children: w.addressChanges ? "New address" : "Same address" })
|
|
8939
|
+
] }, w.chain))
|
|
8940
|
+
] }),
|
|
8941
|
+
changing.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: [
|
|
8942
|
+
"Your ",
|
|
8943
|
+
changing.map((w) => CHAIN_NAME3[w.chain] ?? w.chain).join(" and "),
|
|
8944
|
+
" address changes: every token and the balance go to a new address on this device. Stop sharing the old one; anything sent there later needs help from support to recover."
|
|
8945
|
+
] }),
|
|
8946
|
+
p.state.choice && /* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
8947
|
+
/* @__PURE__ */ jsx("h3", { children: "Choose how your wallet signs" }),
|
|
8948
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-recovery-check", children: [
|
|
8949
|
+
/* @__PURE__ */ jsx("input", { type: "radio", name: "custody", checked: pick === "COSIGNED", onChange: () => setPick("COSIGNED") }),
|
|
8950
|
+
" ",
|
|
8951
|
+
"Co-signed: you and the app approve every signature together, and the wallet can be recovered with help."
|
|
8952
|
+
] }),
|
|
8953
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-recovery-check", children: [
|
|
8954
|
+
/* @__PURE__ */ jsx("input", { type: "radio", name: "custody", checked: pick === "SELF_CUSTODY", onChange: () => setPick("SELF_CUSTODY") }),
|
|
8955
|
+
" ",
|
|
8956
|
+
"Self-custody: only your device signs. Nobody else can move or recover your funds."
|
|
8957
|
+
] })
|
|
8958
|
+
] }),
|
|
8959
|
+
custody === "SELF_CUSTODY" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8960
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: SELF_CUSTODY_RISKS }),
|
|
8961
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-recovery-check", children: [
|
|
8962
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: risksAccepted, onChange: (e) => setRisksAccepted(e.target.checked) }),
|
|
8963
|
+
" I understand and accept these risks"
|
|
8964
|
+
] })
|
|
8965
|
+
] }),
|
|
8966
|
+
failed && !p.error && /* @__PURE__ */ jsx("p", { className: "pollar-recovery-error", role: "alert", children: "The last attempt did not finish. What did not move is still where it was; try again." }),
|
|
8967
|
+
p.blockedReasons && /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: [
|
|
8968
|
+
"A wallet cannot move while something still uses it: ",
|
|
8969
|
+
p.blockedReasons.join(", "),
|
|
8970
|
+
". Let it finish and try again."
|
|
8971
|
+
] }),
|
|
8972
|
+
/* @__PURE__ */ jsx(
|
|
8973
|
+
"button",
|
|
8974
|
+
{
|
|
8975
|
+
type: "button",
|
|
8976
|
+
className: "pollar-recovery-btn pollar-recovery-btn-primary",
|
|
8977
|
+
onClick: () => custody && p.onMove(p.state.choice ? custody : void 0),
|
|
8978
|
+
disabled: !canMove,
|
|
8979
|
+
children: p.busy ? "Moving your wallets..." : "Move my wallets"
|
|
8980
|
+
}
|
|
8981
|
+
),
|
|
8982
|
+
p.blockedReasons && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-recovery-btn pollar-recovery-btn-quiet", onClick: p.onLater, disabled: p.busy, children: "Continue for now" }),
|
|
8983
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-recovery-btn pollar-recovery-btn-quiet", onClick: p.onLogout, disabled: p.busy, children: "Log out" }),
|
|
8984
|
+
p.error && /* @__PURE__ */ jsx("p", { className: "pollar-recovery-error", role: "alert", children: p.error })
|
|
8985
|
+
] }),
|
|
8986
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
8987
|
+
] });
|
|
8988
|
+
}
|
|
8989
|
+
function CustodyMigrationModal({ state, onMoved, onLater }) {
|
|
8990
|
+
const { getClient, styles } = usePollar();
|
|
8991
|
+
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
8992
|
+
const [busy, setBusy] = useState(false);
|
|
8993
|
+
const [error, setError] = useState(null);
|
|
8994
|
+
const [blockedReasons, setBlockedReasons] = useState(null);
|
|
8995
|
+
async function move(custody) {
|
|
8996
|
+
setBusy(true);
|
|
8997
|
+
setError(null);
|
|
8998
|
+
setBlockedReasons(null);
|
|
8999
|
+
try {
|
|
9000
|
+
await getClient().cosign.migrateWallets(custody);
|
|
9001
|
+
onMoved();
|
|
9002
|
+
} catch (err) {
|
|
9003
|
+
if (isPollarApiError(err) && err.code === "SDK_CUSTODY_MIGRATION_BLOCKED") {
|
|
9004
|
+
const reasons = err.body.reasons;
|
|
9005
|
+
setBlockedReasons(Array.isArray(reasons) ? reasons.map(String) : []);
|
|
9006
|
+
return;
|
|
9007
|
+
}
|
|
9008
|
+
setError(transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null));
|
|
9009
|
+
} finally {
|
|
9010
|
+
setBusy(false);
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
9013
|
+
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, children: /* @__PURE__ */ jsx(
|
|
9014
|
+
CustodyMigrationModalTemplate,
|
|
9015
|
+
{
|
|
9016
|
+
theme,
|
|
9017
|
+
accentColor,
|
|
9018
|
+
styleOverrides,
|
|
9019
|
+
state,
|
|
9020
|
+
busy,
|
|
9021
|
+
error,
|
|
9022
|
+
blockedReasons,
|
|
9023
|
+
onMove: (custody) => void move(custody),
|
|
9024
|
+
onLater,
|
|
9025
|
+
onLogout: () => void getClient().logout()
|
|
9026
|
+
}
|
|
9027
|
+
) });
|
|
9028
|
+
}
|
|
9029
|
+
function CloseIcon3() {
|
|
9030
|
+
return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
|
|
9031
|
+
}
|
|
9032
|
+
var shortKey = (key) => `${key.slice(0, 6)}...${key.slice(-4)}`;
|
|
9033
|
+
function Button(props) {
|
|
9034
|
+
const running = props.busy === props.id;
|
|
9035
|
+
return /* @__PURE__ */ jsx(
|
|
9036
|
+
"button",
|
|
9037
|
+
{
|
|
9038
|
+
type: "button",
|
|
9039
|
+
className: `pollar-recovery-btn pollar-recovery-btn-${props.kind ?? "quiet"}`,
|
|
9040
|
+
onClick: props.onClick,
|
|
9041
|
+
disabled: props.disabled || props.busy !== null,
|
|
9042
|
+
children: running ? props.busyLabel ?? "Working..." : props.label
|
|
9043
|
+
}
|
|
9044
|
+
);
|
|
9045
|
+
}
|
|
9046
|
+
function CodeForm(props) {
|
|
9047
|
+
const [code, setCode] = useState("");
|
|
9048
|
+
return /* @__PURE__ */ jsxs(
|
|
9049
|
+
"form",
|
|
9050
|
+
{
|
|
9051
|
+
className: "pollar-recovery-row",
|
|
9052
|
+
onSubmit: (e) => {
|
|
9053
|
+
e.preventDefault();
|
|
9054
|
+
if (code.trim()) props.onSubmit(code);
|
|
9055
|
+
},
|
|
9056
|
+
children: [
|
|
9057
|
+
/* @__PURE__ */ jsx(
|
|
9058
|
+
"input",
|
|
9059
|
+
{
|
|
9060
|
+
className: "pollar-recovery-input",
|
|
9061
|
+
value: code,
|
|
9062
|
+
onChange: (e) => setCode(e.target.value),
|
|
9063
|
+
placeholder: "Code from the email",
|
|
9064
|
+
autoComplete: "one-time-code",
|
|
9065
|
+
"aria-label": "Code from the email"
|
|
9066
|
+
}
|
|
9067
|
+
),
|
|
9068
|
+
/* @__PURE__ */ jsx(
|
|
9069
|
+
Button,
|
|
9070
|
+
{
|
|
9071
|
+
busy: props.busy,
|
|
9072
|
+
id: props.id,
|
|
9073
|
+
label: props.label,
|
|
9074
|
+
kind: "primary",
|
|
9075
|
+
onClick: () => code.trim() && props.onSubmit(code)
|
|
9076
|
+
}
|
|
9077
|
+
)
|
|
9078
|
+
]
|
|
9079
|
+
}
|
|
9080
|
+
);
|
|
9081
|
+
}
|
|
9082
|
+
function RecoveryModalTemplate(p) {
|
|
9083
|
+
const cssVars = buildModalCssVars(p.theme, p.accentColor, p.styleOverrides);
|
|
9084
|
+
const [exportAccepted, setExportAccepted] = useState(false);
|
|
9085
|
+
const [risksAccepted, setRisksAccepted] = useState(false);
|
|
9086
|
+
const state = p.state;
|
|
9087
|
+
const locked = !!state && state.required && !state.configured;
|
|
9088
|
+
const risksPending = locked && p.selfCustodyRisks && !risksAccepted;
|
|
9089
|
+
const lastMethod = !!state && state.passkeys.length + (state.email.verified ? 1 : 0) + (state.devices.length >= 2 ? 1 : 0) <= 1;
|
|
9090
|
+
let body;
|
|
9091
|
+
if (!state) {
|
|
9092
|
+
body = /* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "Loading your wallet's recovery methods..." });
|
|
9093
|
+
} else if (!state.required) {
|
|
9094
|
+
body = /* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "This wallet doesn't need recovery methods: it isn't secured by your device." });
|
|
9095
|
+
} else if (!p.thisDeviceEnrolled && p.view !== "recover-email") {
|
|
9096
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9097
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "status", children: "This device isn't part of your wallet yet. Get back in with one of your recovery methods, or ask a device that already has the wallet to add this one." }),
|
|
9098
|
+
state.passkeys.length > 0 && p.passkeyAvailable && /* @__PURE__ */ jsx(Button, { busy: p.busy, id: "recover-passkey", label: "Use a passkey", kind: "primary", onClick: p.onRecoverWithPasskey }),
|
|
9099
|
+
state.email.verified && /* @__PURE__ */ jsx(
|
|
9100
|
+
Button,
|
|
9101
|
+
{
|
|
9102
|
+
busy: p.busy,
|
|
9103
|
+
id: "recover-send",
|
|
9104
|
+
label: `Email a code to ${state.email.address ?? "your email"}`,
|
|
9105
|
+
onClick: () => p.onView("recover-email")
|
|
9106
|
+
}
|
|
9107
|
+
),
|
|
9108
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "enroll", label: "Ask another device to add this one", onClick: p.onRequestEnrollment })
|
|
9109
|
+
] });
|
|
9110
|
+
} else if (risksPending && p.view === "overview") {
|
|
9111
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9112
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: "This is a self-custody wallet: only your device can sign. If you lose this device and every recovery method, nobody can recover the wallet or its funds, not the app and not Pollar. Nobody can undo or freeze a transaction you sign." }),
|
|
9113
|
+
/* @__PURE__ */ jsx(
|
|
9114
|
+
Button,
|
|
9115
|
+
{
|
|
9116
|
+
busy: p.busy,
|
|
9117
|
+
id: "accept-risks",
|
|
9118
|
+
label: "I understand and accept these risks",
|
|
9119
|
+
kind: "primary",
|
|
9120
|
+
onClick: () => setRisksAccepted(true)
|
|
9121
|
+
}
|
|
9122
|
+
)
|
|
9123
|
+
] });
|
|
9124
|
+
} else if (p.view === "recover-email") {
|
|
9125
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9126
|
+
/* @__PURE__ */ jsxs("p", { className: "pollar-recovery-muted", children: [
|
|
9127
|
+
"We'll email a code to ",
|
|
9128
|
+
state.email.address,
|
|
9129
|
+
". The code goes straight to the wallet provider, encrypted on this device."
|
|
9130
|
+
] }),
|
|
9131
|
+
!p.codeSent ? /* @__PURE__ */ jsx(Button, { busy: p.busy, id: "recover-send", label: "Send code", kind: "primary", onClick: p.onSendRecoveryCode }) : /* @__PURE__ */ jsx(CodeForm, { busy: p.busy, id: "recover-code", label: "Recover", onSubmit: p.onRecoverWithCode }),
|
|
9132
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "back", label: "Back", onClick: () => p.onView("overview") })
|
|
9133
|
+
] });
|
|
9134
|
+
} else if (p.view === "email") {
|
|
9135
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9136
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "Confirm an email you control. If you lose this device, a code sent there lets you add a new one. Anyone who controls this inbox could do the same, so use one that is well protected." }),
|
|
9137
|
+
!p.codeSent ? /* @__PURE__ */ jsxs(
|
|
9138
|
+
"form",
|
|
9139
|
+
{
|
|
9140
|
+
className: "pollar-recovery-row",
|
|
9141
|
+
onSubmit: (e) => {
|
|
9142
|
+
e.preventDefault();
|
|
9143
|
+
p.onSendEmailCode();
|
|
9144
|
+
},
|
|
9145
|
+
children: [
|
|
9146
|
+
/* @__PURE__ */ jsx(
|
|
9147
|
+
"input",
|
|
9148
|
+
{
|
|
9149
|
+
className: "pollar-recovery-input",
|
|
9150
|
+
type: "email",
|
|
9151
|
+
value: p.emailDraft,
|
|
9152
|
+
onChange: (e) => p.onEmailDraft(e.target.value),
|
|
9153
|
+
placeholder: "you@example.com",
|
|
9154
|
+
"aria-label": "Email"
|
|
9155
|
+
}
|
|
9156
|
+
),
|
|
9157
|
+
/* @__PURE__ */ jsx(
|
|
9158
|
+
Button,
|
|
9159
|
+
{
|
|
9160
|
+
busy: p.busy,
|
|
9161
|
+
id: "email-send",
|
|
9162
|
+
label: "Send code",
|
|
9163
|
+
kind: "primary",
|
|
9164
|
+
onClick: p.onSendEmailCode,
|
|
9165
|
+
disabled: !p.emailDraft.trim()
|
|
9166
|
+
}
|
|
9167
|
+
)
|
|
9168
|
+
]
|
|
9169
|
+
}
|
|
9170
|
+
) : /* @__PURE__ */ jsx(CodeForm, { busy: p.busy, id: "email-confirm", label: "Confirm", onSubmit: p.onConfirmEmail }),
|
|
9171
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "back", label: "Back", onClick: () => p.onView("overview") })
|
|
9172
|
+
] });
|
|
9173
|
+
} else if (p.view === "export") {
|
|
9174
|
+
body = p.phrase ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9175
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: "Anyone with these words controls every account in this wallet. Write them down offline and never share them." }),
|
|
9176
|
+
/* @__PURE__ */ jsx("pre", { className: "pollar-recovery-phrase", children: p.phrase }),
|
|
9177
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "hide", label: "Hide", kind: "primary", onClick: () => p.onView("overview") })
|
|
9178
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9179
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "Your recovery phrase lets you take this wallet to another app. It is decrypted only on this device; the app and Pollar never see it." }),
|
|
9180
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-recovery-check", children: [
|
|
9181
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: exportAccepted, onChange: (e) => setExportAccepted(e.target.checked) }),
|
|
9182
|
+
" I understand that whoever sees the phrase controls the wallet"
|
|
9183
|
+
] }),
|
|
9184
|
+
/* @__PURE__ */ jsx(
|
|
9185
|
+
Button,
|
|
9186
|
+
{
|
|
9187
|
+
busy: p.busy,
|
|
9188
|
+
id: "export",
|
|
9189
|
+
label: "Show recovery phrase",
|
|
9190
|
+
kind: "danger",
|
|
9191
|
+
onClick: p.onExport,
|
|
9192
|
+
disabled: !exportAccepted
|
|
9193
|
+
}
|
|
9194
|
+
),
|
|
9195
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "back", label: "Back", onClick: () => p.onView("overview") })
|
|
9196
|
+
] });
|
|
9197
|
+
} else {
|
|
9198
|
+
const enrollments = p.devices?.enrollments ?? [];
|
|
9199
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9200
|
+
locked ? /* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-warn", role: "alert", children: "Your wallet is locked until you add a way to recover it. Without one, losing this device means losing the funds, and nobody can get them back, not the app and not Pollar. Add a passkey, a second device or your email." }) : /* @__PURE__ */ jsx("div", { className: "pollar-recovery-banner pollar-recovery-banner-ok", role: "status", children: "Your wallet can be recovered if you lose this device." }),
|
|
9201
|
+
/* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
9202
|
+
/* @__PURE__ */ jsx("h3", { children: "Passkeys" }),
|
|
9203
|
+
state.passkeys.length === 0 && /* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "No passkey yet." }),
|
|
9204
|
+
state.passkeys.map((pk) => /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-item", children: [
|
|
9205
|
+
/* @__PURE__ */ jsx("span", { children: pk.name }),
|
|
9206
|
+
/* @__PURE__ */ jsx(
|
|
9207
|
+
Button,
|
|
9208
|
+
{
|
|
9209
|
+
busy: p.busy,
|
|
9210
|
+
id: `passkey:${pk.authenticatorId}`,
|
|
9211
|
+
label: "Remove",
|
|
9212
|
+
kind: "danger",
|
|
9213
|
+
onClick: () => p.onRemovePasskey(pk.authenticatorId),
|
|
9214
|
+
disabled: lastMethod
|
|
9215
|
+
}
|
|
9216
|
+
)
|
|
9217
|
+
] }, pk.authenticatorId)),
|
|
9218
|
+
p.passkeyAvailable ? /* @__PURE__ */ jsx(Button, { busy: p.busy, id: "passkey", label: "Add a passkey", onClick: p.onAddPasskey }) : /* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: "Passkeys aren't available on this device." })
|
|
9219
|
+
] }),
|
|
9220
|
+
/* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
9221
|
+
/* @__PURE__ */ jsx("h3", { children: "Email" }),
|
|
9222
|
+
state.email.verified ? /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-item", children: [
|
|
9223
|
+
/* @__PURE__ */ jsx("span", { children: state.email.address }),
|
|
9224
|
+
/* @__PURE__ */ jsx("span", { className: "pollar-recovery-tag", children: "Confirmed" })
|
|
9225
|
+
] }) : /* @__PURE__ */ jsx(Button, { busy: p.busy, id: "email", label: "Confirm an email", onClick: () => p.onView("email") })
|
|
9226
|
+
] }),
|
|
9227
|
+
/* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
9228
|
+
/* @__PURE__ */ jsx("h3", { children: "Devices" }),
|
|
9229
|
+
(p.devices?.devices ?? []).map((d) => /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-item", children: [
|
|
9230
|
+
/* @__PURE__ */ jsx("span", { children: d.label ?? shortKey(d.publicKey) }),
|
|
9231
|
+
d.publicKey.toLowerCase() === p.thisDeviceKey && /* @__PURE__ */ jsx("span", { className: "pollar-recovery-tag", children: "This device" })
|
|
9232
|
+
] }, d.id)),
|
|
9233
|
+
enrollments.map((e) => /* @__PURE__ */ jsxs("div", { className: "pollar-recovery-item pollar-recovery-item-pending", children: [
|
|
9234
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
9235
|
+
e.label ?? "A device",
|
|
9236
|
+
" wants to join (",
|
|
9237
|
+
shortKey(e.publicKey),
|
|
9238
|
+
")"
|
|
9239
|
+
] }),
|
|
9240
|
+
/* @__PURE__ */ jsxs("span", { className: "pollar-recovery-actions", children: [
|
|
9241
|
+
/* @__PURE__ */ jsx(
|
|
9242
|
+
Button,
|
|
9243
|
+
{
|
|
9244
|
+
busy: p.busy,
|
|
9245
|
+
id: `approve:${e.id}`,
|
|
9246
|
+
label: "Approve",
|
|
9247
|
+
kind: "primary",
|
|
9248
|
+
onClick: () => p.onApproveDevice(e.id)
|
|
9249
|
+
}
|
|
9250
|
+
),
|
|
9251
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: `reject:${e.id}`, label: "Reject", onClick: () => p.onRejectDevice(e.id) })
|
|
9252
|
+
] })
|
|
9253
|
+
] }, e.id)),
|
|
9254
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-recovery-muted", children: 'To add another device, sign in there and choose "Ask another device to add this one"; it shows up here to approve. Only approve a device you are holding.' })
|
|
9255
|
+
] }),
|
|
9256
|
+
/* @__PURE__ */ jsxs("section", { className: "pollar-recovery-section", children: [
|
|
9257
|
+
/* @__PURE__ */ jsx("h3", { children: "Recovery phrase" }),
|
|
9258
|
+
/* @__PURE__ */ jsx(Button, { busy: p.busy, id: "export", label: "Export recovery phrase", onClick: () => p.onView("export") })
|
|
9259
|
+
] })
|
|
9260
|
+
] });
|
|
9261
|
+
}
|
|
9262
|
+
return /* @__PURE__ */ jsxs(
|
|
9263
|
+
"div",
|
|
9264
|
+
{
|
|
9265
|
+
className: "pollar-modal-card pollar-recovery-modal",
|
|
9266
|
+
"data-theme": p.theme,
|
|
9267
|
+
style: cssVars,
|
|
9268
|
+
onClick: (e) => e.stopPropagation(),
|
|
9269
|
+
children: [
|
|
9270
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
|
|
9271
|
+
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Wallet recovery" }),
|
|
9272
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: p.onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon3, {}) }) })
|
|
9273
|
+
] }),
|
|
9274
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-recovery-body", children: [
|
|
9275
|
+
body,
|
|
9276
|
+
p.notice && /* @__PURE__ */ jsx("p", { className: "pollar-recovery-notice", role: "status", children: p.notice }),
|
|
9277
|
+
p.error && /* @__PURE__ */ jsx("p", { className: "pollar-recovery-error", role: "alert", children: p.error })
|
|
9278
|
+
] }),
|
|
9279
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
9280
|
+
]
|
|
9281
|
+
}
|
|
9282
|
+
);
|
|
9283
|
+
}
|
|
9284
|
+
function errorText2(err) {
|
|
9285
|
+
return transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null);
|
|
9286
|
+
}
|
|
9287
|
+
function RecoveryModal({ onClose }) {
|
|
9288
|
+
const { getClient, styles, refreshRecovery, appConfig } = usePollar();
|
|
9289
|
+
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
9290
|
+
const recovery = getClient().cosign.recovery;
|
|
9291
|
+
const [state, setState] = useState(null);
|
|
9292
|
+
const [devices, setDevices] = useState(null);
|
|
9293
|
+
const [thisDeviceKey, setThisDeviceKey] = useState(null);
|
|
9294
|
+
const [view, setView] = useState("overview");
|
|
9295
|
+
const [busy, setBusy] = useState(null);
|
|
9296
|
+
const [error, setError] = useState(null);
|
|
9297
|
+
const [notice, setNotice] = useState(null);
|
|
9298
|
+
const [emailDraft, setEmailDraft] = useState("");
|
|
9299
|
+
const [codeSent, setCodeSent] = useState(false);
|
|
9300
|
+
const [phrase, setPhrase] = useState(null);
|
|
9301
|
+
const load = useCallback(async () => {
|
|
9302
|
+
const client = getClient();
|
|
9303
|
+
const [nextState, nextDevices, key] = await Promise.all([
|
|
9304
|
+
client.cosign.recovery.status(),
|
|
9305
|
+
client.cosign.devices(),
|
|
9306
|
+
client.cosign.publicKeyHex()
|
|
9307
|
+
]);
|
|
9308
|
+
setState(nextState);
|
|
9309
|
+
setDevices(nextDevices);
|
|
9310
|
+
setThisDeviceKey(key.toLowerCase());
|
|
9311
|
+
if (nextState.email.address) setEmailDraft((d) => d || nextState.email.address);
|
|
9312
|
+
refreshRecovery();
|
|
9313
|
+
}, [getClient, refreshRecovery]);
|
|
9314
|
+
useEffect(() => {
|
|
9315
|
+
load().catch((err) => setError(errorText2(err)));
|
|
9316
|
+
}, [load]);
|
|
9317
|
+
const onCloseRef = useRef(onClose);
|
|
9318
|
+
onCloseRef.current = onClose;
|
|
9319
|
+
useEffect(
|
|
9320
|
+
() => getClient().onAuthStateChange((authState) => {
|
|
9321
|
+
if (authState.step === "idle") onCloseRef.current();
|
|
9322
|
+
}),
|
|
9323
|
+
[getClient]
|
|
9324
|
+
);
|
|
9325
|
+
const run = useCallback(
|
|
9326
|
+
async (key, action, after) => {
|
|
9327
|
+
setBusy(key);
|
|
9328
|
+
setError(null);
|
|
9329
|
+
setNotice(null);
|
|
9330
|
+
try {
|
|
9331
|
+
await action();
|
|
9332
|
+
await load();
|
|
9333
|
+
if (after?.notice) setNotice(after.notice);
|
|
9334
|
+
if (after?.view) setView(after.view);
|
|
9335
|
+
} catch (err) {
|
|
9336
|
+
setError(errorText2(err));
|
|
9337
|
+
} finally {
|
|
9338
|
+
setBusy(null);
|
|
9339
|
+
}
|
|
9340
|
+
},
|
|
9341
|
+
[load]
|
|
9342
|
+
);
|
|
9343
|
+
const enrolled = !!thisDeviceKey && !!devices?.devices.some((d) => d.publicKey.toLowerCase() === thisDeviceKey);
|
|
9344
|
+
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
|
|
9345
|
+
RecoveryModalTemplate,
|
|
9346
|
+
{
|
|
9347
|
+
theme,
|
|
9348
|
+
accentColor,
|
|
9349
|
+
styleOverrides,
|
|
9350
|
+
state,
|
|
9351
|
+
devices,
|
|
9352
|
+
thisDeviceKey,
|
|
9353
|
+
thisDeviceEnrolled: enrolled,
|
|
9354
|
+
passkeyAvailable: recovery.passkeyAvailable,
|
|
9355
|
+
selfCustodyRisks: appConfig.application?.walletCustodyMode === "SELF_CUSTODY" && devices?.custody === "SELF_CUSTODY",
|
|
9356
|
+
view,
|
|
9357
|
+
busy,
|
|
9358
|
+
error,
|
|
9359
|
+
notice,
|
|
9360
|
+
emailDraft,
|
|
9361
|
+
codeSent,
|
|
9362
|
+
phrase,
|
|
9363
|
+
onView: (next) => {
|
|
9364
|
+
setError(null);
|
|
9365
|
+
setNotice(null);
|
|
9366
|
+
setCodeSent(false);
|
|
9367
|
+
if (next !== "export") setPhrase(null);
|
|
9368
|
+
setView(next);
|
|
9369
|
+
},
|
|
9370
|
+
onEmailDraft: setEmailDraft,
|
|
9371
|
+
onAddPasskey: () => void run("passkey", () => recovery.addPasskey(), { notice: "Passkey added." }),
|
|
9372
|
+
onRemovePasskey: (id) => void run(`passkey:${id}`, () => recovery.removePasskey(id)),
|
|
9373
|
+
onSendEmailCode: () => void run("email-send", async () => {
|
|
9374
|
+
await recovery.startEmail(emailDraft.trim());
|
|
9375
|
+
setCodeSent(true);
|
|
9376
|
+
}),
|
|
9377
|
+
onConfirmEmail: (code) => void run("email-confirm", () => recovery.confirmEmail(code.trim()), { notice: "Email confirmed.", view: "overview" }),
|
|
9378
|
+
onApproveDevice: (id) => void run(`approve:${id}`, () => recovery.approveDevice(id), { notice: "Device added." }),
|
|
9379
|
+
onRejectDevice: (id) => void run(`reject:${id}`, () => recovery.rejectDevice(id)),
|
|
9380
|
+
onRequestEnrollment: () => void run("enroll", () => recovery.requestDeviceEnrollment(), {
|
|
9381
|
+
notice: "Request sent. Approve it from a device that already has this wallet within 15 minutes."
|
|
9382
|
+
}),
|
|
9383
|
+
onRecoverWithPasskey: () => void run("recover-passkey", () => recovery.recoverWithPasskey(), {
|
|
9384
|
+
notice: "This device is part of your wallet again."
|
|
9385
|
+
}),
|
|
9386
|
+
onSendRecoveryCode: () => void run("recover-send", async () => {
|
|
9387
|
+
await recovery.startEmailRecovery();
|
|
9388
|
+
setCodeSent(true);
|
|
9389
|
+
}),
|
|
9390
|
+
onRecoverWithCode: (code) => void run("recover-code", () => recovery.recoverWithEmailCode(code.trim()), {
|
|
9391
|
+
notice: "This device is part of your wallet again.",
|
|
9392
|
+
view: "overview"
|
|
9393
|
+
}),
|
|
9394
|
+
onExport: () => void run("export", async () => {
|
|
9395
|
+
setPhrase(await recovery.exportWallet());
|
|
9396
|
+
}),
|
|
9397
|
+
onClose
|
|
9398
|
+
}
|
|
9399
|
+
) });
|
|
9400
|
+
}
|
|
9401
|
+
var OPERATION_LABELS2 = {
|
|
9402
|
+
payment: "Send",
|
|
9403
|
+
path_payment_strict_send: "Swap",
|
|
9404
|
+
change_trust: "Add asset",
|
|
9405
|
+
create_account: "Create account",
|
|
9406
|
+
invoke_contract: "Contract call",
|
|
9407
|
+
manage_data: "Manage data"
|
|
9408
|
+
};
|
|
9409
|
+
function labelOf2(confirmation) {
|
|
9410
|
+
const op = confirmation.operation ?? "";
|
|
9411
|
+
return OPERATION_LABELS2[op] ?? (op ? op.replace(/_/g, " ") : "Transaction");
|
|
9412
|
+
}
|
|
9413
|
+
function detailRows2(confirmation) {
|
|
9414
|
+
const details = confirmation.details ?? {};
|
|
9415
|
+
const rows = [];
|
|
9416
|
+
const push = (label, value) => {
|
|
9417
|
+
if (value === void 0 || value === null || value === "") return;
|
|
9418
|
+
const text = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
9419
|
+
rows.push([label, /^[GC][A-Z2-7]{55}$/.test(text) ? cropAddress(text) : text]);
|
|
9420
|
+
};
|
|
9421
|
+
push("Destination", details.destination);
|
|
9422
|
+
push("Amount", details.amount);
|
|
9423
|
+
push("Asset", details.asset ?? details.assetCode ?? details.mint);
|
|
9424
|
+
push("Network", confirmation.network);
|
|
9425
|
+
return rows;
|
|
9426
|
+
}
|
|
9427
|
+
function CloseIcon4() {
|
|
9428
|
+
return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
|
|
9429
|
+
}
|
|
9430
|
+
function ConfirmModal({ confirmation }) {
|
|
9431
|
+
const { getClient, styles } = usePollar();
|
|
9432
|
+
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
9433
|
+
const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
|
|
9434
|
+
const rejectRef = useRef(confirmation.reject);
|
|
9435
|
+
rejectRef.current = confirmation.reject;
|
|
9436
|
+
useEffect(() => {
|
|
9437
|
+
return getClient().onAuthStateChange((authState) => {
|
|
9438
|
+
if (authState.step === "idle") rejectRef.current();
|
|
9439
|
+
});
|
|
9440
|
+
}, [getClient]);
|
|
9441
|
+
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: confirmation.reject, children: /* @__PURE__ */ jsxs(
|
|
9442
|
+
"div",
|
|
9443
|
+
{
|
|
9444
|
+
className: "pollar-modal-card pollar-cosign-modal",
|
|
9445
|
+
"data-theme": theme,
|
|
9446
|
+
style: cssVars,
|
|
9447
|
+
onClick: (e) => e.stopPropagation(),
|
|
9448
|
+
children: [
|
|
9449
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
|
|
9450
|
+
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm transaction" }),
|
|
9451
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: confirmation.reject, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon4, {}) }) })
|
|
9452
|
+
] }),
|
|
9453
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
|
|
9454
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf2(confirmation) }),
|
|
9455
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "The app asks you to sign this transaction with your wallet." }),
|
|
9456
|
+
/* @__PURE__ */ jsx("dl", { className: "pollar-cosign-rows", children: detailRows2(confirmation).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
|
|
9457
|
+
/* @__PURE__ */ jsx("dt", { children: k }),
|
|
9458
|
+
/* @__PURE__ */ jsx("dd", { children: v })
|
|
9459
|
+
] }, k)) }),
|
|
9460
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-cosign-actions", children: [
|
|
9461
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-cosign-btn pollar-cosign-btn-secondary", onClick: confirmation.reject, children: "Cancel" }),
|
|
9462
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-cosign-btn pollar-cosign-btn-primary", onClick: confirmation.confirm, children: "Confirm" })
|
|
9463
|
+
] })
|
|
9464
|
+
] }),
|
|
9465
|
+
/* @__PURE__ */ jsx(PollarModalFooter, {})
|
|
9466
|
+
]
|
|
9467
|
+
}
|
|
9468
|
+
) });
|
|
9469
|
+
}
|
|
7980
9470
|
function TransactionModalTemplate({
|
|
7981
9471
|
theme,
|
|
7982
9472
|
accentColor,
|
|
@@ -8086,12 +9576,6 @@ function TransactionModal({ onClose }) {
|
|
|
8086
9576
|
) });
|
|
8087
9577
|
}
|
|
8088
9578
|
var PAGE_SIZE = 10;
|
|
8089
|
-
function explorerUrlFor(chain, hash, network) {
|
|
8090
|
-
if (chain === "SOLANA") {
|
|
8091
|
-
return `https://explorer.solana.com/tx/${hash}${network === "testnet" ? "?cluster=devnet" : ""}`;
|
|
8092
|
-
}
|
|
8093
|
-
return `https://stellar.expert/explorer/${network === "testnet" ? "testnet" : "public"}/tx/${hash}`;
|
|
8094
|
-
}
|
|
8095
9579
|
function StatusBadge({ status }) {
|
|
8096
9580
|
return /* @__PURE__ */ jsx("span", { className: "pollar-hist-item-badge", "data-status": status, children: status });
|
|
8097
9581
|
}
|
|
@@ -8101,6 +9585,25 @@ function formatDate(iso) {
|
|
|
8101
9585
|
function truncateMiddle(value, head = 4, tail = 4) {
|
|
8102
9586
|
return value.length <= head + tail + 1 ? value : `${value.slice(0, head)}\u2026${value.slice(-tail)}`;
|
|
8103
9587
|
}
|
|
9588
|
+
function formatFee(fee) {
|
|
9589
|
+
if (fee.unit === "lamports") {
|
|
9590
|
+
try {
|
|
9591
|
+
return `${fromBaseUnits(fee.amount, 9)} SOL`;
|
|
9592
|
+
} catch {
|
|
9593
|
+
}
|
|
9594
|
+
}
|
|
9595
|
+
return `${fee.amount} ${fee.unit}`;
|
|
9596
|
+
}
|
|
9597
|
+
function formatAmount4(details) {
|
|
9598
|
+
const { amount, decimals, symbol } = details ?? {};
|
|
9599
|
+
if (typeof amount !== "string" || typeof decimals !== "number") return null;
|
|
9600
|
+
try {
|
|
9601
|
+
const value = fromBaseUnits(amount, decimals);
|
|
9602
|
+
return typeof symbol === "string" ? `${value} ${symbol}` : value;
|
|
9603
|
+
} catch {
|
|
9604
|
+
return null;
|
|
9605
|
+
}
|
|
9606
|
+
}
|
|
8104
9607
|
var STELLAR_ADDRESS = /\b[GC][A-Z2-7]{55}\b/g;
|
|
8105
9608
|
function AddressChip({ value, label }) {
|
|
8106
9609
|
return /* @__PURE__ */ jsxs("span", { className: "pollar-hist-item-issuer", children: [
|
|
@@ -8208,7 +9711,7 @@ function TxHistoryModalTemplate({
|
|
|
8208
9711
|
txHistory.step === "loaded" && records.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No transactions yet." }),
|
|
8209
9712
|
records.map((record) => {
|
|
8210
9713
|
const hash = typeof record.hash === "string" && record.hash.length > 0 ? record.hash : void 0;
|
|
8211
|
-
const explorerUrl = hash ?
|
|
9714
|
+
const explorerUrl = hash ? explorerTxUrl(record.chain, hash, record.network) : void 0;
|
|
8212
9715
|
const asset = typeof record.details?.asset === "string" ? record.details.asset : void 0;
|
|
8213
9716
|
const colon = asset ? asset.indexOf(":") : -1;
|
|
8214
9717
|
const trustline = record.operation === "change_trust" && asset && colon > 0 ? {
|
|
@@ -8238,15 +9741,17 @@ function TxHistoryModalTemplate({
|
|
|
8238
9741
|
] }),
|
|
8239
9742
|
/* @__PURE__ */ jsxs("span", { className: "pollar-hist-item-meta", children: [
|
|
8240
9743
|
/* @__PURE__ */ jsx("span", { children: record.operation }),
|
|
9744
|
+
record.chain !== "STELLAR" && formatAmount4(record.details) && /* @__PURE__ */ jsxs("span", { children: [
|
|
9745
|
+
"\xB7 ",
|
|
9746
|
+
formatAmount4(record.details)
|
|
9747
|
+
] }),
|
|
8241
9748
|
typeof record.details?.sponsored === "boolean" && /* @__PURE__ */ jsxs("span", { children: [
|
|
8242
9749
|
"\xB7 ",
|
|
8243
9750
|
record.details.sponsored ? "Sponsored" : "Self-paid"
|
|
8244
9751
|
] }),
|
|
8245
9752
|
record.fee && /* @__PURE__ */ jsxs("span", { children: [
|
|
8246
9753
|
"\xB7 ",
|
|
8247
|
-
record.fee
|
|
8248
|
-
" ",
|
|
8249
|
-
record.fee.unit
|
|
9754
|
+
formatFee(record.fee)
|
|
8250
9755
|
] })
|
|
8251
9756
|
] }),
|
|
8252
9757
|
/* @__PURE__ */ jsxs("div", { className: "pollar-hist-item-footer", children: [
|
|
@@ -8557,6 +10062,48 @@ var browserPasskeySigner = async ({ credentialId, challenge }) => {
|
|
|
8557
10062
|
signature: response.signature
|
|
8558
10063
|
};
|
|
8559
10064
|
};
|
|
10065
|
+
var browserRecoveryPasskey = {
|
|
10066
|
+
async create({ challenge, userName }) {
|
|
10067
|
+
const rpId = window.location.hostname;
|
|
10068
|
+
const response = await startRegistration({
|
|
10069
|
+
optionsJSON: {
|
|
10070
|
+
challenge,
|
|
10071
|
+
rp: { id: rpId, name: rpId },
|
|
10072
|
+
user: { id: randomUserId(), name: userName, displayName: userName },
|
|
10073
|
+
pubKeyCredParams: [
|
|
10074
|
+
{ type: "public-key", alg: -7 },
|
|
10075
|
+
{ type: "public-key", alg: -257 }
|
|
10076
|
+
],
|
|
10077
|
+
authenticatorSelection: { residentKey: "preferred", userVerification: "preferred" },
|
|
10078
|
+
attestation: "none",
|
|
10079
|
+
timeout: 6e4
|
|
10080
|
+
}
|
|
10081
|
+
});
|
|
10082
|
+
return {
|
|
10083
|
+
credentialId: response.id,
|
|
10084
|
+
clientDataJson: response.response.clientDataJSON,
|
|
10085
|
+
attestationObject: response.response.attestationObject,
|
|
10086
|
+
transports: response.response.transports ?? []
|
|
10087
|
+
};
|
|
10088
|
+
},
|
|
10089
|
+
async get({ challenge, allowCredentials }) {
|
|
10090
|
+
const rpId = window.location.hostname;
|
|
10091
|
+
const response = await startAuthentication({
|
|
10092
|
+
optionsJSON: {
|
|
10093
|
+
challenge,
|
|
10094
|
+
rpId,
|
|
10095
|
+
allowCredentials: allowCredentials.map((id) => ({ id, type: "public-key" })),
|
|
10096
|
+
userVerification: "preferred"
|
|
10097
|
+
}
|
|
10098
|
+
});
|
|
10099
|
+
return {
|
|
10100
|
+
credentialId: response.id,
|
|
10101
|
+
authenticatorData: response.response.authenticatorData,
|
|
10102
|
+
clientDataJson: response.response.clientDataJSON,
|
|
10103
|
+
signature: response.response.signature
|
|
10104
|
+
};
|
|
10105
|
+
}
|
|
10106
|
+
};
|
|
8560
10107
|
var DEFAULT_APP_CONFIG = {
|
|
8561
10108
|
application: { name: "", network: "testnet", chains: [] },
|
|
8562
10109
|
styles: {}
|
|
@@ -8581,7 +10128,11 @@ function PollarProvider({
|
|
|
8581
10128
|
}) {
|
|
8582
10129
|
const [pollarClient] = useState(() => {
|
|
8583
10130
|
if (isPollarClient(client)) {
|
|
8584
|
-
client.setPasskeyDefaults({
|
|
10131
|
+
client.setPasskeyDefaults({
|
|
10132
|
+
passkey: browserPasskeyCeremony,
|
|
10133
|
+
passkeySign: browserPasskeySigner,
|
|
10134
|
+
recoveryPasskey: browserRecoveryPasskey
|
|
10135
|
+
});
|
|
8585
10136
|
return client;
|
|
8586
10137
|
}
|
|
8587
10138
|
const alreadyBuilt = clientByConfig.get(client);
|
|
@@ -8589,7 +10140,8 @@ function PollarProvider({
|
|
|
8589
10140
|
const built = new PollarClient({
|
|
8590
10141
|
...client,
|
|
8591
10142
|
passkey: client.passkey ?? browserPasskeyCeremony,
|
|
8592
|
-
passkeySign: client.passkeySign ?? browserPasskeySigner
|
|
10143
|
+
passkeySign: client.passkeySign ?? browserPasskeySigner,
|
|
10144
|
+
recoveryPasskey: client.recoveryPasskey ?? browserRecoveryPasskey
|
|
8593
10145
|
});
|
|
8594
10146
|
clientByConfig.set(client, built);
|
|
8595
10147
|
return built;
|
|
@@ -8617,6 +10169,9 @@ function PollarProvider({
|
|
|
8617
10169
|
const [transaction, setTransaction] = useState({ step: "idle" });
|
|
8618
10170
|
const [txHistory, setTxHistory] = useState({ step: "idle" });
|
|
8619
10171
|
const [sessions, setSessions] = useState({ step: "idle" });
|
|
10172
|
+
const [cosign, setCosign] = useState({ pending: [] });
|
|
10173
|
+
const [confirmations, setConfirmations] = useState([]);
|
|
10174
|
+
const firstConfirmation = confirmations[0] ?? null;
|
|
8620
10175
|
const [walletBalance, setWalletBalance] = useState({ step: "idle" });
|
|
8621
10176
|
const [enabledAssets, setEnabledAssets] = useState({ step: "idle" });
|
|
8622
10177
|
const [resolvedConfig, setResolvedConfig] = useState(() => appConfigProp ?? DEFAULT_APP_CONFIG);
|
|
@@ -8634,6 +10189,12 @@ function PollarProvider({
|
|
|
8634
10189
|
useEffect(() => {
|
|
8635
10190
|
return pollarClient.onTxHistoryStateChange(setTxHistory);
|
|
8636
10191
|
}, [pollarClient]);
|
|
10192
|
+
useEffect(() => {
|
|
10193
|
+
return pollarClient.onCosignStateChange(setCosign);
|
|
10194
|
+
}, [pollarClient]);
|
|
10195
|
+
useEffect(() => {
|
|
10196
|
+
return pollarClient.onConfirmationRequest(setConfirmations);
|
|
10197
|
+
}, [pollarClient]);
|
|
8637
10198
|
useEffect(() => {
|
|
8638
10199
|
return pollarClient.onSessionsStateChange(setSessions);
|
|
8639
10200
|
}, [pollarClient]);
|
|
@@ -8734,6 +10295,13 @@ function PollarProvider({
|
|
|
8734
10295
|
const [cardModalKey, setCardModalKey] = useState(0);
|
|
8735
10296
|
const [receiveModalOpen, setReceiveModalOpen] = useState(false);
|
|
8736
10297
|
const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
|
|
10298
|
+
const [cosignModalOpen, setCosignModalOpen] = useState(false);
|
|
10299
|
+
const [recoveryModalOpen, setRecoveryModalOpen] = useState(false);
|
|
10300
|
+
const cosignPendingCount = useRef(0);
|
|
10301
|
+
useEffect(() => {
|
|
10302
|
+
if (cosign.pending.length > cosignPendingCount.current) setCosignModalOpen(true);
|
|
10303
|
+
cosignPendingCount.current = cosign.pending.length;
|
|
10304
|
+
}, [cosign.pending.length]);
|
|
8737
10305
|
const [distributionRulesModalOpen, setDistributionRulesModalOpen] = useState(false);
|
|
8738
10306
|
useEffect(
|
|
8739
10307
|
() => pollarClient.onAuthStateChange((authState) => {
|
|
@@ -8750,6 +10318,8 @@ function PollarProvider({
|
|
|
8750
10318
|
setCardModalOpen(false);
|
|
8751
10319
|
setReceiveModalOpen(false);
|
|
8752
10320
|
setSessionsModalOpen(false);
|
|
10321
|
+
setCosignModalOpen(false);
|
|
10322
|
+
setRecoveryModalOpen(false);
|
|
8753
10323
|
setDistributionRulesModalOpen(false);
|
|
8754
10324
|
}),
|
|
8755
10325
|
[pollarClient]
|
|
@@ -8758,6 +10328,45 @@ function PollarProvider({
|
|
|
8758
10328
|
const walletProvisioning = sessionState?.wallet?.provisioning;
|
|
8759
10329
|
const hasSession = sessionState !== null;
|
|
8760
10330
|
const getClient = useCallback(() => pollarClient, [pollarClient]);
|
|
10331
|
+
const [recovery, setRecovery] = useState(null);
|
|
10332
|
+
const [recoveryTick, setRecoveryTick] = useState(0);
|
|
10333
|
+
const refreshRecovery = useCallback(() => setRecoveryTick((t) => t + 1), []);
|
|
10334
|
+
useEffect(() => {
|
|
10335
|
+
if (!hasSession) {
|
|
10336
|
+
setRecovery(null);
|
|
10337
|
+
return;
|
|
10338
|
+
}
|
|
10339
|
+
let cancelled = false;
|
|
10340
|
+
pollarClient.cosign.recovery.status().then((next) => {
|
|
10341
|
+
if (!cancelled) setRecovery(next);
|
|
10342
|
+
}).catch(() => {
|
|
10343
|
+
if (!cancelled) setRecovery(null);
|
|
10344
|
+
});
|
|
10345
|
+
return () => {
|
|
10346
|
+
cancelled = true;
|
|
10347
|
+
};
|
|
10348
|
+
}, [pollarClient, hasSession, walletAddress, recoveryTick]);
|
|
10349
|
+
const recoveryLocked = !!recovery && recovery.required && !recovery.configured;
|
|
10350
|
+
const [custodyMigration, setCustodyMigration] = useState(null);
|
|
10351
|
+
const [custodyMigrationTick, setCustodyMigrationTick] = useState(0);
|
|
10352
|
+
const refreshCustodyMigration = useCallback(() => setCustodyMigrationTick((t) => t + 1), []);
|
|
10353
|
+
const [custodyMigrationLater, setCustodyMigrationLater] = useState(false);
|
|
10354
|
+
useEffect(() => {
|
|
10355
|
+
if (!hasSession) {
|
|
10356
|
+
setCustodyMigration(null);
|
|
10357
|
+
setCustodyMigrationLater(false);
|
|
10358
|
+
return;
|
|
10359
|
+
}
|
|
10360
|
+
let cancelled = false;
|
|
10361
|
+
pollarClient.cosign.custodyMigration().then((next) => {
|
|
10362
|
+
if (!cancelled) setCustodyMigration(next);
|
|
10363
|
+
}).catch(() => {
|
|
10364
|
+
if (!cancelled) setCustodyMigration(null);
|
|
10365
|
+
});
|
|
10366
|
+
return () => {
|
|
10367
|
+
cancelled = true;
|
|
10368
|
+
};
|
|
10369
|
+
}, [pollarClient, hasSession, walletAddress, custodyMigrationTick]);
|
|
8761
10370
|
const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
|
|
8762
10371
|
const refreshAssets = useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
|
|
8763
10372
|
const contextValue = useMemo(() => {
|
|
@@ -8768,7 +10377,7 @@ function PollarProvider({
|
|
|
8768
10377
|
wallets: pollarClient.getWallets(),
|
|
8769
10378
|
isAuthenticated: hasSession,
|
|
8770
10379
|
hasWallet: pollarClient.hasWallet(),
|
|
8771
|
-
createWallet: () => pollarClient.createWallet(),
|
|
10380
|
+
createWallet: (options) => pollarClient.createWallet(options),
|
|
8772
10381
|
verified,
|
|
8773
10382
|
// client
|
|
8774
10383
|
getClient,
|
|
@@ -8834,6 +10443,16 @@ function PollarProvider({
|
|
|
8834
10443
|
// sessions
|
|
8835
10444
|
sessions,
|
|
8836
10445
|
openSessionsModal: () => setSessionsModalOpen(true),
|
|
10446
|
+
// cosign
|
|
10447
|
+
cosign,
|
|
10448
|
+
openCosignModal: () => setCosignModalOpen(true),
|
|
10449
|
+
recovery,
|
|
10450
|
+
recoveryLocked,
|
|
10451
|
+
refreshRecovery,
|
|
10452
|
+
openRecoveryModal: () => setRecoveryModalOpen(true),
|
|
10453
|
+
custodyMigration,
|
|
10454
|
+
refreshCustodyMigration,
|
|
10455
|
+
confirmations,
|
|
8837
10456
|
// distribution
|
|
8838
10457
|
openDistributionRulesModal: () => setDistributionRulesModalOpen(true),
|
|
8839
10458
|
// network
|
|
@@ -8856,6 +10475,11 @@ function PollarProvider({
|
|
|
8856
10475
|
}, [
|
|
8857
10476
|
walletAddress,
|
|
8858
10477
|
walletProvisioning,
|
|
10478
|
+
recovery,
|
|
10479
|
+
recoveryLocked,
|
|
10480
|
+
refreshRecovery,
|
|
10481
|
+
custodyMigration,
|
|
10482
|
+
refreshCustodyMigration,
|
|
8859
10483
|
hasSession,
|
|
8860
10484
|
verified,
|
|
8861
10485
|
pollarClient,
|
|
@@ -8863,6 +10487,8 @@ function PollarProvider({
|
|
|
8863
10487
|
transaction,
|
|
8864
10488
|
txHistory,
|
|
8865
10489
|
sessions,
|
|
10490
|
+
cosign,
|
|
10491
|
+
confirmations,
|
|
8866
10492
|
walletBalance,
|
|
8867
10493
|
refreshWalletBalance,
|
|
8868
10494
|
enabledAssets,
|
|
@@ -8910,6 +10536,25 @@ function PollarProvider({
|
|
|
8910
10536
|
/* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCardModalOpen(false), children: /* @__PURE__ */ jsx(CardModal, { onClose: () => setCardModalOpen(false) }) }, `${cardModalKey}:${walletAddress}`),
|
|
8911
10537
|
receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
|
|
8912
10538
|
sessionsModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) }),
|
|
10539
|
+
cosignModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCosignModalOpen(false), children: /* @__PURE__ */ jsx(CosignModal, { onClose: () => setCosignModalOpen(false) }) }),
|
|
10540
|
+
custodyMigration?.required && !custodyMigrationLater && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: refreshCustodyMigration, children: /* @__PURE__ */ jsx(
|
|
10541
|
+
CustodyMigrationModal,
|
|
10542
|
+
{
|
|
10543
|
+
state: custodyMigration,
|
|
10544
|
+
onMoved: () => {
|
|
10545
|
+
refreshCustodyMigration();
|
|
10546
|
+
refreshRecovery();
|
|
10547
|
+
setRecoveryModalOpen(true);
|
|
10548
|
+
},
|
|
10549
|
+
onLater: () => {
|
|
10550
|
+
setCustodyMigrationLater(true);
|
|
10551
|
+
refreshCustodyMigration();
|
|
10552
|
+
refreshRecovery();
|
|
10553
|
+
}
|
|
10554
|
+
}
|
|
10555
|
+
) }),
|
|
10556
|
+
recoveryModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setRecoveryModalOpen(false), children: /* @__PURE__ */ jsx(RecoveryModal, { onClose: () => setRecoveryModalOpen(false) }) }),
|
|
10557
|
+
firstConfirmation && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: firstConfirmation.reject, children: /* @__PURE__ */ jsx(ConfirmModal, { confirmation: firstConfirmation }, firstConfirmation.id) }),
|
|
8913
10558
|
distributionRulesModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setDistributionRulesModalOpen(false), children: /* @__PURE__ */ jsx(DistributionRulesModal, { onClose: () => setDistributionRulesModalOpen(false) }) })
|
|
8914
10559
|
] });
|
|
8915
10560
|
}
|
|
@@ -8959,6 +10604,8 @@ function WalletButtonTemplate({
|
|
|
8959
10604
|
isInProgress,
|
|
8960
10605
|
showCreateAccount,
|
|
8961
10606
|
notReadyReason,
|
|
10607
|
+
recoveryLocked,
|
|
10608
|
+
onRecovery,
|
|
8962
10609
|
creatingAccount,
|
|
8963
10610
|
onToggleOpen,
|
|
8964
10611
|
onCreateAccount,
|
|
@@ -8976,8 +10623,11 @@ function WalletButtonTemplate({
|
|
|
8976
10623
|
signedIn,
|
|
8977
10624
|
creatingWallet,
|
|
8978
10625
|
createWalletError,
|
|
10626
|
+
custodyChoice,
|
|
8979
10627
|
onCreateWallet
|
|
8980
10628
|
}) {
|
|
10629
|
+
const [selfCustodyNotice, setSelfCustodyNotice] = useState(false);
|
|
10630
|
+
const [risksAccepted, setRisksAccepted] = useState(false);
|
|
8981
10631
|
if (!walletAddress && signedIn) {
|
|
8982
10632
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, children: [
|
|
8983
10633
|
/* @__PURE__ */ jsxs("button", { className: "pollar-wallet-btn", style: { backgroundColor: accentColor }, onClick: onToggleOpen, children: [
|
|
@@ -9001,19 +10651,84 @@ function WalletButtonTemplate({
|
|
|
9001
10651
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: createWalletError }),
|
|
9002
10652
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
9003
10653
|
] }),
|
|
9004
|
-
onCreateWallet && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10654
|
+
onCreateWallet && !custodyChoice && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9005
10655
|
/* @__PURE__ */ jsx(
|
|
9006
10656
|
"button",
|
|
9007
10657
|
{
|
|
9008
10658
|
className: "pollar-wallet-dropdown-item",
|
|
9009
10659
|
style: { color: itemColor },
|
|
9010
|
-
onClick: onCreateWallet,
|
|
10660
|
+
onClick: () => onCreateWallet(),
|
|
9011
10661
|
disabled: creatingWallet,
|
|
9012
10662
|
children: creatingWallet ? "Creating wallet\u2026" : "Create wallet"
|
|
9013
10663
|
}
|
|
9014
10664
|
),
|
|
9015
10665
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
9016
10666
|
] }),
|
|
10667
|
+
onCreateWallet && custodyChoice && !selfCustodyNotice && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10668
|
+
/* @__PURE__ */ jsx(
|
|
10669
|
+
"button",
|
|
10670
|
+
{
|
|
10671
|
+
className: "pollar-wallet-dropdown-item",
|
|
10672
|
+
style: { color: itemColor },
|
|
10673
|
+
onClick: () => onCreateWallet("COSIGNED"),
|
|
10674
|
+
disabled: creatingWallet,
|
|
10675
|
+
children: creatingWallet ? "Creating wallet\u2026" : "Create co-signed wallet"
|
|
10676
|
+
}
|
|
10677
|
+
),
|
|
10678
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: "You and the app approve every signature together. Your wallet can be recovered with help." }),
|
|
10679
|
+
/* @__PURE__ */ jsx(
|
|
10680
|
+
"button",
|
|
10681
|
+
{
|
|
10682
|
+
className: "pollar-wallet-dropdown-item",
|
|
10683
|
+
style: { color: itemColor },
|
|
10684
|
+
onClick: () => setSelfCustodyNotice(true),
|
|
10685
|
+
disabled: creatingWallet,
|
|
10686
|
+
children: "Create self-custody wallet"
|
|
10687
|
+
}
|
|
10688
|
+
),
|
|
10689
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: "Only your device signs. Nobody else can move or recover your funds." }),
|
|
10690
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
10691
|
+
] }),
|
|
10692
|
+
onCreateWallet && custodyChoice && selfCustodyNotice && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10693
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "alert", children: "In a self-custody wallet only your device can sign. If you lose this device and every recovery method, nobody can recover the wallet or its funds, not the app and not Pollar. Nobody can undo or freeze a transaction you sign." }),
|
|
10694
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: [
|
|
10695
|
+
/* @__PURE__ */ jsx(
|
|
10696
|
+
"input",
|
|
10697
|
+
{
|
|
10698
|
+
type: "checkbox",
|
|
10699
|
+
checked: risksAccepted,
|
|
10700
|
+
onChange: (e) => setRisksAccepted(e.target.checked),
|
|
10701
|
+
disabled: creatingWallet
|
|
10702
|
+
}
|
|
10703
|
+
),
|
|
10704
|
+
" ",
|
|
10705
|
+
"I understand and accept these risks"
|
|
10706
|
+
] }),
|
|
10707
|
+
/* @__PURE__ */ jsx(
|
|
10708
|
+
"button",
|
|
10709
|
+
{
|
|
10710
|
+
className: "pollar-wallet-dropdown-item",
|
|
10711
|
+
style: { color: itemColor },
|
|
10712
|
+
onClick: () => onCreateWallet("SELF_CUSTODY"),
|
|
10713
|
+
disabled: creatingWallet || !risksAccepted,
|
|
10714
|
+
children: creatingWallet ? "Creating wallet\u2026" : "Create self-custody wallet"
|
|
10715
|
+
}
|
|
10716
|
+
),
|
|
10717
|
+
/* @__PURE__ */ jsx(
|
|
10718
|
+
"button",
|
|
10719
|
+
{
|
|
10720
|
+
className: "pollar-wallet-dropdown-item",
|
|
10721
|
+
style: { color: itemColor },
|
|
10722
|
+
onClick: () => {
|
|
10723
|
+
setSelfCustodyNotice(false);
|
|
10724
|
+
setRisksAccepted(false);
|
|
10725
|
+
},
|
|
10726
|
+
disabled: creatingWallet,
|
|
10727
|
+
children: "Back"
|
|
10728
|
+
}
|
|
10729
|
+
),
|
|
10730
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
10731
|
+
] }),
|
|
9017
10732
|
/* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSessions, children: "Active sessions" }),
|
|
9018
10733
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
|
|
9019
10734
|
/* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: "Log out" })
|
|
@@ -9069,6 +10784,10 @@ function WalletButtonTemplate({
|
|
|
9069
10784
|
}
|
|
9070
10785
|
),
|
|
9071
10786
|
open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
|
|
10787
|
+
recoveryLocked && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10788
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice pollar-danger", role: "alert", children: "Locked: add a way to recover this wallet before sending or receiving. If you lose this device without one, the funds are lost." }),
|
|
10789
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
10790
|
+
] }),
|
|
9072
10791
|
notReadyReason && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9073
10792
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: notReadyReason }),
|
|
9074
10793
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
@@ -9301,6 +11020,31 @@ function WalletButtonTemplate({
|
|
|
9301
11020
|
),
|
|
9302
11021
|
"Active sessions"
|
|
9303
11022
|
] }),
|
|
11023
|
+
onRecovery && /* @__PURE__ */ jsxs(
|
|
11024
|
+
"button",
|
|
11025
|
+
{
|
|
11026
|
+
className: `pollar-wallet-dropdown-item${recoveryLocked ? " pollar-danger" : ""}`,
|
|
11027
|
+
style: recoveryLocked ? void 0 : { color: itemColor },
|
|
11028
|
+
onClick: onRecovery,
|
|
11029
|
+
children: [
|
|
11030
|
+
/* @__PURE__ */ jsx(
|
|
11031
|
+
"svg",
|
|
11032
|
+
{
|
|
11033
|
+
width: "14",
|
|
11034
|
+
height: "14",
|
|
11035
|
+
viewBox: "0 0 24 24",
|
|
11036
|
+
fill: "none",
|
|
11037
|
+
stroke: "currentColor",
|
|
11038
|
+
strokeWidth: "2",
|
|
11039
|
+
strokeLinecap: "round",
|
|
11040
|
+
strokeLinejoin: "round",
|
|
11041
|
+
children: /* @__PURE__ */ jsx("path", { d: "M12 2 4 5v6c0 5 3.4 9.3 8 11 4.6-1.7 8-6 8-11V5l-8-3z" })
|
|
11042
|
+
}
|
|
11043
|
+
),
|
|
11044
|
+
recoveryLocked ? "Set up wallet recovery" : "Wallet recovery"
|
|
11045
|
+
]
|
|
11046
|
+
}
|
|
11047
|
+
),
|
|
9304
11048
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
|
|
9305
11049
|
/* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: [
|
|
9306
11050
|
/* @__PURE__ */ jsxs(
|
|
@@ -9343,7 +11087,10 @@ function WalletButton() {
|
|
|
9343
11087
|
tx: transaction,
|
|
9344
11088
|
isAuthenticated,
|
|
9345
11089
|
createWallet,
|
|
9346
|
-
appConfig
|
|
11090
|
+
appConfig,
|
|
11091
|
+
recovery,
|
|
11092
|
+
recoveryLocked,
|
|
11093
|
+
openRecoveryModal
|
|
9347
11094
|
} = usePollar();
|
|
9348
11095
|
const { primaryChain, primaryAddress } = useChains();
|
|
9349
11096
|
const walletAddress = primaryAddress || (wallet?.address ?? "");
|
|
@@ -9406,12 +11153,12 @@ function WalletButton() {
|
|
|
9406
11153
|
setCreating(false);
|
|
9407
11154
|
}
|
|
9408
11155
|
}
|
|
9409
|
-
async function handleCreateWallet() {
|
|
11156
|
+
async function handleCreateWallet(custody) {
|
|
9410
11157
|
if (creatingWallet) return;
|
|
9411
11158
|
setCreatingWallet(true);
|
|
9412
11159
|
setCreateWalletError(null);
|
|
9413
11160
|
try {
|
|
9414
|
-
await createWallet();
|
|
11161
|
+
await createWallet(custody ? { custody } : void 0);
|
|
9415
11162
|
setOpen(false);
|
|
9416
11163
|
} catch (err) {
|
|
9417
11164
|
setCreateWalletError(
|
|
@@ -9441,6 +11188,10 @@ function WalletButton() {
|
|
|
9441
11188
|
setOpen(false);
|
|
9442
11189
|
openSessionsModal();
|
|
9443
11190
|
}
|
|
11191
|
+
function handleRecovery() {
|
|
11192
|
+
setOpen(false);
|
|
11193
|
+
openRecoveryModal();
|
|
11194
|
+
}
|
|
9444
11195
|
function handleKyc() {
|
|
9445
11196
|
setOpen(false);
|
|
9446
11197
|
openKycModal();
|
|
@@ -9469,6 +11220,7 @@ function WalletButton() {
|
|
|
9469
11220
|
signedIn: isAuthenticated,
|
|
9470
11221
|
creatingWallet,
|
|
9471
11222
|
createWalletError,
|
|
11223
|
+
custodyChoice: appConfig.application?.walletCustodyMode === "USER_CHOICE",
|
|
9472
11224
|
...appConfig.application?.walletProvisioning === "NONE" ? {} : { onCreateWallet: handleCreateWallet },
|
|
9473
11225
|
notReadyReason,
|
|
9474
11226
|
creatingAccount: creating,
|
|
@@ -9480,6 +11232,8 @@ function WalletButton() {
|
|
|
9480
11232
|
onSend: handleSend,
|
|
9481
11233
|
onReceive: handleReceive,
|
|
9482
11234
|
onSessions: handleSessions,
|
|
11235
|
+
recoveryLocked,
|
|
11236
|
+
...recovery?.required ? { onRecovery: handleRecovery } : {},
|
|
9483
11237
|
onKyc: handleKyc,
|
|
9484
11238
|
onRamp: handleRamp,
|
|
9485
11239
|
onDistributionRules: handleDistributionRules,
|
|
@@ -9489,6 +11243,6 @@ function WalletButton() {
|
|
|
9489
11243
|
);
|
|
9490
11244
|
}
|
|
9491
11245
|
|
|
9492
|
-
export { CardModal, ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, usePollar, walletNotReadyReason };
|
|
11246
|
+
export { CardModal, ChainSelect, CosignModal, CosignModalTemplate, CustodyMigrationModal, CustodyMigrationModalTemplate, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, RampWorkflow, ReceiveModal, ReceiveModalTemplate, RecoveryModal, RecoveryModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, browserRecoveryPasskey, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, useCosign, usePollar, walletNotReadyReason };
|
|
9493
11247
|
//# sourceMappingURL=index.mjs.map
|
|
9494
11248
|
//# sourceMappingURL=index.mjs.map
|