@pollar/react 0.11.5 → 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 +1 -1
- package/dist/index.css +203 -11
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +165 -10
- package/dist/index.d.ts +165 -10
- package/dist/index.js +1390 -165
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1386 -167
- 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,
|
|
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';
|
|
@@ -1036,7 +1036,7 @@ var PollarModalFooter = () => {
|
|
|
1036
1036
|
/* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_DARK, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-dark" }),
|
|
1037
1037
|
/* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
|
|
1038
1038
|
"v",
|
|
1039
|
-
"0.
|
|
1039
|
+
"0.12.0-rc.2"
|
|
1040
1040
|
] })
|
|
1041
1041
|
] });
|
|
1042
1042
|
};
|
|
@@ -1913,7 +1913,7 @@ function KycStatus({ status, label, className }) {
|
|
|
1913
1913
|
|
|
1914
1914
|
// src/components/kyc-modal/kyc-messages.ts
|
|
1915
1915
|
function kycErrorMessage(error, phase) {
|
|
1916
|
-
const code = error
|
|
1916
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
1917
1917
|
switch (code) {
|
|
1918
1918
|
case "SDK_KYC_NOT_CONFIGURED":
|
|
1919
1919
|
return "Identity verification is not available for this app yet.";
|
|
@@ -2158,7 +2158,13 @@ function KycModal({
|
|
|
2158
2158
|
finish("approved");
|
|
2159
2159
|
return;
|
|
2160
2160
|
}
|
|
2161
|
-
|
|
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
|
+
});
|
|
2162
2168
|
setStep("verifying");
|
|
2163
2169
|
} catch (e) {
|
|
2164
2170
|
const code = isPollarApiError(e) ? e.code : void 0;
|
|
@@ -2345,10 +2351,14 @@ function fieldErrorMessage(code, language) {
|
|
|
2345
2351
|
}
|
|
2346
2352
|
function fieldErrorsOf(error) {
|
|
2347
2353
|
if (!error || typeof error !== "object") return null;
|
|
2348
|
-
|
|
2349
|
-
|
|
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;
|
|
2350
2359
|
const errors = {};
|
|
2351
|
-
for (const item of
|
|
2360
|
+
for (const item of items) {
|
|
2361
|
+
if (typeof item !== "object" || item === null || !("key" in item) || !("code" in item)) continue;
|
|
2352
2362
|
if (typeof item.key === "string" && typeof item.code === "string") errors[item.key] = item.code;
|
|
2353
2363
|
}
|
|
2354
2364
|
return Object.keys(errors).length ? errors : null;
|
|
@@ -3398,13 +3408,200 @@ function LoginModal({ onClose }) {
|
|
|
3398
3408
|
) });
|
|
3399
3409
|
}
|
|
3400
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
|
+
|
|
3401
3593
|
// src/components/ramp-widget/ramp-kyc.ts
|
|
3402
3594
|
var nonEmpty = (value) => typeof value === "string" && !!value.trim();
|
|
3403
3595
|
function requiredRampKyc(error) {
|
|
3404
3596
|
if (!error || typeof error !== "object") return null;
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
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;
|
|
3408
3605
|
if (!nonEmpty(rampProviderId) || !nonEmpty(corridorId)) return null;
|
|
3409
3606
|
if ((requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") && nonEmpty(optionId)) {
|
|
3410
3607
|
return { rampProviderId, corridorId, type: requirementType, optionId };
|
|
@@ -3471,13 +3668,14 @@ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
|
|
|
3471
3668
|
children: [
|
|
3472
3669
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
|
|
3473
3670
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
|
|
3474
|
-
/* @__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 ") })
|
|
3475
3676
|
] }),
|
|
3476
3677
|
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: busy ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3477
|
-
/* @__PURE__ */
|
|
3478
|
-
quote.fee,
|
|
3479
|
-
"% fee"
|
|
3480
|
-
] }),
|
|
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` }),
|
|
3481
3679
|
quote.recommended && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
|
|
3482
3680
|
] }) })
|
|
3483
3681
|
]
|
|
@@ -3557,6 +3755,8 @@ function displayValue(field) {
|
|
|
3557
3755
|
return Number.isNaN(at.getTime()) ? field.value : at.toLocaleString();
|
|
3558
3756
|
}
|
|
3559
3757
|
function RampWidgetTemplate({
|
|
3758
|
+
workflowContent,
|
|
3759
|
+
routeSelector,
|
|
3560
3760
|
theme,
|
|
3561
3761
|
accentColor,
|
|
3562
3762
|
styleOverrides,
|
|
@@ -3593,6 +3793,11 @@ function RampWidgetTemplate({
|
|
|
3593
3793
|
depositInstructions,
|
|
3594
3794
|
canComplete,
|
|
3595
3795
|
completing,
|
|
3796
|
+
savedRamps = [],
|
|
3797
|
+
onResumeRamp,
|
|
3798
|
+
onDismissRamp,
|
|
3799
|
+
onDismissCurrentRamp,
|
|
3800
|
+
onExploreRoutes,
|
|
3596
3801
|
errorMsg,
|
|
3597
3802
|
noticeMsg,
|
|
3598
3803
|
onDirectionChange,
|
|
@@ -3631,7 +3836,7 @@ function RampWidgetTemplate({
|
|
|
3631
3836
|
// money, not who you are. Say which, rather than asserting the Bridge case
|
|
3632
3837
|
// over every provider.
|
|
3633
3838
|
contact: `${provider || "This provider"} needs ${listOf(requiredFields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
|
|
3634
|
-
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`,
|
|
3635
3840
|
error: "Please try again"
|
|
3636
3841
|
};
|
|
3637
3842
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
|
|
@@ -3685,7 +3890,39 @@ function RampWidgetTemplate({
|
|
|
3685
3890
|
}
|
|
3686
3891
|
)
|
|
3687
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
|
+
] }),
|
|
3688
3924
|
step === "input" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3925
|
+
noticeMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", role: "status", children: noticeMsg }),
|
|
3689
3926
|
/* @__PURE__ */ jsxs("div", { className: "pollar-tabs", children: [
|
|
3690
3927
|
/* @__PURE__ */ jsx(
|
|
3691
3928
|
"button",
|
|
@@ -3709,25 +3946,36 @@ function RampWidgetTemplate({
|
|
|
3709
3946
|
)
|
|
3710
3947
|
] }),
|
|
3711
3948
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
|
|
3712
|
-
/* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", children: "Country" }),
|
|
3949
|
+
/* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-country", children: "Country" }),
|
|
3713
3950
|
countriesLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-input pollar-select-loading", children: [
|
|
3714
3951
|
/* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
|
|
3715
3952
|
/* @__PURE__ */ jsx("span", { children: "Loading countries\u2026" })
|
|
3716
|
-
] }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsx(
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
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
|
+
)
|
|
3722
3968
|
] }),
|
|
3969
|
+
routeSelector,
|
|
3723
3970
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
|
|
3724
|
-
/* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", children: [
|
|
3971
|
+
/* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-amount", children: [
|
|
3725
3972
|
"Amount",
|
|
3726
3973
|
currency ? ` (${currency})` : ""
|
|
3727
3974
|
] }),
|
|
3728
3975
|
/* @__PURE__ */ jsx(
|
|
3729
3976
|
"input",
|
|
3730
3977
|
{
|
|
3978
|
+
id: "pollar-ramp-amount",
|
|
3731
3979
|
type: "number",
|
|
3732
3980
|
className: "pollar-ramp-input",
|
|
3733
3981
|
placeholder: "0.00",
|
|
@@ -3833,7 +4081,16 @@ function RampWidgetTemplate({
|
|
|
3833
4081
|
)
|
|
3834
4082
|
] })
|
|
3835
4083
|
] }),
|
|
3836
|
-
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: [
|
|
3837
4094
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
3838
4095
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Provider" }),
|
|
3839
4096
|
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsx("code", { children: provider }) })
|
|
@@ -3965,7 +4222,7 @@ function RampWidgetTemplate({
|
|
|
3965
4222
|
canComplete && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3966
4223
|
/* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
|
|
3967
4224
|
"Submitting\u2026"
|
|
3968
|
-
] }) : "
|
|
4225
|
+
] }) : "Check verification and continue" }),
|
|
3969
4226
|
errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
|
|
3970
4227
|
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: txStatus === "completed" ? "Done" : "Close" })
|
|
3971
4228
|
] }),
|
|
@@ -4131,19 +4388,26 @@ var SHARED_FIELD_LABELS = {
|
|
|
4131
4388
|
country_code: "Pa\xEDs de residencia"
|
|
4132
4389
|
}
|
|
4133
4390
|
};
|
|
4134
|
-
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
|
+
};
|
|
4135
4396
|
function missingFieldsOf(error) {
|
|
4136
|
-
const body = error
|
|
4137
|
-
|
|
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") : [];
|
|
4138
4400
|
}
|
|
4139
4401
|
function registrationFixOf(error) {
|
|
4140
|
-
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;
|
|
4141
4405
|
const strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
4142
|
-
const forms = Array.isArray(
|
|
4143
|
-
|
|
4144
|
-
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) }];
|
|
4145
4409
|
}) : [];
|
|
4146
|
-
return { invalid: strings(
|
|
4410
|
+
return { invalid: strings(invalid), forms };
|
|
4147
4411
|
}
|
|
4148
4412
|
function RegistryCheckModal({ optionId, progress, onClose, onApproved }) {
|
|
4149
4413
|
const { getClient, styles } = usePollar();
|
|
@@ -4463,9 +4727,51 @@ function ProviderRegistrationModal({
|
|
|
4463
4727
|
}
|
|
4464
4728
|
) });
|
|
4465
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
|
+
}
|
|
4466
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
|
+
}
|
|
4467
4773
|
function requiredFieldsOf(quote) {
|
|
4468
|
-
return quote.requiredFields
|
|
4774
|
+
return "requiredFields" in quote ? quote.requiredFields : [];
|
|
4469
4775
|
}
|
|
4470
4776
|
function flowStepsOf(quotes, selected) {
|
|
4471
4777
|
const needsDetails = selected != null ? requiredFieldsOf(selected).length > 0 : quotes.some((q) => requiredFieldsOf(q).length > 0);
|
|
@@ -4479,7 +4785,8 @@ var STEP_LABEL = {
|
|
|
4479
4785
|
status: "Complete"
|
|
4480
4786
|
};
|
|
4481
4787
|
function brokenLimitOf(amount, quote) {
|
|
4482
|
-
const
|
|
4788
|
+
const minAmount = "minAmount" in quote ? quote.minAmount : void 0;
|
|
4789
|
+
const maxAmount = "maxAmount" in quote ? quote.maxAmount : void 0;
|
|
4483
4790
|
if (!Number.isFinite(amount)) return null;
|
|
4484
4791
|
if (minAmount != null && amount < minAmount) return { limit: "min", value: minAmount };
|
|
4485
4792
|
if (maxAmount != null && amount > maxAmount) return { limit: "max", value: maxAmount };
|
|
@@ -4503,17 +4810,49 @@ var RAMP_ERROR_MESSAGES = {
|
|
|
4503
4810
|
SDK_RAMPS_ONCHAIN_SUBMIT_FAILED: "The network rejected the transaction, so nothing was sent. The wallet may need XLM for the fee."
|
|
4504
4811
|
};
|
|
4505
4812
|
function rampErrorMessage(e, fallback) {
|
|
4506
|
-
const
|
|
4507
|
-
const code = e?.code;
|
|
4813
|
+
const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
|
|
4508
4814
|
if (typeof code !== "string") return e instanceof Error ? e.message : fallback;
|
|
4509
|
-
const
|
|
4510
|
-
const
|
|
4511
|
-
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;
|
|
4512
4820
|
if ((limit === "min" || limit === "max") && typeof value === "number" && typeof currency === "string") {
|
|
4513
4821
|
return limitMessage(limit, value, currency);
|
|
4514
4822
|
}
|
|
4515
4823
|
return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
|
|
4516
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
|
+
}
|
|
4517
4856
|
function explorerTxUrl2(chain, hash, network) {
|
|
4518
4857
|
if (!hash) return null;
|
|
4519
4858
|
const testnet = network === "testnet";
|
|
@@ -4522,7 +4861,7 @@ function explorerTxUrl2(chain, hash, network) {
|
|
|
4522
4861
|
return `https://stellar.expert/explorer/${testnet ? "testnet" : "public"}/tx/${hash}`;
|
|
4523
4862
|
}
|
|
4524
4863
|
function RampWidget({ onClose }) {
|
|
4525
|
-
const { getClient,
|
|
4864
|
+
const { getClient, wallet, styles, network } = usePollar();
|
|
4526
4865
|
const walletAddress = wallet?.address ?? "";
|
|
4527
4866
|
const client = getClient();
|
|
4528
4867
|
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
@@ -4533,7 +4872,7 @@ function RampWidget({ onClose }) {
|
|
|
4533
4872
|
const [country, setCountry] = useState("");
|
|
4534
4873
|
const [fieldValues, setFieldValues] = useState({});
|
|
4535
4874
|
const setFieldValue = (key, value) => setFieldValues((v) => ({ ...v, [key]: value }));
|
|
4536
|
-
const [
|
|
4875
|
+
const [legacyCountries, setCountries] = useState([]);
|
|
4537
4876
|
const [countriesLoading, setCountriesLoading] = useState(true);
|
|
4538
4877
|
const [refreshing, setRefreshing] = useState(false);
|
|
4539
4878
|
const [quotes, setQuotes] = useState([]);
|
|
@@ -4563,9 +4902,88 @@ function RampWidget({ onClose }) {
|
|
|
4563
4902
|
const [fundsInTransit, setFundsInTransit] = useState(false);
|
|
4564
4903
|
const [retrying, setRetrying] = useState(false);
|
|
4565
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]);
|
|
4566
4947
|
const [completing, setCompleting] = useState(false);
|
|
4948
|
+
const completionLocked = useRef(false);
|
|
4567
4949
|
const [errorMsg, setErrorMsg] = useState(null);
|
|
4568
|
-
|
|
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
|
+
}
|
|
4569
4987
|
const directionRef = useRef(direction);
|
|
4570
4988
|
directionRef.current = direction;
|
|
4571
4989
|
function applySettlement(tx) {
|
|
@@ -4588,23 +5006,20 @@ function RampWidget({ onClose }) {
|
|
|
4588
5006
|
setRetrying(false);
|
|
4589
5007
|
}
|
|
4590
5008
|
}
|
|
5009
|
+
const workflowTerminal = !!workflow && ["completed", "failed", "refunded"].includes(workflow.lifecycleState ?? workflow.status);
|
|
4591
5010
|
useEffect(() => {
|
|
4592
5011
|
if (step !== "status" || !txId) return;
|
|
4593
|
-
if (txStatus && TERMINAL.includes(txStatus)) return;
|
|
5012
|
+
if (workflowTerminal || txStatus && TERMINAL.includes(txStatus)) return;
|
|
4594
5013
|
let active = true;
|
|
4595
5014
|
const id = setInterval(async () => {
|
|
4596
5015
|
try {
|
|
4597
5016
|
const tx = await client.getRampTransaction(txId);
|
|
4598
5017
|
if (!active) return;
|
|
5018
|
+
if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
|
|
4599
5019
|
setTxStatus(tx.status);
|
|
4600
5020
|
applySettlement(tx);
|
|
4601
|
-
const pending = tx.pendingSignature;
|
|
4602
|
-
if (pending
|
|
4603
|
-
signingRef.current = true;
|
|
4604
|
-
void resumeRef.current(txId, pending).finally(() => {
|
|
4605
|
-
signingRef.current = false;
|
|
4606
|
-
});
|
|
4607
|
-
}
|
|
5021
|
+
const pending = "pendingSignature" in tx ? tx.pendingSignature : void 0;
|
|
5022
|
+
if (pending) setLegacySignature(pending);
|
|
4608
5023
|
if (tx.kycUrl) setKycUrl(tx.kycUrl);
|
|
4609
5024
|
if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
|
|
4610
5025
|
if (TERMINAL.includes(tx.status)) clearInterval(id);
|
|
@@ -4615,9 +5030,9 @@ function RampWidget({ onClose }) {
|
|
|
4615
5030
|
active = false;
|
|
4616
5031
|
clearInterval(id);
|
|
4617
5032
|
};
|
|
4618
|
-
}, [step, txId, txStatus, client]);
|
|
5033
|
+
}, [step, txId, txStatus, workflowTerminal, client]);
|
|
4619
5034
|
useEffect(() => {
|
|
4620
|
-
if (step !== "status" || !kycPending || kycApproved) return;
|
|
5035
|
+
if (workflow || step !== "status" || !kycPending || kycApproved || provider.toLowerCase() !== "abroad") return;
|
|
4621
5036
|
if (onboardingStatus === "awaiting_provider") return;
|
|
4622
5037
|
let active = true;
|
|
4623
5038
|
const check = async () => {
|
|
@@ -4633,14 +5048,15 @@ function RampWidget({ onClose }) {
|
|
|
4633
5048
|
active = false;
|
|
4634
5049
|
clearInterval(id);
|
|
4635
5050
|
};
|
|
4636
|
-
}, [step, kycPending, kycApproved, onboardingStatus, client]);
|
|
5051
|
+
}, [step, kycPending, kycApproved, onboardingStatus, workflow, client, provider]);
|
|
4637
5052
|
async function loadCountries(resetSelection) {
|
|
4638
5053
|
setCountriesLoading(true);
|
|
4639
5054
|
try {
|
|
4640
5055
|
const { countries: list } = await client.getRampCountries();
|
|
4641
5056
|
setCountries(list);
|
|
4642
|
-
const
|
|
4643
|
-
const
|
|
5057
|
+
const available = mergeRampCountries(list, routes);
|
|
5058
|
+
const first = available[0];
|
|
5059
|
+
const stillValid = available.some((c) => c.code === country);
|
|
4644
5060
|
if (first && (resetSelection || !stillValid)) {
|
|
4645
5061
|
setCountry(first.code);
|
|
4646
5062
|
if (first.currency) setCurrency(first.currency);
|
|
@@ -4665,11 +5081,22 @@ function RampWidget({ onClose }) {
|
|
|
4665
5081
|
try {
|
|
4666
5082
|
await loadCountries(false);
|
|
4667
5083
|
if (step === "select_route") {
|
|
4668
|
-
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
|
+
);
|
|
4669
5095
|
setQuotes(result.quotes ?? []);
|
|
4670
5096
|
setRequirementsRequired(result.requirementsRequired ?? []);
|
|
4671
5097
|
} else if (step === "status" && txId) {
|
|
4672
5098
|
const tx = await client.getRampTransaction(txId);
|
|
5099
|
+
if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
|
|
4673
5100
|
setTxStatus(tx.status);
|
|
4674
5101
|
applySettlement(tx);
|
|
4675
5102
|
if (tx.kycUrl) setKycUrl(tx.kycUrl);
|
|
@@ -4681,11 +5108,16 @@ function RampWidget({ onClose }) {
|
|
|
4681
5108
|
}
|
|
4682
5109
|
}
|
|
4683
5110
|
function resetToInput({ keepMessage = false } = {}) {
|
|
5111
|
+
writeRampStorage(storageKey, null);
|
|
5112
|
+
writeRampStorage(`${storageKey}:selection`, "1");
|
|
4684
5113
|
setStep("input");
|
|
4685
5114
|
setQuotes([]);
|
|
4686
5115
|
setRequirementsRequired([]);
|
|
4687
5116
|
setSelectedQuote(null);
|
|
4688
5117
|
setFieldValues({});
|
|
5118
|
+
setWorkflow(null);
|
|
5119
|
+
workflowRef.current = null;
|
|
5120
|
+
setLegacySignature(void 0);
|
|
4689
5121
|
setTxId(null);
|
|
4690
5122
|
setProvider("");
|
|
4691
5123
|
setKycUrl(null);
|
|
@@ -4701,8 +5133,20 @@ function RampWidget({ onClose }) {
|
|
|
4701
5133
|
setNoticeMsg(null);
|
|
4702
5134
|
if (!keepMessage) setErrorMsg(null);
|
|
4703
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
|
+
}
|
|
4704
5148
|
async function resumeWithSignature(id, ps) {
|
|
4705
|
-
const outcome = await signTx(ps.unsignedXdr);
|
|
5149
|
+
const outcome = await client.signTx(ps.unsignedXdr, { skipSponsorship: ps.action === "sep10" });
|
|
4706
5150
|
if (outcome.status === "cosign_pending") {
|
|
4707
5151
|
setErrorMsg("Approve the signature on your device, then resume.");
|
|
4708
5152
|
setStep("error");
|
|
@@ -4715,20 +5159,15 @@ function RampWidget({ onClose }) {
|
|
|
4715
5159
|
}
|
|
4716
5160
|
const result = await client.submitRampSignature(id, {
|
|
4717
5161
|
signedXdr: outcome.signedXdr,
|
|
4718
|
-
// The CCTP actions are not in the generated schema until it is regenerated.
|
|
4719
5162
|
action: ps.action
|
|
4720
5163
|
});
|
|
4721
5164
|
await applyResult(result);
|
|
4722
5165
|
}
|
|
4723
|
-
const resumeRef = useRef(resumeWithSignature);
|
|
4724
|
-
resumeRef.current = resumeWithSignature;
|
|
4725
5166
|
async function applyResult(result) {
|
|
5167
|
+
if ("transactionVersion" in result && result.transactionVersion !== void 0 && !acceptWorkflow(result)) return;
|
|
4726
5168
|
setTxId(result.txId);
|
|
4727
5169
|
setProvider(result.provider);
|
|
4728
|
-
|
|
4729
|
-
await resumeWithSignature(result.txId, result.pendingSignature);
|
|
4730
|
-
return;
|
|
4731
|
-
}
|
|
5170
|
+
setLegacySignature(result.pendingSignature);
|
|
4732
5171
|
setKycUrl(result.kycUrl ?? null);
|
|
4733
5172
|
setTosUrl(result.tosUrl ?? null);
|
|
4734
5173
|
setOnboardingStatus(result.onboardingStatus);
|
|
@@ -4740,14 +5179,21 @@ function RampWidget({ onClose }) {
|
|
|
4740
5179
|
setStep("status");
|
|
4741
5180
|
}
|
|
4742
5181
|
async function loadQuotes() {
|
|
4743
|
-
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
|
+
);
|
|
4744
5193
|
const list = result.quotes ?? [];
|
|
4745
5194
|
const locked = result.requirementsRequired ?? [];
|
|
4746
5195
|
if (list.length === 0 && locked.length === 0) {
|
|
4747
|
-
|
|
4748
|
-
setErrorMsg(
|
|
4749
|
-
down.length > 0 ? `${down.join(", ")} ${down.length === 1 ? "is" : "are"} temporarily unavailable. Please try again later.` : `No ramp providers available for ${country} yet.`
|
|
4750
|
-
);
|
|
5196
|
+
setErrorMsg(unavailableRampMessage(country, result.unavailable ?? []));
|
|
4751
5197
|
setStep("error");
|
|
4752
5198
|
return false;
|
|
4753
5199
|
}
|
|
@@ -4793,19 +5239,40 @@ function RampWidget({ onClose }) {
|
|
|
4793
5239
|
setIsLoading(true);
|
|
4794
5240
|
setErrorMsg(null);
|
|
4795
5241
|
try {
|
|
4796
|
-
const
|
|
4797
|
-
|
|
4798
|
-
|
|
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
|
+
};
|
|
4799
5250
|
const extraFields = {};
|
|
4800
5251
|
for (const f of requiredFieldsOf(quote)) {
|
|
4801
5252
|
const val = (fieldValues[f.key] ?? "").trim();
|
|
4802
5253
|
if (!val) continue;
|
|
4803
|
-
if (f.bankType)
|
|
4804
|
-
|
|
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;
|
|
4805
5257
|
else extraFields[f.key] = val;
|
|
4806
5258
|
}
|
|
4807
|
-
if (
|
|
4808
|
-
|
|
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);
|
|
4809
5276
|
await applyResult(result);
|
|
4810
5277
|
} catch (e) {
|
|
4811
5278
|
const requirement = requiredRampKyc(e);
|
|
@@ -4855,8 +5322,24 @@ function RampWidget({ onClose }) {
|
|
|
4855
5322
|
function handleOpenTos() {
|
|
4856
5323
|
if (tosUrl) window.open(tosUrl, "_blank", "noopener,noreferrer");
|
|
4857
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
|
+
}
|
|
4858
5340
|
async function handleCompleteWithdraw() {
|
|
4859
|
-
if (!txId) return;
|
|
5341
|
+
if (!txId || completionLocked.current) return;
|
|
5342
|
+
completionLocked.current = true;
|
|
4860
5343
|
setCompleting(true);
|
|
4861
5344
|
setErrorMsg(null);
|
|
4862
5345
|
try {
|
|
@@ -4873,11 +5356,12 @@ function RampWidget({ onClose }) {
|
|
|
4873
5356
|
msg.includes("KYC") ? "Finish KYC at the provider first, then try again." : msg || "Failed to complete the withdrawal."
|
|
4874
5357
|
);
|
|
4875
5358
|
} finally {
|
|
5359
|
+
completionLocked.current = false;
|
|
4876
5360
|
setCompleting(false);
|
|
4877
5361
|
}
|
|
4878
5362
|
}
|
|
4879
5363
|
const kycBlocking = kycPending && !kycApproved;
|
|
4880
|
-
const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !txHash && !kycPending;
|
|
5364
|
+
const canComplete = !workflow && !legacySignature && direction === "offramp" && step === "status" && txStatus !== "completed" && txStatus !== "failed" && !stellarTxHash && !txHash && !kycPending;
|
|
4881
5365
|
const flowSteps = flowStepsOf(quotes, selectedQuote);
|
|
4882
5366
|
const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
|
|
4883
5367
|
if (pendingKyc?.type === "FORM") {
|
|
@@ -4935,6 +5419,20 @@ function RampWidget({ onClose }) {
|
|
|
4935
5419
|
return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
|
|
4936
5420
|
RampWidgetTemplate,
|
|
4937
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,
|
|
4938
5436
|
theme,
|
|
4939
5437
|
accentColor,
|
|
4940
5438
|
styleOverrides,
|
|
@@ -4971,9 +5469,18 @@ function RampWidget({ onClose }) {
|
|
|
4971
5469
|
depositInstructions,
|
|
4972
5470
|
canComplete,
|
|
4973
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,
|
|
4974
5479
|
errorMsg,
|
|
4975
5480
|
noticeMsg,
|
|
4976
|
-
onDirectionChange:
|
|
5481
|
+
onDirectionChange: (next) => {
|
|
5482
|
+
setDirection(next);
|
|
5483
|
+
},
|
|
4977
5484
|
onAmountChange: (next) => {
|
|
4978
5485
|
setAmount(next);
|
|
4979
5486
|
setErrorMsg(null);
|
|
@@ -5006,49 +5513,6 @@ function walletNotReadyReason(wallet, chain) {
|
|
|
5006
5513
|
}
|
|
5007
5514
|
return null;
|
|
5008
5515
|
}
|
|
5009
|
-
|
|
5010
|
-
// src/lib/qr-code/index.tsx
|
|
5011
|
-
var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
|
|
5012
|
-
var import_QRCode = __toESM(require_QRCode());
|
|
5013
|
-
var QRCodeSvg = forwardRef(function QRCodeSvg2({ bgColor, bgD, fgD, fgColor, size, title, viewBoxSize, xmlns = "http://www.w3.org/2000/svg", ...props }, ref) {
|
|
5014
|
-
return /* @__PURE__ */ jsxs("svg", { ...props, height: size, ref, viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, width: size, xmlns, children: [
|
|
5015
|
-
title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
|
|
5016
|
-
/* @__PURE__ */ jsx("path", { d: bgD, fill: bgColor }),
|
|
5017
|
-
/* @__PURE__ */ jsx("path", { d: fgD, fill: fgColor })
|
|
5018
|
-
] });
|
|
5019
|
-
});
|
|
5020
|
-
QRCodeSvg.displayName = "QRCodeSvg";
|
|
5021
|
-
function bytesToBinaryString(bytes) {
|
|
5022
|
-
return bytes.map((b) => String.fromCharCode(b & 255)).join("");
|
|
5023
|
-
}
|
|
5024
|
-
function encodeStringToUtf8Bytes(input) {
|
|
5025
|
-
return Array.from(new TextEncoder().encode(input));
|
|
5026
|
-
}
|
|
5027
|
-
var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#000000", level = "L", size = 256, value, ...props }, ref) {
|
|
5028
|
-
const qrcode = new import_QRCode.default(-1, import_ErrorCorrectLevel.default[level]);
|
|
5029
|
-
const utf8Bytes = encodeStringToUtf8Bytes(value);
|
|
5030
|
-
const binaryString = bytesToBinaryString(utf8Bytes);
|
|
5031
|
-
qrcode.addData(binaryString, "Byte");
|
|
5032
|
-
qrcode.make();
|
|
5033
|
-
const cells = qrcode.modules;
|
|
5034
|
-
return /* @__PURE__ */ jsx(
|
|
5035
|
-
QRCodeSvg,
|
|
5036
|
-
{
|
|
5037
|
-
...props,
|
|
5038
|
-
bgColor,
|
|
5039
|
-
bgD: cells.map(
|
|
5040
|
-
(row, rowIndex) => row.map((cell, cellIndex) => !cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
5041
|
-
).join(" "),
|
|
5042
|
-
fgColor,
|
|
5043
|
-
fgD: cells.map(
|
|
5044
|
-
(row, rowIndex) => row.map((cell, cellIndex) => cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
|
|
5045
|
-
).join(" "),
|
|
5046
|
-
ref,
|
|
5047
|
-
size,
|
|
5048
|
-
viewBoxSize: cells.length
|
|
5049
|
-
}
|
|
5050
|
-
);
|
|
5051
|
-
});
|
|
5052
5516
|
function PollarLogo(props) {
|
|
5053
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: [
|
|
5054
5518
|
/* @__PURE__ */ jsx(
|
|
@@ -5089,6 +5553,8 @@ function ReceiveModalTemplate({
|
|
|
5089
5553
|
onSelectChain,
|
|
5090
5554
|
copied,
|
|
5091
5555
|
notReadyReason,
|
|
5556
|
+
recoveryLocked,
|
|
5557
|
+
onSetupRecovery,
|
|
5092
5558
|
onCopy,
|
|
5093
5559
|
onClose
|
|
5094
5560
|
}) {
|
|
@@ -5107,7 +5573,10 @@ function ReceiveModalTemplate({
|
|
|
5107
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" }) }) }) })
|
|
5108
5574
|
] }),
|
|
5109
5575
|
chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
|
|
5110
|
-
|
|
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: [
|
|
5111
5580
|
/* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr", children: [
|
|
5112
5581
|
/* @__PURE__ */ jsxs("div", { className: "pollar-receive-network-badge", children: [
|
|
5113
5582
|
/* @__PURE__ */ jsx("span", { className: "pollar-receive-network-dot", "aria-hidden": true }),
|
|
@@ -5181,7 +5650,7 @@ function ReceiveModalTemplate({
|
|
|
5181
5650
|
);
|
|
5182
5651
|
}
|
|
5183
5652
|
function ReceiveModal({ onClose }) {
|
|
5184
|
-
const { wallet, wallets, styles } = usePollar();
|
|
5653
|
+
const { wallet, wallets, styles, recoveryLocked, openRecoveryModal } = usePollar();
|
|
5185
5654
|
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
5186
5655
|
const [copied, setCopied] = useState(false);
|
|
5187
5656
|
const copyTimerRef = useRef(null);
|
|
@@ -5223,6 +5692,11 @@ function ReceiveModal({ onClose }) {
|
|
|
5223
5692
|
setSelectedChain(chain);
|
|
5224
5693
|
},
|
|
5225
5694
|
notReadyReason,
|
|
5695
|
+
recoveryLocked,
|
|
5696
|
+
onSetupRecovery: () => {
|
|
5697
|
+
onClose();
|
|
5698
|
+
openRecoveryModal();
|
|
5699
|
+
},
|
|
5226
5700
|
copied,
|
|
5227
5701
|
onCopy: handleCopy,
|
|
5228
5702
|
onClose
|
|
@@ -5746,6 +6220,11 @@ function SendModalTemplate({
|
|
|
5746
6220
|
);
|
|
5747
6221
|
}
|
|
5748
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
|
+
}
|
|
5749
6228
|
function parseMemo(type, raw) {
|
|
5750
6229
|
const value = type === "id" ? raw.trim() : raw;
|
|
5751
6230
|
if (value.trim() === "") return void 0;
|
|
@@ -5905,7 +6384,15 @@ function SendModal({ onClose }) {
|
|
|
5905
6384
|
}
|
|
5906
6385
|
function handleMax() {
|
|
5907
6386
|
if (selectedAsset?.available !== null && selectedAsset?.available !== void 0) {
|
|
5908
|
-
|
|
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);
|
|
5909
6396
|
setFormError("");
|
|
5910
6397
|
}
|
|
5911
6398
|
}
|
|
@@ -7104,9 +7591,12 @@ function EarnModal({ onClose }) {
|
|
|
7104
7591
|
var nonEmpty2 = (value) => typeof value === "string" && !!value.trim();
|
|
7105
7592
|
function requiredCardStep(error) {
|
|
7106
7593
|
if (!error || typeof error !== "object") return null;
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
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;
|
|
7110
7600
|
if (!nonEmpty2(cardProviderId) || !nonEmpty2(optionId)) return null;
|
|
7111
7601
|
if (requirementType === "KYC" || requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") {
|
|
7112
7602
|
return { cardProviderId, type: requirementType, optionId };
|
|
@@ -7760,15 +8250,17 @@ function CardModal({ onClose }) {
|
|
|
7760
8250
|
transactions === null && /* @__PURE__ */ jsx("p", { className: "pollar-card-note", children: "Loading..." }),
|
|
7761
8251
|
transactions?.length === 0 && /* @__PURE__ */ jsx("p", { className: "pollar-card-note", children: "No movements yet." }),
|
|
7762
8252
|
transactions?.map((tx) => {
|
|
7763
|
-
const
|
|
7764
|
-
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;
|
|
7765
8257
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-card-tx", children: [
|
|
7766
8258
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
7767
8259
|
tx.merchantName ?? TX_LABELS[tx.type] ?? tx.type,
|
|
7768
8260
|
/* @__PURE__ */ jsxs("span", { className: "pollar-card-tx-meta", children: [
|
|
7769
8261
|
new Date(tx.occurredAt).toLocaleDateString(),
|
|
7770
8262
|
tx.merchantName ? ` \xB7 ${TX_LABELS[tx.type] ?? tx.type}` : "",
|
|
7771
|
-
|
|
8263
|
+
purchaseStatus && purchaseStatus !== "completed" ? ` \xB7 ${purchaseStatus}` : ""
|
|
7772
8264
|
] })
|
|
7773
8265
|
] }),
|
|
7774
8266
|
/* @__PURE__ */ jsxs("div", { className: "pollar-card-tx-amount", "data-direction": tx.direction ?? void 0, children: [
|
|
@@ -8167,7 +8659,8 @@ function useCosign() {
|
|
|
8167
8659
|
revokeDevice: useCallback(
|
|
8168
8660
|
(deviceId, options) => getClient().cosign.revokeDevice(deviceId, options),
|
|
8169
8661
|
[getClient]
|
|
8170
|
-
)
|
|
8662
|
+
),
|
|
8663
|
+
switchCustody: useCallback((to) => getClient().cosign.switchCustody(to), [getClient])
|
|
8171
8664
|
};
|
|
8172
8665
|
}
|
|
8173
8666
|
var OPERATION_LABELS = {
|
|
@@ -8189,6 +8682,15 @@ function expiresIn(request) {
|
|
|
8189
8682
|
const m = Math.floor(ms % 36e5 / 6e4);
|
|
8190
8683
|
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
8191
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
|
+
}
|
|
8192
8694
|
function detailRows(request) {
|
|
8193
8695
|
const details = request.details ?? {};
|
|
8194
8696
|
const rows = [];
|
|
@@ -8200,8 +8702,9 @@ function detailRows(request) {
|
|
|
8200
8702
|
]);
|
|
8201
8703
|
};
|
|
8202
8704
|
push("Destination", details.destination);
|
|
8203
|
-
|
|
8204
|
-
push("
|
|
8705
|
+
const readable = readableAmount(details);
|
|
8706
|
+
push("Amount", readable ? `${readable.amount} ${readable.symbol}` : details.amount);
|
|
8707
|
+
push("Asset", details.asset ?? details.assetCode ?? readable?.symbol);
|
|
8205
8708
|
push("Network", request.network);
|
|
8206
8709
|
return rows;
|
|
8207
8710
|
}
|
|
@@ -8276,7 +8779,6 @@ function CosignModalTemplate({
|
|
|
8276
8779
|
current && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
|
|
8277
8780
|
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf(current) }),
|
|
8278
8781
|
current.summary && /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: current.summary }),
|
|
8279
|
-
/* @__PURE__ */ jsx(GroupSteps, { request: current }),
|
|
8280
8782
|
/* @__PURE__ */ jsxs("dl", { className: "pollar-cosign-rows", children: [
|
|
8281
8783
|
detailRows(current).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
|
|
8282
8784
|
/* @__PURE__ */ jsx("dt", { children: k }),
|
|
@@ -8327,22 +8829,6 @@ function CosignModalTemplate({
|
|
|
8327
8829
|
}
|
|
8328
8830
|
);
|
|
8329
8831
|
}
|
|
8330
|
-
function GroupSteps({ request }) {
|
|
8331
|
-
const group = cosignGroupOf(request);
|
|
8332
|
-
if (!group || group.steps.length < 2) return null;
|
|
8333
|
-
const stepKey = cosignStepKeyOf(request);
|
|
8334
|
-
return /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-group", children: [
|
|
8335
|
-
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-group-title", children: group.title }),
|
|
8336
|
-
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Approving covers these steps; each one is checked before it is signed." }),
|
|
8337
|
-
/* @__PURE__ */ jsx("ol", { className: "pollar-cosign-group-steps", children: group.steps.map((step) => {
|
|
8338
|
-
const isThis = stepKey !== null && stepInGroup({ steps: [step] }, stepKey);
|
|
8339
|
-
return /* @__PURE__ */ jsxs("li", { className: isThis ? "pollar-cosign-group-step-current" : void 0, children: [
|
|
8340
|
-
step.label,
|
|
8341
|
-
isThis && /* @__PURE__ */ jsx("span", { className: "pollar-cosign-muted", children: " (this one)" })
|
|
8342
|
-
] }, step.key);
|
|
8343
|
-
}) })
|
|
8344
|
-
] });
|
|
8345
|
-
}
|
|
8346
8832
|
function CosignModal({ onClose }) {
|
|
8347
8833
|
const { getClient, styles, network } = usePollar();
|
|
8348
8834
|
const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
|
|
@@ -8425,6 +8911,493 @@ function CosignModal({ onClose }) {
|
|
|
8425
8911
|
}
|
|
8426
8912
|
) });
|
|
8427
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
|
+
}
|
|
8428
9401
|
var OPERATION_LABELS2 = {
|
|
8429
9402
|
payment: "Send",
|
|
8430
9403
|
path_payment_strict_send: "Swap",
|
|
@@ -8451,7 +9424,7 @@ function detailRows2(confirmation) {
|
|
|
8451
9424
|
push("Network", confirmation.network);
|
|
8452
9425
|
return rows;
|
|
8453
9426
|
}
|
|
8454
|
-
function
|
|
9427
|
+
function CloseIcon4() {
|
|
8455
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" }) });
|
|
8456
9429
|
}
|
|
8457
9430
|
function ConfirmModal({ confirmation }) {
|
|
@@ -8475,7 +9448,7 @@ function ConfirmModal({ confirmation }) {
|
|
|
8475
9448
|
children: [
|
|
8476
9449
|
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
|
|
8477
9450
|
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm transaction" }),
|
|
8478
|
-
/* @__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(
|
|
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, {}) }) })
|
|
8479
9452
|
] }),
|
|
8480
9453
|
/* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
|
|
8481
9454
|
/* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf2(confirmation) }),
|
|
@@ -8612,6 +9585,25 @@ function formatDate(iso) {
|
|
|
8612
9585
|
function truncateMiddle(value, head = 4, tail = 4) {
|
|
8613
9586
|
return value.length <= head + tail + 1 ? value : `${value.slice(0, head)}\u2026${value.slice(-tail)}`;
|
|
8614
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
|
+
}
|
|
8615
9607
|
var STELLAR_ADDRESS = /\b[GC][A-Z2-7]{55}\b/g;
|
|
8616
9608
|
function AddressChip({ value, label }) {
|
|
8617
9609
|
return /* @__PURE__ */ jsxs("span", { className: "pollar-hist-item-issuer", children: [
|
|
@@ -8749,15 +9741,17 @@ function TxHistoryModalTemplate({
|
|
|
8749
9741
|
] }),
|
|
8750
9742
|
/* @__PURE__ */ jsxs("span", { className: "pollar-hist-item-meta", children: [
|
|
8751
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
|
+
] }),
|
|
8752
9748
|
typeof record.details?.sponsored === "boolean" && /* @__PURE__ */ jsxs("span", { children: [
|
|
8753
9749
|
"\xB7 ",
|
|
8754
9750
|
record.details.sponsored ? "Sponsored" : "Self-paid"
|
|
8755
9751
|
] }),
|
|
8756
9752
|
record.fee && /* @__PURE__ */ jsxs("span", { children: [
|
|
8757
9753
|
"\xB7 ",
|
|
8758
|
-
record.fee
|
|
8759
|
-
" ",
|
|
8760
|
-
record.fee.unit
|
|
9754
|
+
formatFee(record.fee)
|
|
8761
9755
|
] })
|
|
8762
9756
|
] }),
|
|
8763
9757
|
/* @__PURE__ */ jsxs("div", { className: "pollar-hist-item-footer", children: [
|
|
@@ -9068,6 +10062,48 @@ var browserPasskeySigner = async ({ credentialId, challenge }) => {
|
|
|
9068
10062
|
signature: response.signature
|
|
9069
10063
|
};
|
|
9070
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
|
+
};
|
|
9071
10107
|
var DEFAULT_APP_CONFIG = {
|
|
9072
10108
|
application: { name: "", network: "testnet", chains: [] },
|
|
9073
10109
|
styles: {}
|
|
@@ -9092,7 +10128,11 @@ function PollarProvider({
|
|
|
9092
10128
|
}) {
|
|
9093
10129
|
const [pollarClient] = useState(() => {
|
|
9094
10130
|
if (isPollarClient(client)) {
|
|
9095
|
-
client.setPasskeyDefaults({
|
|
10131
|
+
client.setPasskeyDefaults({
|
|
10132
|
+
passkey: browserPasskeyCeremony,
|
|
10133
|
+
passkeySign: browserPasskeySigner,
|
|
10134
|
+
recoveryPasskey: browserRecoveryPasskey
|
|
10135
|
+
});
|
|
9096
10136
|
return client;
|
|
9097
10137
|
}
|
|
9098
10138
|
const alreadyBuilt = clientByConfig.get(client);
|
|
@@ -9100,7 +10140,8 @@ function PollarProvider({
|
|
|
9100
10140
|
const built = new PollarClient({
|
|
9101
10141
|
...client,
|
|
9102
10142
|
passkey: client.passkey ?? browserPasskeyCeremony,
|
|
9103
|
-
passkeySign: client.passkeySign ?? browserPasskeySigner
|
|
10143
|
+
passkeySign: client.passkeySign ?? browserPasskeySigner,
|
|
10144
|
+
recoveryPasskey: client.recoveryPasskey ?? browserRecoveryPasskey
|
|
9104
10145
|
});
|
|
9105
10146
|
clientByConfig.set(client, built);
|
|
9106
10147
|
return built;
|
|
@@ -9255,6 +10296,7 @@ function PollarProvider({
|
|
|
9255
10296
|
const [receiveModalOpen, setReceiveModalOpen] = useState(false);
|
|
9256
10297
|
const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
|
|
9257
10298
|
const [cosignModalOpen, setCosignModalOpen] = useState(false);
|
|
10299
|
+
const [recoveryModalOpen, setRecoveryModalOpen] = useState(false);
|
|
9258
10300
|
const cosignPendingCount = useRef(0);
|
|
9259
10301
|
useEffect(() => {
|
|
9260
10302
|
if (cosign.pending.length > cosignPendingCount.current) setCosignModalOpen(true);
|
|
@@ -9277,6 +10319,7 @@ function PollarProvider({
|
|
|
9277
10319
|
setReceiveModalOpen(false);
|
|
9278
10320
|
setSessionsModalOpen(false);
|
|
9279
10321
|
setCosignModalOpen(false);
|
|
10322
|
+
setRecoveryModalOpen(false);
|
|
9280
10323
|
setDistributionRulesModalOpen(false);
|
|
9281
10324
|
}),
|
|
9282
10325
|
[pollarClient]
|
|
@@ -9285,6 +10328,45 @@ function PollarProvider({
|
|
|
9285
10328
|
const walletProvisioning = sessionState?.wallet?.provisioning;
|
|
9286
10329
|
const hasSession = sessionState !== null;
|
|
9287
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]);
|
|
9288
10370
|
const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
|
|
9289
10371
|
const refreshAssets = useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
|
|
9290
10372
|
const contextValue = useMemo(() => {
|
|
@@ -9295,7 +10377,7 @@ function PollarProvider({
|
|
|
9295
10377
|
wallets: pollarClient.getWallets(),
|
|
9296
10378
|
isAuthenticated: hasSession,
|
|
9297
10379
|
hasWallet: pollarClient.hasWallet(),
|
|
9298
|
-
createWallet: () => pollarClient.createWallet(),
|
|
10380
|
+
createWallet: (options) => pollarClient.createWallet(options),
|
|
9299
10381
|
verified,
|
|
9300
10382
|
// client
|
|
9301
10383
|
getClient,
|
|
@@ -9364,6 +10446,12 @@ function PollarProvider({
|
|
|
9364
10446
|
// cosign
|
|
9365
10447
|
cosign,
|
|
9366
10448
|
openCosignModal: () => setCosignModalOpen(true),
|
|
10449
|
+
recovery,
|
|
10450
|
+
recoveryLocked,
|
|
10451
|
+
refreshRecovery,
|
|
10452
|
+
openRecoveryModal: () => setRecoveryModalOpen(true),
|
|
10453
|
+
custodyMigration,
|
|
10454
|
+
refreshCustodyMigration,
|
|
9367
10455
|
confirmations,
|
|
9368
10456
|
// distribution
|
|
9369
10457
|
openDistributionRulesModal: () => setDistributionRulesModalOpen(true),
|
|
@@ -9387,6 +10475,11 @@ function PollarProvider({
|
|
|
9387
10475
|
}, [
|
|
9388
10476
|
walletAddress,
|
|
9389
10477
|
walletProvisioning,
|
|
10478
|
+
recovery,
|
|
10479
|
+
recoveryLocked,
|
|
10480
|
+
refreshRecovery,
|
|
10481
|
+
custodyMigration,
|
|
10482
|
+
refreshCustodyMigration,
|
|
9390
10483
|
hasSession,
|
|
9391
10484
|
verified,
|
|
9392
10485
|
pollarClient,
|
|
@@ -9444,6 +10537,23 @@ function PollarProvider({
|
|
|
9444
10537
|
receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
|
|
9445
10538
|
sessionsModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) }),
|
|
9446
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) }) }),
|
|
9447
10557
|
firstConfirmation && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: firstConfirmation.reject, children: /* @__PURE__ */ jsx(ConfirmModal, { confirmation: firstConfirmation }, firstConfirmation.id) }),
|
|
9448
10558
|
distributionRulesModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setDistributionRulesModalOpen(false), children: /* @__PURE__ */ jsx(DistributionRulesModal, { onClose: () => setDistributionRulesModalOpen(false) }) })
|
|
9449
10559
|
] });
|
|
@@ -9494,6 +10604,8 @@ function WalletButtonTemplate({
|
|
|
9494
10604
|
isInProgress,
|
|
9495
10605
|
showCreateAccount,
|
|
9496
10606
|
notReadyReason,
|
|
10607
|
+
recoveryLocked,
|
|
10608
|
+
onRecovery,
|
|
9497
10609
|
creatingAccount,
|
|
9498
10610
|
onToggleOpen,
|
|
9499
10611
|
onCreateAccount,
|
|
@@ -9511,8 +10623,11 @@ function WalletButtonTemplate({
|
|
|
9511
10623
|
signedIn,
|
|
9512
10624
|
creatingWallet,
|
|
9513
10625
|
createWalletError,
|
|
10626
|
+
custodyChoice,
|
|
9514
10627
|
onCreateWallet
|
|
9515
10628
|
}) {
|
|
10629
|
+
const [selfCustodyNotice, setSelfCustodyNotice] = useState(false);
|
|
10630
|
+
const [risksAccepted, setRisksAccepted] = useState(false);
|
|
9516
10631
|
if (!walletAddress && signedIn) {
|
|
9517
10632
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, children: [
|
|
9518
10633
|
/* @__PURE__ */ jsxs("button", { className: "pollar-wallet-btn", style: { backgroundColor: accentColor }, onClick: onToggleOpen, children: [
|
|
@@ -9536,19 +10651,84 @@ function WalletButtonTemplate({
|
|
|
9536
10651
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: createWalletError }),
|
|
9537
10652
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
9538
10653
|
] }),
|
|
9539
|
-
onCreateWallet && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10654
|
+
onCreateWallet && !custodyChoice && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9540
10655
|
/* @__PURE__ */ jsx(
|
|
9541
10656
|
"button",
|
|
9542
10657
|
{
|
|
9543
10658
|
className: "pollar-wallet-dropdown-item",
|
|
9544
10659
|
style: { color: itemColor },
|
|
9545
|
-
onClick: onCreateWallet,
|
|
10660
|
+
onClick: () => onCreateWallet(),
|
|
9546
10661
|
disabled: creatingWallet,
|
|
9547
10662
|
children: creatingWallet ? "Creating wallet\u2026" : "Create wallet"
|
|
9548
10663
|
}
|
|
9549
10664
|
),
|
|
9550
10665
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
9551
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
|
+
] }),
|
|
9552
10732
|
/* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSessions, children: "Active sessions" }),
|
|
9553
10733
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
|
|
9554
10734
|
/* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: "Log out" })
|
|
@@ -9604,6 +10784,10 @@ function WalletButtonTemplate({
|
|
|
9604
10784
|
}
|
|
9605
10785
|
),
|
|
9606
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
|
+
] }),
|
|
9607
10791
|
notReadyReason && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9608
10792
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: notReadyReason }),
|
|
9609
10793
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
|
|
@@ -9836,6 +11020,31 @@ function WalletButtonTemplate({
|
|
|
9836
11020
|
),
|
|
9837
11021
|
"Active sessions"
|
|
9838
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
|
+
),
|
|
9839
11048
|
/* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
|
|
9840
11049
|
/* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: [
|
|
9841
11050
|
/* @__PURE__ */ jsxs(
|
|
@@ -9878,7 +11087,10 @@ function WalletButton() {
|
|
|
9878
11087
|
tx: transaction,
|
|
9879
11088
|
isAuthenticated,
|
|
9880
11089
|
createWallet,
|
|
9881
|
-
appConfig
|
|
11090
|
+
appConfig,
|
|
11091
|
+
recovery,
|
|
11092
|
+
recoveryLocked,
|
|
11093
|
+
openRecoveryModal
|
|
9882
11094
|
} = usePollar();
|
|
9883
11095
|
const { primaryChain, primaryAddress } = useChains();
|
|
9884
11096
|
const walletAddress = primaryAddress || (wallet?.address ?? "");
|
|
@@ -9941,12 +11153,12 @@ function WalletButton() {
|
|
|
9941
11153
|
setCreating(false);
|
|
9942
11154
|
}
|
|
9943
11155
|
}
|
|
9944
|
-
async function handleCreateWallet() {
|
|
11156
|
+
async function handleCreateWallet(custody) {
|
|
9945
11157
|
if (creatingWallet) return;
|
|
9946
11158
|
setCreatingWallet(true);
|
|
9947
11159
|
setCreateWalletError(null);
|
|
9948
11160
|
try {
|
|
9949
|
-
await createWallet();
|
|
11161
|
+
await createWallet(custody ? { custody } : void 0);
|
|
9950
11162
|
setOpen(false);
|
|
9951
11163
|
} catch (err) {
|
|
9952
11164
|
setCreateWalletError(
|
|
@@ -9976,6 +11188,10 @@ function WalletButton() {
|
|
|
9976
11188
|
setOpen(false);
|
|
9977
11189
|
openSessionsModal();
|
|
9978
11190
|
}
|
|
11191
|
+
function handleRecovery() {
|
|
11192
|
+
setOpen(false);
|
|
11193
|
+
openRecoveryModal();
|
|
11194
|
+
}
|
|
9979
11195
|
function handleKyc() {
|
|
9980
11196
|
setOpen(false);
|
|
9981
11197
|
openKycModal();
|
|
@@ -10004,6 +11220,7 @@ function WalletButton() {
|
|
|
10004
11220
|
signedIn: isAuthenticated,
|
|
10005
11221
|
creatingWallet,
|
|
10006
11222
|
createWalletError,
|
|
11223
|
+
custodyChoice: appConfig.application?.walletCustodyMode === "USER_CHOICE",
|
|
10007
11224
|
...appConfig.application?.walletProvisioning === "NONE" ? {} : { onCreateWallet: handleCreateWallet },
|
|
10008
11225
|
notReadyReason,
|
|
10009
11226
|
creatingAccount: creating,
|
|
@@ -10015,6 +11232,8 @@ function WalletButton() {
|
|
|
10015
11232
|
onSend: handleSend,
|
|
10016
11233
|
onReceive: handleReceive,
|
|
10017
11234
|
onSessions: handleSessions,
|
|
11235
|
+
recoveryLocked,
|
|
11236
|
+
...recovery?.required ? { onRecovery: handleRecovery } : {},
|
|
10018
11237
|
onKyc: handleKyc,
|
|
10019
11238
|
onRamp: handleRamp,
|
|
10020
11239
|
onDistributionRules: handleDistributionRules,
|
|
@@ -10024,6 +11243,6 @@ function WalletButton() {
|
|
|
10024
11243
|
);
|
|
10025
11244
|
}
|
|
10026
11245
|
|
|
10027
|
-
export { CardModal, ChainSelect, CosignModal, CosignModalTemplate, 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, useCosign, 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 };
|
|
10028
11247
|
//# sourceMappingURL=index.mjs.map
|
|
10029
11248
|
//# sourceMappingURL=index.mjs.map
|