@pollar/core 0.9.0-rc.1 → 0.9.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +170 -8
- package/dist/index.d.ts +170 -8
- package/dist/index.js +100 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +100 -47
- package/dist/index.mjs.map +1 -1
- package/dist/index.rn.d.mts +1 -1
- package/dist/index.rn.d.ts +1 -1
- package/dist/index.rn.js +100 -47
- package/dist/index.rn.js.map +1 -1
- package/dist/index.rn.mjs +100 -47
- package/dist/index.rn.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1171,7 +1171,7 @@ function defaultStorage(options = {}) {
|
|
|
1171
1171
|
}
|
|
1172
1172
|
|
|
1173
1173
|
// src/version.ts
|
|
1174
|
-
var POLLAR_CORE_VERSION = "0.9.0-rc.
|
|
1174
|
+
var POLLAR_CORE_VERSION = "0.9.0-rc.3" ;
|
|
1175
1175
|
|
|
1176
1176
|
// src/visibility/noop.ts
|
|
1177
1177
|
function createNoopVisibilityProvider() {
|
|
@@ -1343,7 +1343,10 @@ function openAlbedoPopup(url) {
|
|
|
1343
1343
|
}
|
|
1344
1344
|
function waitForAlbedoPopup() {
|
|
1345
1345
|
return new Promise((resolve, reject) => {
|
|
1346
|
-
const timeout = setTimeout(() =>
|
|
1346
|
+
const timeout = setTimeout(() => {
|
|
1347
|
+
window.removeEventListener("message", handler);
|
|
1348
|
+
reject(new Error("Albedo response timeout"));
|
|
1349
|
+
}, 2 * 60 * 1e3);
|
|
1347
1350
|
function handler(event) {
|
|
1348
1351
|
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
1349
1352
|
clearTimeout(timeout);
|
|
@@ -1353,24 +1356,6 @@ function waitForAlbedoPopup() {
|
|
|
1353
1356
|
window.addEventListener("message", handler);
|
|
1354
1357
|
});
|
|
1355
1358
|
}
|
|
1356
|
-
function waitForAlbedoResult() {
|
|
1357
|
-
return new Promise((resolve, reject) => {
|
|
1358
|
-
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
1359
|
-
const parseResult = () => {
|
|
1360
|
-
const params = new URLSearchParams(window.location.search);
|
|
1361
|
-
if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
|
|
1362
|
-
clearTimeout(timeout);
|
|
1363
|
-
const result = {};
|
|
1364
|
-
params.forEach((value, key) => {
|
|
1365
|
-
result[key] = value;
|
|
1366
|
-
});
|
|
1367
|
-
window.history.replaceState({}, document.title, window.location.pathname);
|
|
1368
|
-
resolve(result);
|
|
1369
|
-
};
|
|
1370
|
-
parseResult();
|
|
1371
|
-
window.addEventListener("popstate", parseResult);
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
1359
|
var AlbedoAdapter = class {
|
|
1375
1360
|
/**
|
|
1376
1361
|
* Network used for `connect` and `signAuthEntry` (which carry no per-call
|
|
@@ -1412,10 +1397,10 @@ var AlbedoAdapter = class {
|
|
|
1412
1397
|
url.searchParams.set("xdr", xdr);
|
|
1413
1398
|
url.searchParams.set("app_name", "Pollar");
|
|
1414
1399
|
url.searchParams.set("network", albedoNetwork(options, this.network));
|
|
1415
|
-
url.searchParams.set("callback", window.location.
|
|
1400
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1416
1401
|
url.searchParams.set("origin", window.location.origin);
|
|
1417
|
-
|
|
1418
|
-
const result = await
|
|
1402
|
+
openAlbedoPopup(url.toString());
|
|
1403
|
+
const result = await waitForAlbedoPopup();
|
|
1419
1404
|
if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
|
|
1420
1405
|
return { signedTxXdr: result.signed_envelope_xdr };
|
|
1421
1406
|
}
|
|
@@ -1425,10 +1410,10 @@ var AlbedoAdapter = class {
|
|
|
1425
1410
|
url.searchParams.set("xdr", entryXdr);
|
|
1426
1411
|
url.searchParams.set("app_name", "Pollar");
|
|
1427
1412
|
url.searchParams.set("network", this.network);
|
|
1428
|
-
url.searchParams.set("callback", window.location.
|
|
1413
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1429
1414
|
url.searchParams.set("origin", window.location.origin);
|
|
1430
|
-
|
|
1431
|
-
const result = await
|
|
1415
|
+
openAlbedoPopup(url.toString());
|
|
1416
|
+
const result = await waitForAlbedoPopup();
|
|
1432
1417
|
if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
|
|
1433
1418
|
return { signedAuthEntry: result.signed_xdr };
|
|
1434
1419
|
}
|
|
@@ -1511,8 +1496,8 @@ function isValidSession(value, logger = console) {
|
|
|
1511
1496
|
return false;
|
|
1512
1497
|
}
|
|
1513
1498
|
const w = wallet;
|
|
1514
|
-
if (w["type"] !== "
|
|
1515
|
-
logger.debug("[PollarClient:session] Invalid session \u2014 wallet.type must be
|
|
1499
|
+
if (w["type"] !== "internal" && w["type"] !== "smart" && w["type"] !== "external") {
|
|
1500
|
+
logger.debug("[PollarClient:session] Invalid session \u2014 wallet.type must be internal|smart|external");
|
|
1516
1501
|
return false;
|
|
1517
1502
|
}
|
|
1518
1503
|
if (w["address"] !== null && !isBoundedString(w["address"], MAX_WALLET_PUBLIC_KEY)) {
|
|
@@ -1543,6 +1528,9 @@ async function readStorage(storage, apiKeyHash, logger = console) {
|
|
|
1543
1528
|
if (w && w["address"] == null && typeof w["publicKey"] === "string") {
|
|
1544
1529
|
w["address"] = w["publicKey"];
|
|
1545
1530
|
}
|
|
1531
|
+
if (w && w["type"] === "custodial") {
|
|
1532
|
+
w["type"] = "internal";
|
|
1533
|
+
}
|
|
1546
1534
|
}
|
|
1547
1535
|
if (!isValidSession(session, logger)) {
|
|
1548
1536
|
await storage.remove(sessionStorageKey(apiKeyHash));
|
|
@@ -1643,16 +1631,12 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
|
|
|
1643
1631
|
}
|
|
1644
1632
|
const reader = data.getReader();
|
|
1645
1633
|
const decoder = new TextDecoder();
|
|
1646
|
-
let streamDone = false;
|
|
1647
1634
|
let sawAnyChunk = false;
|
|
1648
1635
|
try {
|
|
1649
1636
|
while (true) {
|
|
1650
1637
|
throwIfAborted(signal);
|
|
1651
1638
|
const { done, value } = await reader.read();
|
|
1652
|
-
if (done)
|
|
1653
|
-
streamDone = true;
|
|
1654
|
-
break;
|
|
1655
|
-
}
|
|
1639
|
+
if (done) break;
|
|
1656
1640
|
sawAnyChunk = true;
|
|
1657
1641
|
const chunk = decoder.decode(value);
|
|
1658
1642
|
for (const message of chunk.split("\n\n").filter(Boolean)) {
|
|
@@ -1680,8 +1664,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
|
|
|
1680
1664
|
}
|
|
1681
1665
|
if (sawAnyChunk) backoff = retryDelayMs;
|
|
1682
1666
|
else backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
|
|
1683
|
-
|
|
1684
|
-
if (delay) await sleep(delay);
|
|
1667
|
+
await sleep(backoff);
|
|
1685
1668
|
}
|
|
1686
1669
|
}
|
|
1687
1670
|
async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal, logger = console) {
|
|
@@ -1761,17 +1744,18 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1761
1744
|
signal
|
|
1762
1745
|
});
|
|
1763
1746
|
if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content, logger)) {
|
|
1764
|
-
|
|
1747
|
+
const sessionWallet = data.content.data?.providers?.wallet?.address;
|
|
1748
|
+
if (expectedWallet && sessionWallet !== expectedWallet) {
|
|
1765
1749
|
setAuthState({
|
|
1766
1750
|
step: "error",
|
|
1767
1751
|
previousStep: "authenticating",
|
|
1768
1752
|
message: "Wallet mismatch: session wallet does not match connected wallet",
|
|
1769
1753
|
errorCode: AUTH_ERROR_CODES.WALLET_AUTH_FAILED
|
|
1770
1754
|
});
|
|
1771
|
-
clearSession();
|
|
1755
|
+
await clearSession();
|
|
1772
1756
|
return;
|
|
1773
1757
|
}
|
|
1774
|
-
storeSession(data.content);
|
|
1758
|
+
await storeSession(data.content);
|
|
1775
1759
|
} else {
|
|
1776
1760
|
setAuthState({
|
|
1777
1761
|
step: "error",
|
|
@@ -1779,7 +1763,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1779
1763
|
message: "Failed to load session",
|
|
1780
1764
|
errorCode: AUTH_ERROR_CODES.AUTH_FAILED
|
|
1781
1765
|
});
|
|
1782
|
-
clearSession();
|
|
1766
|
+
await clearSession();
|
|
1783
1767
|
}
|
|
1784
1768
|
}
|
|
1785
1769
|
|
|
@@ -1908,7 +1892,7 @@ async function loginOAuth(provider, deps) {
|
|
|
1908
1892
|
}
|
|
1909
1893
|
|
|
1910
1894
|
// src/client/auth/passkeyFlow.ts
|
|
1911
|
-
async function
|
|
1895
|
+
async function smartWalletFlow(deps, mode) {
|
|
1912
1896
|
const { api, signal, setAuthState, passkey } = deps;
|
|
1913
1897
|
if (!passkey) {
|
|
1914
1898
|
setAuthState({
|
|
@@ -1931,7 +1915,7 @@ async function loginSmartWallet(deps) {
|
|
|
1931
1915
|
return failPasskey(setAuthState, "Failed to start passkey");
|
|
1932
1916
|
}
|
|
1933
1917
|
setAuthState({ step: "creating_passkey" });
|
|
1934
|
-
const ceremony = await passkey({ challenge });
|
|
1918
|
+
const ceremony = await passkey({ challenge, mode });
|
|
1935
1919
|
const response = ceremony.response;
|
|
1936
1920
|
if (ceremony.kind === "register") {
|
|
1937
1921
|
setAuthState({ step: "deploying_smart_account" });
|
|
@@ -2095,7 +2079,7 @@ var PollarClient = class {
|
|
|
2095
2079
|
this._visibilityProvider = config.visibilityProvider ?? defaultVisibilityProvider();
|
|
2096
2080
|
this._maxIdleMs = config.maxIdleMs;
|
|
2097
2081
|
this._openAuthUrl = config.openAuthUrl ?? defaultWebOAuthOpener;
|
|
2098
|
-
this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location
|
|
2082
|
+
this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location?.origin ?? "" : "");
|
|
2099
2083
|
this._api = createApiClient(this.basePath);
|
|
2100
2084
|
this._wireMiddlewares();
|
|
2101
2085
|
this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
|
|
@@ -2524,9 +2508,10 @@ var PollarClient = class {
|
|
|
2524
2508
|
loginWallet(type, this._flowDeps(controller.signal)).catch((err) => this._handleFlowError(err));
|
|
2525
2509
|
}
|
|
2526
2510
|
/**
|
|
2527
|
-
* "Smart Wallet" login: runs the passkey (WebAuthn) ceremony
|
|
2528
|
-
* user
|
|
2529
|
-
* ceremony to be configured (e.g. via
|
|
2511
|
+
* "Smart Wallet" login: runs the passkey (WebAuthn) `get()` ceremony for a
|
|
2512
|
+
* returning user and signs them in. Use {@link createSmartWallet} for a new
|
|
2513
|
+
* user. Requires the `passkey` ceremony to be configured (e.g. via
|
|
2514
|
+
* `@pollar/react`).
|
|
2530
2515
|
*/
|
|
2531
2516
|
loginSmartWallet() {
|
|
2532
2517
|
if (!isClientRuntime) {
|
|
@@ -2534,7 +2519,21 @@ var PollarClient = class {
|
|
|
2534
2519
|
return;
|
|
2535
2520
|
}
|
|
2536
2521
|
const controller = this._newController();
|
|
2537
|
-
|
|
2522
|
+
smartWalletFlow(this._flowDeps(controller.signal), "login").catch((err) => this._handleFlowError(err));
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* "Smart Wallet" registration: runs the passkey (WebAuthn) `create()` ceremony
|
|
2526
|
+
* for a new user and deploys a sponsored smart-account C-address. Use
|
|
2527
|
+
* {@link loginSmartWallet} for a returning user. Requires the `passkey`
|
|
2528
|
+
* ceremony to be configured (e.g. via `@pollar/react`).
|
|
2529
|
+
*/
|
|
2530
|
+
createSmartWallet() {
|
|
2531
|
+
if (!isClientRuntime) {
|
|
2532
|
+
warnServerSide("createSmartWallet");
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const controller = this._newController();
|
|
2536
|
+
smartWalletFlow(this._flowDeps(controller.signal), "register").catch((err) => this._handleFlowError(err));
|
|
2538
2537
|
}
|
|
2539
2538
|
// ─── Cancel ───────────────────────────────────────────────────────────────
|
|
2540
2539
|
cancelLogin() {
|
|
@@ -2774,6 +2773,54 @@ var PollarClient = class {
|
|
|
2774
2773
|
this._setEnabledAssetsState({ step: "error", message: "Failed to load assets" });
|
|
2775
2774
|
}
|
|
2776
2775
|
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Establishes (omit `limit`) or removes (`limit: '0'`) a trustline for an asset.
|
|
2778
|
+
*
|
|
2779
|
+
* Routing mirrors how the platform pays for the reserve:
|
|
2780
|
+
* - **Sponsored custodial** (`opts.sponsored` true, internal wallet) → the
|
|
2781
|
+
* server orchestrates a sponsored `changeTrust`: the app's wallets cover the
|
|
2782
|
+
* 0.5 XLM reserve and the fee, so the user pays nothing. Pass the asset's
|
|
2783
|
+
* `sponsored` flag (from {@link refreshAssets}) straight through.
|
|
2784
|
+
* - **Self-paid** (external/adapter wallet, sponsorship disabled, or a custom
|
|
2785
|
+
* asset not configured in the app) → a plain `change_trust` transaction the
|
|
2786
|
+
* user's own wallet signs and pays for, via {@link runTx}.
|
|
2787
|
+
*
|
|
2788
|
+
* Does not refresh on its own — callers should `refreshAssets()` afterwards.
|
|
2789
|
+
*/
|
|
2790
|
+
async setTrustline(asset, opts) {
|
|
2791
|
+
const limit = opts?.limit;
|
|
2792
|
+
const walletType = this._session?.wallet?.type;
|
|
2793
|
+
if (!this._session?.wallet?.address) {
|
|
2794
|
+
return { status: "error", details: "No wallet connected" };
|
|
2795
|
+
}
|
|
2796
|
+
if (walletType === "smart") {
|
|
2797
|
+
return { status: "error", details: "Trustlines do not apply to smart wallets" };
|
|
2798
|
+
}
|
|
2799
|
+
if (opts?.sponsored && !this._walletAdapter && walletType === "internal") {
|
|
2800
|
+
try {
|
|
2801
|
+
const { data, error } = await this._api.POST("/wallet/assets/trustline", {
|
|
2802
|
+
body: { code: asset.code, issuer: asset.issuer, ...limit !== void 0 && { limit } }
|
|
2803
|
+
});
|
|
2804
|
+
if (!error && data?.success) {
|
|
2805
|
+
if (data.content) this._setEnabledAssetsState({ step: "loaded", data: data.content });
|
|
2806
|
+
return { status: "success" };
|
|
2807
|
+
}
|
|
2808
|
+
const details = error?.details ?? error?.code;
|
|
2809
|
+
return { status: "error", ...details && { details } };
|
|
2810
|
+
} catch (err) {
|
|
2811
|
+
const details = err instanceof Error ? err.message : void 0;
|
|
2812
|
+
return { status: "error", ...details && { details } };
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
return this.runTx("change_trust", {
|
|
2816
|
+
asset: {
|
|
2817
|
+
type: asset.code.length <= 4 ? "credit_alphanum4" : "credit_alphanum12",
|
|
2818
|
+
code: asset.code,
|
|
2819
|
+
issuer: asset.issuer
|
|
2820
|
+
},
|
|
2821
|
+
...limit !== void 0 && { limit }
|
|
2822
|
+
});
|
|
2823
|
+
}
|
|
2777
2824
|
// ─── Transactions ─────────────────────────────────────────────────────────
|
|
2778
2825
|
/**
|
|
2779
2826
|
* Builds an unsigned XDR. Drives `_setTransactionState` for modal-style UIs
|
|
@@ -3409,6 +3456,9 @@ var PollarClient = class {
|
|
|
3409
3456
|
void this._resume();
|
|
3410
3457
|
} else {
|
|
3411
3458
|
this._log.info("[PollarClient] No session in storage");
|
|
3459
|
+
if (this._authState.step !== "idle") {
|
|
3460
|
+
await this._clearSession();
|
|
3461
|
+
}
|
|
3412
3462
|
}
|
|
3413
3463
|
}
|
|
3414
3464
|
/**
|
|
@@ -3455,8 +3505,11 @@ var PollarClient = class {
|
|
|
3455
3505
|
user: session.user,
|
|
3456
3506
|
// The wire response still carries the legacy `publicKey` alias (kept for
|
|
3457
3507
|
// older SDKs); the persisted session standardizes on `address` only.
|
|
3508
|
+
// The wire also still emits the legacy type `'custodial'` (unchanged for
|
|
3509
|
+
// SDKs ≤0.8.x); we remap it to `'internal'` here so the SDK surface and
|
|
3510
|
+
// persisted session speak one vocabulary while the wire stays compatible.
|
|
3458
3511
|
wallet: {
|
|
3459
|
-
type: w.type,
|
|
3512
|
+
type: w.type === "custodial" ? "internal" : w.type,
|
|
3460
3513
|
address: w.address ?? w.publicKey ?? null,
|
|
3461
3514
|
...w.existsOnStellar !== void 0 ? { existsOnStellar: w.existsOnStellar } : {},
|
|
3462
3515
|
...w.createdAt !== void 0 ? { createdAt: w.createdAt } : {},
|