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