@pollar/core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.rn.js CHANGED
@@ -30,10 +30,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
 
31
31
  // ../../node_modules/@stellar/freighter-api/build/index.min.js
32
32
  var require_index_min = __commonJS({
33
- "../../node_modules/@stellar/freighter-api/build/index.min.js"(exports$1, module) {
33
+ "../../node_modules/@stellar/freighter-api/build/index.min.js"(exports, module) {
34
34
  !(function(e, r) {
35
- "object" == typeof exports$1 && "object" == typeof module ? module.exports = r() : "function" == typeof define && define.amd ? define([], r) : "object" == typeof exports$1 ? exports$1.freighterApi = r() : e.freighterApi = r();
36
- })(exports$1, (() => (() => {
35
+ "object" == typeof exports && "object" == typeof module ? module.exports = r() : "function" == typeof define && define.amd ? define([], r) : "object" == typeof exports ? exports.freighterApi = r() : e.freighterApi = r();
36
+ })(exports, (() => (() => {
37
37
  var e, r, E = { d: (e2, r2) => {
38
38
  for (var o2 in r2) E.o(r2, o2) && !E.o(e2, o2) && Object.defineProperty(e2, o2, { enumerable: true, get: r2[o2] });
39
39
  }, o: (e2, r2) => Object.prototype.hasOwnProperty.call(e2, r2), r: (e2) => {
@@ -452,9 +452,7 @@ var WebCryptoKeyManager = class {
452
452
  */
453
453
  this._initPromise = null;
454
454
  if (typeof globalThis.crypto === "undefined" || !globalThis.crypto.subtle) {
455
- throw new Error(
456
- "[PollarClient:keys] SubtleCrypto is unavailable. DPoP requires a secure context (HTTPS or localhost)."
457
- );
455
+ throw new Error("[PollarClient:keys] SubtleCrypto is unavailable. DPoP requires a secure context (HTTPS or localhost).");
458
456
  }
459
457
  this.apiKey = apiKey;
460
458
  }
@@ -569,7 +567,7 @@ function createClient(clientOptions) {
569
567
  const {
570
568
  baseUrl: localBaseUrl,
571
569
  fetch: fetch2 = baseFetch,
572
- Request = CustomRequest,
570
+ Request: Request2 = CustomRequest,
573
571
  headers,
574
572
  params = {},
575
573
  parseAs = "json",
@@ -621,7 +619,7 @@ function createClient(clientOptions) {
621
619
  };
622
620
  let id;
623
621
  let options;
624
- let request = new Request(
622
+ let request = new Request2(
625
623
  createFinalURL(schemaPath, { baseUrl: finalBaseUrl, params, querySerializer, pathSerializer }),
626
624
  requestInit
627
625
  );
@@ -651,7 +649,7 @@ function createClient(clientOptions) {
651
649
  id
652
650
  });
653
651
  if (result) {
654
- if (result instanceof Request) {
652
+ if (result instanceof Request2) {
655
653
  request = result;
656
654
  } else if (result instanceof Response) {
657
655
  response = result;
@@ -1023,24 +1021,44 @@ function createApiClient(baseUrl) {
1023
1021
  return createClient({ baseUrl });
1024
1022
  }
1025
1023
 
1024
+ // src/api/endpoints/distribution.ts
1025
+ async function listDistributionRules(api) {
1026
+ const { data, error } = await api.GET("/distribution/rules");
1027
+ if (!data?.content || error) {
1028
+ throw new Error(
1029
+ error?.code ?? error?.error ?? "Failed to list distribution rules"
1030
+ );
1031
+ }
1032
+ return data.content.rules;
1033
+ }
1034
+ async function claimDistributionRule(api, body) {
1035
+ const { data, error } = await api.POST("/distribution/claim", { body });
1036
+ if (!data?.content || error) {
1037
+ throw new Error(
1038
+ error?.code ?? error?.error ?? "Failed to claim distribution rule"
1039
+ );
1040
+ }
1041
+ return data.content;
1042
+ }
1043
+
1026
1044
  // src/api/endpoints/kyc.ts
1027
1045
  async function getKycStatus(api, providerId) {
1028
1046
  const { data, error } = await api.GET("/kyc/status", {
1029
1047
  params: { query: providerId ? { providerId } : {} }
1030
1048
  });
1031
1049
  if (!data?.content || error) {
1032
- throw new Error(error?.error ?? "Failed to get KYC status");
1050
+ throw new Error(error?.code ?? error?.error ?? "Failed to get KYC status");
1033
1051
  }
1034
1052
  return data.content;
1035
1053
  }
1036
1054
  async function getKycProviders(api, country) {
1037
1055
  const { data, error } = await api.GET("/kyc/providers", { params: { query: { country } } });
1038
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to get KYC providers");
1056
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to get KYC providers");
1039
1057
  return data.content;
1040
1058
  }
1041
1059
  async function startKyc(api, body) {
1042
1060
  const { data, error } = await api.POST("/kyc/start", { body });
1043
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to start KYC");
1061
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to start KYC");
1044
1062
  return data.content;
1045
1063
  }
1046
1064
  async function resolveKyc(api, providerId, level = "basic") {
@@ -1062,22 +1080,22 @@ async function pollKycStatus(api, providerId, { intervalMs = 3e3, timeoutMs = 3e
1062
1080
  // src/api/endpoints/ramps.ts
1063
1081
  async function getRampsQuote(api, query) {
1064
1082
  const { data, error } = await api.GET("/ramps/quote", { params: { query } });
1065
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to get ramp quotes");
1083
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to get ramp quotes");
1066
1084
  return data.content;
1067
1085
  }
1068
1086
  async function createOnRamp(api, body) {
1069
1087
  const { data, error } = await api.POST("/ramps/onramp", { body });
1070
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to create onramp");
1088
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to create onramp");
1071
1089
  return data.content;
1072
1090
  }
1073
1091
  async function createOffRamp(api, body) {
1074
1092
  const { data, error } = await api.POST("/ramps/offramp", { body });
1075
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to create offramp");
1093
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to create offramp");
1076
1094
  return data.content;
1077
1095
  }
1078
1096
  async function getRampTransaction(api, txId) {
1079
1097
  const { data, error } = await api.GET("/ramps/transaction/{txId}", { params: { path: { txId } } });
1080
- if (!data?.content || error) throw new Error(error?.error ?? "Failed to get transaction");
1098
+ if (!data?.content || error) throw new Error(error?.code ?? error?.error ?? "Failed to get transaction");
1081
1099
  return data.content;
1082
1100
  }
1083
1101
  async function pollRampTransaction(api, txId, { intervalMs = 5e3, timeoutMs = 6e5 } = {}) {
@@ -1152,34 +1170,6 @@ function generateJti() {
1152
1170
  );
1153
1171
  }
1154
1172
 
1155
- // src/stellar/StellarClient.ts
1156
- var HORIZON_URLS = {
1157
- mainnet: "https://horizon.stellar.org",
1158
- testnet: "https://horizon-testnet.stellar.org"
1159
- };
1160
- var StellarClient = class {
1161
- constructor(config) {
1162
- this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
1163
- }
1164
- async submitTransaction(signedXdr) {
1165
- try {
1166
- const response = await fetch(`${this.horizonUrl}/transactions`, {
1167
- method: "POST",
1168
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
1169
- body: new URLSearchParams({ tx: signedXdr })
1170
- });
1171
- if (!response.ok) {
1172
- const body = await response.json().catch(() => ({}));
1173
- return { success: false, errorCode: body.extras?.result_codes?.transaction ?? "HORIZON_ERROR" };
1174
- }
1175
- const data = await response.json();
1176
- return { success: true, hash: data.hash };
1177
- } catch {
1178
- return { success: false, errorCode: "NETWORK_ERROR" };
1179
- }
1180
- }
1181
- };
1182
-
1183
1173
  // src/storage/web.ts
1184
1174
  var LOG_PREFIX = "[PollarClient:storage]";
1185
1175
  function createMemoryAdapter() {
@@ -1267,6 +1257,57 @@ function defaultStorage(options = {}) {
1267
1257
  return createLocalStorageAdapter(options);
1268
1258
  }
1269
1259
 
1260
+ // src/visibility/noop.ts
1261
+ function createNoopVisibilityProvider() {
1262
+ return {
1263
+ isVisible: () => true,
1264
+ onChange: () => () => {
1265
+ }
1266
+ };
1267
+ }
1268
+
1269
+ // src/visibility/web.ts
1270
+ function createWebVisibilityProvider() {
1271
+ const isVisibleNow = () => typeof document === "undefined" || document.visibilityState === "visible";
1272
+ return {
1273
+ isVisible: isVisibleNow,
1274
+ onChange: (cb) => {
1275
+ if (typeof window === "undefined" || typeof document === "undefined") {
1276
+ return () => {
1277
+ };
1278
+ }
1279
+ let last = isVisibleNow();
1280
+ const handler = () => {
1281
+ const next = isVisibleNow();
1282
+ if (next !== last) {
1283
+ last = next;
1284
+ cb(next);
1285
+ }
1286
+ };
1287
+ document.addEventListener("visibilitychange", handler);
1288
+ window.addEventListener("pageshow", handler);
1289
+ window.addEventListener("pagehide", handler);
1290
+ window.addEventListener("focus", handler);
1291
+ window.addEventListener("blur", handler);
1292
+ return () => {
1293
+ document.removeEventListener("visibilitychange", handler);
1294
+ window.removeEventListener("pageshow", handler);
1295
+ window.removeEventListener("pagehide", handler);
1296
+ window.removeEventListener("focus", handler);
1297
+ window.removeEventListener("blur", handler);
1298
+ };
1299
+ }
1300
+ };
1301
+ }
1302
+
1303
+ // src/visibility/autodetect.ts
1304
+ function defaultVisibilityProvider() {
1305
+ if (typeof document !== "undefined" && typeof window !== "undefined") {
1306
+ return createWebVisibilityProvider();
1307
+ }
1308
+ return createNoopVisibilityProvider();
1309
+ }
1310
+
1270
1311
  // src/types.ts
1271
1312
  var AUTH_ERROR_CODES = {
1272
1313
  SESSION_CREATE_FAILED: "SESSION_CREATE_FAILED",
@@ -1277,6 +1318,7 @@ var AUTH_ERROR_CODES = {
1277
1318
  AUTH_FAILED: "AUTH_FAILED",
1278
1319
  WALLET_CONNECT_FAILED: "WALLET_CONNECT_FAILED",
1279
1320
  WALLET_AUTH_FAILED: "WALLET_AUTH_FAILED",
1321
+ WALLET_RESOLVER_TIMEOUT: "WALLET_RESOLVER_TIMEOUT",
1280
1322
  UNEXPECTED_ERROR: "UNEXPECTED_ERROR"
1281
1323
  };
1282
1324
  var PollarFlowError = class extends Error {
@@ -1667,7 +1709,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1667
1709
  setAuthState({ step: "authenticating" });
1668
1710
  await streamUntilFound(api, clientSessionId, (data2) => data2?.status === "READY", 200, signal);
1669
1711
  const dpopJwk = await deps.getPublicJwk();
1670
- const { data, error } = await api.POST("/auth/login", {
1712
+ const { data } = await api.POST("/auth/login", {
1671
1713
  body: {
1672
1714
  clientSessionId,
1673
1715
  dpopJwk,
@@ -1832,7 +1874,7 @@ async function loginWallet(type, deps) {
1832
1874
  let connectedWallet;
1833
1875
  try {
1834
1876
  setAuthState({ step: "connecting_wallet", walletType: type });
1835
- const adapter = await deps.resolveWalletAdapter(type);
1877
+ const adapter = await withSignal(deps.resolveWalletAdapter(type), signal);
1836
1878
  const available = await withSignal(adapter.isAvailable(), signal);
1837
1879
  if (!available) {
1838
1880
  setAuthState({ step: "wallet_not_installed", walletType: type });
@@ -1869,7 +1911,7 @@ async function loginWallet(type, deps) {
1869
1911
 
1870
1912
  // src/client/client.ts
1871
1913
  var isBrowser = typeof window !== "undefined" && typeof localStorage !== "undefined";
1872
- var RETRIED_HEADER = "X-Pollar-Retried";
1914
+ var REFRESH_SKEW_SECONDS = 60;
1873
1915
  function warnServerSide(method) {
1874
1916
  console.warn(
1875
1917
  `[PollarClient] ${method}() called server-side \u2014 browser APIs unavailable. Use PollarClient only in Client Components.`
@@ -1887,9 +1929,21 @@ var PollarClient = class {
1887
1929
  this._profile = null;
1888
1930
  /** Last `DPoP-Nonce` we saw from a server response. Carried into the next proof. */
1889
1931
  this._dpopNonce = null;
1932
+ /**
1933
+ * Snapshot of each in-flight request's body, taken in `onRequest` before
1934
+ * `fetch()` consumes the stream. Needed because `Request.clone()` throws
1935
+ * once the body is disturbed, so the auto-retry path (DPoP nonce challenge
1936
+ * / 401 refresh) must rebuild the request from scratch instead of cloning.
1937
+ */
1938
+ this._requestBodyCache = /* @__PURE__ */ new WeakMap();
1890
1939
  /** Singleton in-flight refresh — concurrent 401s coalesce into one /auth/refresh call. */
1891
1940
  this._refreshPromise = null;
1892
1941
  this._storageEventHandler = null;
1942
+ /** Updated by the request middleware. Read by the silent-refresh scheduler
1943
+ * to skip proactive refreshes after `maxIdleMs` of no HTTP activity. */
1944
+ this._lastRequestAt = Date.now();
1945
+ this._refreshTimer = null;
1946
+ this._visibilityUnsubscribe = null;
1893
1947
  this._transactionState = null;
1894
1948
  this._transactionStateListeners = /* @__PURE__ */ new Set();
1895
1949
  this._txHistoryState = { step: "idle" };
@@ -1900,15 +1954,32 @@ var PollarClient = class {
1900
1954
  this._authStateListeners = /* @__PURE__ */ new Set();
1901
1955
  this._networkState = { step: "idle" };
1902
1956
  this._networkStateListeners = /* @__PURE__ */ new Set();
1957
+ /**
1958
+ * Latched once the storage adapter degrades. We dedupe (the adapter only
1959
+ * fires once anyway) and use it to replay state to late-subscribers — same
1960
+ * pattern as `onAuthStateChange` replaying `_authState` on subscribe.
1961
+ * Only populated when the SDK constructed the default storage adapter; if
1962
+ * the consumer passes `config.storage`, they own degradation notifications.
1963
+ */
1964
+ this._storageDegraded = null;
1965
+ this._storageDegradeListeners = /* @__PURE__ */ new Set();
1903
1966
  this._walletAdapter = null;
1904
1967
  this._loginController = null;
1905
1968
  this.apiKey = config.apiKey;
1906
1969
  this.id = crypto.randomUUID();
1907
1970
  this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
1908
- this._storage = config.storage ?? defaultStorage(config.onStorageDegrade ? { onDegrade: config.onStorageDegrade } : void 0);
1971
+ this._storage = config.storage ?? defaultStorage({
1972
+ onDegrade: (reason, error) => {
1973
+ config.onStorageDegrade?.(reason, error);
1974
+ this._dispatchStorageDegrade(reason, error);
1975
+ }
1976
+ });
1909
1977
  this._keyManager = config.keyManager ?? defaultKeyManager(this._storage, config.apiKey);
1910
1978
  this._walletAdapterResolver = config.walletAdapter ?? null;
1979
+ this._walletResolverTimeoutMs = config.walletResolverTimeoutMs ?? 5e3;
1911
1980
  this._deviceLabel = config.deviceLabel;
1981
+ this._visibilityProvider = config.visibilityProvider ?? defaultVisibilityProvider();
1982
+ this._maxIdleMs = config.maxIdleMs;
1912
1983
  this._api = createApiClient(this.basePath);
1913
1984
  this._wireMiddlewares();
1914
1985
  this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
@@ -1954,6 +2025,9 @@ var PollarClient = class {
1954
2025
  console.warn("[PollarClient] KeyManager init failed; DPoP unavailable for this session", err);
1955
2026
  }
1956
2027
  await this._restoreSession();
2028
+ this._visibilityUnsubscribe = this._visibilityProvider.onChange((visible) => {
2029
+ if (visible) void this._maybeProactiveRefresh();
2030
+ });
1957
2031
  }
1958
2032
  /** Detach the cross-tab storage listener and abort any in-flight login. */
1959
2033
  destroy() {
@@ -1963,6 +2037,11 @@ var PollarClient = class {
1963
2037
  }
1964
2038
  this._loginController?.abort();
1965
2039
  this._loginController = null;
2040
+ this._clearRefreshTimer();
2041
+ if (this._visibilityUnsubscribe) {
2042
+ this._visibilityUnsubscribe();
2043
+ this._visibilityUnsubscribe = null;
2044
+ }
1966
2045
  }
1967
2046
  // ─── Middlewares (DPoP + auto-refresh) ────────────────────────────────────
1968
2047
  _wireMiddlewares() {
@@ -1970,7 +2049,15 @@ var PollarClient = class {
1970
2049
  this._api.use({
1971
2050
  onRequest: async ({ request }) => {
1972
2051
  request.headers.set("x-pollar-api-key", self.apiKey);
2052
+ self._lastRequestAt = Date.now();
1973
2053
  await self._initialized;
2054
+ if (request.body !== null) {
2055
+ try {
2056
+ self._requestBodyCache.set(request, await request.clone().arrayBuffer());
2057
+ } catch (err) {
2058
+ console.warn("[PollarClient] Could not snapshot request body for retry", err);
2059
+ }
2060
+ }
1974
2061
  const isRefresh = request.url.includes("/auth/refresh");
1975
2062
  if (!isRefresh && self._refreshPromise) await self._refreshPromise;
1976
2063
  if (isRefresh) {
@@ -1993,16 +2080,22 @@ var PollarClient = class {
1993
2080
  const newNonce = response.headers.get("DPoP-Nonce");
1994
2081
  if (newNonce) self._dpopNonce = newNonce;
1995
2082
  if (response.status !== 401) return response;
1996
- if (request.headers.get(RETRIED_HEADER)) return response;
1997
- if (request.url.includes("/auth/refresh")) return response;
1998
2083
  const wwwAuth = response.headers.get("WWW-Authenticate") ?? "";
1999
2084
  const isNonceChallenge = wwwAuth.includes("use_dpop_nonce");
2085
+ if (request.url.includes("/auth/refresh")) {
2086
+ if (isNonceChallenge) return self._retryRequest(request);
2087
+ return response;
2088
+ }
2000
2089
  if (!isNonceChallenge) {
2001
2090
  try {
2002
2091
  await self.refresh();
2003
2092
  } catch {
2004
2093
  return response;
2005
2094
  }
2095
+ const method = request.method.toUpperCase();
2096
+ if (method !== "GET" && method !== "HEAD") {
2097
+ return response;
2098
+ }
2006
2099
  }
2007
2100
  return self._retryRequest(request);
2008
2101
  }
@@ -2026,19 +2119,37 @@ var PollarClient = class {
2026
2119
  }
2027
2120
  }
2028
2121
  async _retryRequest(originalRequest) {
2029
- const clone = originalRequest.clone();
2030
- clone.headers.set(RETRIED_HEADER, "1");
2031
- const accessToken = this._session?.token?.accessToken;
2032
- if (accessToken) {
2033
- const proof = await this._buildProofForRequest(clone, accessToken);
2034
- if (proof) {
2035
- clone.headers.set("Authorization", `DPoP ${accessToken}`);
2036
- clone.headers.set("DPoP", proof);
2037
- } else {
2038
- clone.headers.set("Authorization", `Bearer ${accessToken}`);
2122
+ const headers = new Headers(originalRequest.headers);
2123
+ const isRefresh = originalRequest.url.includes("/auth/refresh");
2124
+ if (isRefresh) {
2125
+ const proof = await this._buildProofForRequest(originalRequest, void 0);
2126
+ headers.delete("Authorization");
2127
+ if (proof) headers.set("DPoP", proof);
2128
+ else headers.delete("DPoP");
2129
+ } else {
2130
+ const accessToken = this._session?.token?.accessToken;
2131
+ if (accessToken) {
2132
+ const proof = await this._buildProofForRequest(originalRequest, accessToken);
2133
+ if (proof) {
2134
+ headers.set("Authorization", `DPoP ${accessToken}`);
2135
+ headers.set("DPoP", proof);
2136
+ } else {
2137
+ headers.set("Authorization", `Bearer ${accessToken}`);
2138
+ }
2039
2139
  }
2040
2140
  }
2041
- return fetch(clone);
2141
+ const cachedBody = this._requestBodyCache.get(originalRequest);
2142
+ const retried = new Request(originalRequest.url, {
2143
+ method: originalRequest.method,
2144
+ headers,
2145
+ body: cachedBody ?? null,
2146
+ credentials: originalRequest.credentials,
2147
+ mode: originalRequest.mode,
2148
+ redirect: originalRequest.redirect,
2149
+ referrer: originalRequest.referrer,
2150
+ integrity: originalRequest.integrity
2151
+ });
2152
+ return fetch(retried);
2042
2153
  }
2043
2154
  // ─── Refresh (race-safe singleton) ───────────────────────────────────────
2044
2155
  /**
@@ -2095,6 +2206,65 @@ var PollarClient = class {
2095
2206
  } catch (err) {
2096
2207
  console.error("[PollarClient] Failed to persist refreshed session", err);
2097
2208
  }
2209
+ this._scheduleNextRefresh();
2210
+ }
2211
+ }
2212
+ // ─── Silent refresh scheduler ────────────────────────────────────────────────
2213
+ /**
2214
+ * Arm a single setTimeout to fire shortly before the current access token
2215
+ * expires. Idempotent — clearing any previous timer first. Safe to call
2216
+ * from any session-write site (initial login, restore-from-storage, after
2217
+ * a successful rotation). No-op if there's no session in memory.
2218
+ *
2219
+ * Browser/RN background-tab throttling makes long-running setTimeouts
2220
+ * unreliable on their own; the `visibilitychange` listener compensates by
2221
+ * re-invoking `_maybeProactiveRefresh` whenever the app comes back to the
2222
+ * foreground, catching any timer that fired late or never fired at all.
2223
+ */
2224
+ _scheduleNextRefresh() {
2225
+ this._clearRefreshTimer();
2226
+ const expiresAt = this._session?.token?.expiresAt;
2227
+ if (typeof expiresAt !== "number") return;
2228
+ const dueInMs = Math.max(0, (expiresAt - Math.floor(Date.now() / 1e3) - REFRESH_SKEW_SECONDS) * 1e3);
2229
+ this._refreshTimer = setTimeout(() => {
2230
+ void this._maybeProactiveRefresh();
2231
+ }, dueInMs);
2232
+ }
2233
+ /**
2234
+ * Decide whether to actually run a refresh right now. Called both from the
2235
+ * scheduler timer and from the visibility-change listener.
2236
+ *
2237
+ * Skip if:
2238
+ * - no session / no RT (nothing to refresh)
2239
+ * - app is hidden — wait for the visibility listener to re-trigger us
2240
+ * - `maxIdleMs` configured and no client request since that window — let
2241
+ * the next reactive 401-refresh handle it whenever the user comes back
2242
+ * - the AT still has more than `REFRESH_SKEW_SECONDS` of life — reschedule
2243
+ *
2244
+ * Otherwise call `refresh()`, which uses the existing in-flight singleton
2245
+ * so we never collide with a reactive 401-triggered refresh. On failure,
2246
+ * `_doRefresh` already calls `_clearSession`, so auth-state listeners see
2247
+ * `step:'idle'` — no extra event dispatch needed here.
2248
+ */
2249
+ async _maybeProactiveRefresh() {
2250
+ if (!this._session?.token?.refreshToken) return;
2251
+ if (!this._visibilityProvider.isVisible()) return;
2252
+ if (this._maxIdleMs !== void 0 && Date.now() - this._lastRequestAt > this._maxIdleMs) return;
2253
+ const expiresAt = this._session.token.expiresAt;
2254
+ if (Math.floor(Date.now() / 1e3) < expiresAt - REFRESH_SKEW_SECONDS) {
2255
+ this._scheduleNextRefresh();
2256
+ return;
2257
+ }
2258
+ try {
2259
+ await this.refresh();
2260
+ } catch (err) {
2261
+ console.warn("[PollarClient] Proactive refresh failed; session cleared", err);
2262
+ }
2263
+ }
2264
+ _clearRefreshTimer() {
2265
+ if (this._refreshTimer !== null) {
2266
+ clearTimeout(this._refreshTimer);
2267
+ this._refreshTimer = null;
2098
2268
  }
2099
2269
  }
2100
2270
  // ─── Auth state ──────────────────────────────────────────────────────────────
@@ -2106,6 +2276,38 @@ var PollarClient = class {
2106
2276
  cb(this._authState);
2107
2277
  return () => this._authStateListeners.delete(cb);
2108
2278
  }
2279
+ /**
2280
+ * Subscribe to persistent-storage degradation (Safari private mode,
2281
+ * sandboxed iframes, quota errors, etc.). The SDK keeps running off
2282
+ * in-memory storage after degrade, but sessions won't survive reload — a
2283
+ * host UI typically wants to show "your session won't be saved" so the
2284
+ * user isn't blindsided after a refresh.
2285
+ *
2286
+ * Fires at most once per client lifetime (the underlying adapter dedupes).
2287
+ * Late subscribers receive the latched state synchronously on subscribe.
2288
+ *
2289
+ * Only fires when the SDK constructs the default storage adapter. If you
2290
+ * pass a custom `config.storage`, wire your own notification path through
2291
+ * that adapter's API — the SDK has no hook into it.
2292
+ */
2293
+ onStorageDegrade(cb) {
2294
+ this._storageDegradeListeners.add(cb);
2295
+ if (this._storageDegraded) {
2296
+ cb(this._storageDegraded.reason, this._storageDegraded.error);
2297
+ }
2298
+ return () => this._storageDegradeListeners.delete(cb);
2299
+ }
2300
+ _dispatchStorageDegrade(reason, error) {
2301
+ if (this._storageDegraded) return;
2302
+ this._storageDegraded = { reason, error };
2303
+ for (const cb of this._storageDegradeListeners) {
2304
+ try {
2305
+ cb(reason, error);
2306
+ } catch (err) {
2307
+ console.error("[PollarClient] onStorageDegrade listener threw", err);
2308
+ }
2309
+ }
2310
+ }
2109
2311
  /** PII (email, names, avatar, providers). Held in memory only — never persisted. */
2110
2312
  getUserProfile() {
2111
2313
  return this._profile;
@@ -2342,10 +2544,16 @@ var PollarClient = class {
2342
2544
  }
2343
2545
  }
2344
2546
  // ─── Transactions ─────────────────────────────────────────────────────────
2547
+ /**
2548
+ * Builds an unsigned XDR. Drives `_setTransactionState` for modal-style UIs
2549
+ * AND returns a {@link BuildOutcome} so headless callers can `await` and
2550
+ * inspect the result without subscribing to state changes.
2551
+ */
2345
2552
  async buildTx(operation, params, options) {
2346
2553
  if (!this._session?.wallet?.publicKey) {
2347
- this._setTransactionState({ step: "error", details: "No wallet connected" });
2348
- return;
2554
+ const details = "No wallet connected";
2555
+ this._setTransactionState({ step: "error", phase: "building", details });
2556
+ return { status: "error", details };
2349
2557
  }
2350
2558
  const body = {
2351
2559
  network: this.getNetwork(),
@@ -2359,39 +2567,194 @@ var PollarClient = class {
2359
2567
  const { data, error } = await this._api.POST("/tx/build", { body });
2360
2568
  if (!error && data?.success && data.content) {
2361
2569
  this._setTransactionState({ step: "built", buildData: data.content });
2362
- } else {
2363
- const details = error?.details;
2364
- this._setTransactionState({ step: "error", ...details && { details } });
2570
+ return { status: "built", buildData: data.content };
2365
2571
  }
2366
- } catch {
2367
- this._setTransactionState({ step: "error" });
2572
+ const details = error?.details;
2573
+ this._setTransactionState({ step: "error", phase: "building", ...details && { details } });
2574
+ return { status: "error", ...details && { details } };
2575
+ } catch (err) {
2576
+ console.error("[PollarClient] buildTx failed", err);
2577
+ this._setTransactionState({ step: "error", phase: "building" });
2578
+ return { status: "error" };
2368
2579
  }
2369
2580
  }
2370
2581
  getWalletType() {
2371
2582
  return this._walletAdapter?.type ?? null;
2372
2583
  }
2373
- async signAndSubmitTx(unsignedXdr) {
2374
- const state = this._transactionState;
2375
- const buildData = state?.step === "built" ? state.buildData : state?.step === "error" ? state.buildData : void 0;
2376
- const stateExtra = buildData ? { buildData } : { external: true };
2377
- this._setTransactionState({ step: "signing", ...stateExtra });
2378
- const accountToSign = this._session?.wallet?.publicKey;
2584
+ /**
2585
+ * Signs the given unsigned XDR and returns the signed XDR.
2586
+ *
2587
+ * - External wallets: signs locally via the wallet adapter.
2588
+ * - Custodial wallets: posts to `/tx/sign`. The backend signs (through
2589
+ * wallet-service or the app's customer-managed adapter) and returns the
2590
+ * signed XDR plus an `idempotencyKey` the caller should echo back to
2591
+ * `submitTx`.
2592
+ *
2593
+ * Drives `_setTransactionState`: emits `signing` while in flight and
2594
+ * `signed` on success (or `error[phase: 'signing']` on failure). `buildData`
2595
+ * is threaded through if the consumer previously called `buildTx`.
2596
+ */
2597
+ async signTx(unsignedXdr) {
2598
+ const buildData = this._currentBuildData();
2599
+ this._setTransactionState({ step: "signing", ...buildData && { buildData } });
2379
2600
  if (this._walletAdapter) {
2601
+ const accountToSign = this._session?.wallet?.publicKey;
2602
+ const signOpts = accountToSign ? { networkPassphrase: this._networkPassphrase(), accountToSign } : { networkPassphrase: this._networkPassphrase() };
2380
2603
  try {
2381
- const signOpts = accountToSign ? { networkPassphrase: this._networkPassphrase(), accountToSign } : { networkPassphrase: this._networkPassphrase() };
2382
2604
  const { signedTxXdr } = await this._walletAdapter.signTransaction(unsignedXdr, signOpts);
2383
- const stellarClient = new StellarClient(this.getNetwork());
2384
- const result = await stellarClient.submitTransaction(signedTxXdr);
2385
- if (result.success) {
2386
- this._setTransactionState({ step: "success", ...stateExtra, hash: result.hash });
2387
- } else {
2388
- this._setTransactionState({ step: "error", ...stateExtra, details: result.errorCode });
2605
+ this._setTransactionState({
2606
+ step: "signed",
2607
+ signedXdr: signedTxXdr,
2608
+ ...buildData && { buildData }
2609
+ });
2610
+ return { status: "signed", signedXdr: signedTxXdr };
2611
+ } catch (err) {
2612
+ const details = err instanceof Error ? err.message : void 0;
2613
+ this._setTransactionState({
2614
+ step: "error",
2615
+ phase: "signing",
2616
+ ...buildData && { buildData },
2617
+ ...details && { details }
2618
+ });
2619
+ return { status: "error", ...details && { details } };
2620
+ }
2621
+ }
2622
+ const publicKey = this._session?.wallet?.publicKey ?? "";
2623
+ try {
2624
+ const { data, error } = await this._api.POST("/tx/sign", {
2625
+ body: { network: this.getNetwork(), publicKey, unsignedXdr }
2626
+ });
2627
+ if (!error && data?.success && data.content?.signedXdr) {
2628
+ const { signedXdr, idempotencyKey } = data.content;
2629
+ this._setTransactionState({
2630
+ step: "signed",
2631
+ signedXdr,
2632
+ submissionToken: idempotencyKey,
2633
+ ...buildData && { buildData }
2634
+ });
2635
+ return { status: "signed", signedXdr, submissionToken: idempotencyKey };
2636
+ }
2637
+ const details = error?.details;
2638
+ this._setTransactionState({
2639
+ step: "error",
2640
+ phase: "signing",
2641
+ ...buildData && { buildData },
2642
+ ...details && { details }
2643
+ });
2644
+ return { status: "error", ...details && { details } };
2645
+ } catch (err) {
2646
+ const details = err instanceof Error ? err.message : void 0;
2647
+ this._setTransactionState({
2648
+ step: "error",
2649
+ phase: "signing",
2650
+ ...buildData && { buildData },
2651
+ ...details && { details }
2652
+ });
2653
+ return { status: "error", ...details && { details } };
2654
+ }
2655
+ }
2656
+ /**
2657
+ * Submits a signed XDR via `/tx/submit` regardless of wallet type
2658
+ * (custodial or external). Routing through sdk-api gives us:
2659
+ * - End-to-end tx_records persistence with full phase lifecycle so the
2660
+ * developer dashboard can show every tx (both custodial and external
2661
+ * wallet flows) at `/apps/:id/monitor/transactions`.
2662
+ * - Idempotency tracking via `submissionToken` (returned by `signTx`).
2663
+ * - A single response shape (SUCCESS / PENDING / FAILED) shared by both
2664
+ * flows — previously external wallets could only return SUCCESS or
2665
+ * error since the direct-to-Horizon path was synchronous.
2666
+ *
2667
+ * The extra hop adds ~50–150 ms vs. the legacy direct-Horizon path; the
2668
+ * persistence + observability win is worth it.
2669
+ *
2670
+ * Drives `_setTransactionState`: emits `submitting` while in flight,
2671
+ * `submitted` on Horizon ack (pending), `success` on ledger confirmation,
2672
+ * or `error[phase: 'submitting']` on failure.
2673
+ */
2674
+ async submitTx(signedXdr, opts) {
2675
+ const buildData = this._currentBuildData();
2676
+ const outcomeExtra = buildData ? { buildData } : {};
2677
+ this._setTransactionState({ step: "submitting", signedXdr, ...buildData && { buildData } });
2678
+ const publicKey = this._session?.wallet?.publicKey ?? "";
2679
+ try {
2680
+ const { data, error } = await this._api.POST("/tx/submit", {
2681
+ body: {
2682
+ network: this.getNetwork(),
2683
+ publicKey,
2684
+ signedXdr,
2685
+ ...opts?.submissionToken && { idempotencyKey: opts.submissionToken }
2389
2686
  }
2390
- } catch {
2391
- this._setTransactionState({ step: "error", ...stateExtra });
2687
+ });
2688
+ if (!error && data?.success && data.content) {
2689
+ const { hash, status: backendStatus, resultCode } = data.content;
2690
+ if (backendStatus === "SUCCESS") {
2691
+ this._setTransactionState({ step: "success", hash, ...buildData && { buildData } });
2692
+ return { status: "success", hash, ...outcomeExtra };
2693
+ }
2694
+ if (backendStatus === "PENDING") {
2695
+ this._setTransactionState({ step: "submitted", hash, ...buildData && { buildData } });
2696
+ return { status: "pending", hash, ...outcomeExtra };
2697
+ }
2698
+ this._setTransactionState({
2699
+ step: "error",
2700
+ phase: "submitting",
2701
+ ...buildData && { buildData },
2702
+ ...resultCode && { details: resultCode }
2703
+ });
2704
+ return {
2705
+ status: "error",
2706
+ hash,
2707
+ ...outcomeExtra,
2708
+ ...resultCode && { details: resultCode, resultCode }
2709
+ };
2392
2710
  }
2393
- return;
2711
+ const details = error?.details;
2712
+ this._setTransactionState({
2713
+ step: "error",
2714
+ phase: "submitting",
2715
+ ...buildData && { buildData },
2716
+ ...details && { details }
2717
+ });
2718
+ return { status: "error", ...outcomeExtra, ...details && { details } };
2719
+ } catch (err) {
2720
+ const details = err instanceof Error ? err.message : void 0;
2721
+ this._setTransactionState({
2722
+ step: "error",
2723
+ phase: "submitting",
2724
+ ...buildData && { buildData },
2725
+ ...details && { details }
2726
+ });
2727
+ return { status: "error", ...outcomeExtra, ...details && { details } };
2394
2728
  }
2729
+ }
2730
+ /**
2731
+ * Signs and submits in one logical step. Returns a {@link SubmitOutcome}.
2732
+ *
2733
+ * - **External wallets**: composes `signTx` + `submitTx` client-side. State
2734
+ * machine sees the full granular sequence `signing → signed → submitting
2735
+ * → success` because the underlying methods each emit.
2736
+ * - **Custodial wallets**: atomic `/tx/sign-and-send` round-trip. State
2737
+ * machine emits the compound `signing-submitting` step (the SDK can't
2738
+ * observe when one phase ends and the next begins inside that single
2739
+ * backend call) and then transitions to `submitted` (Horizon ack only) or
2740
+ * `success` (ledger-confirmed), or `error[phase: 'signing-submitting']`.
2741
+ */
2742
+ async signAndSubmitTx(unsignedXdr) {
2743
+ if (this._walletAdapter) {
2744
+ const signed = await this.signTx(unsignedXdr);
2745
+ if (signed.status === "error") {
2746
+ const buildData2 = this._currentBuildData();
2747
+ return {
2748
+ status: "error",
2749
+ ...buildData2 && { buildData: buildData2 },
2750
+ ...signed.details && { details: signed.details }
2751
+ };
2752
+ }
2753
+ return this.submitTx(signed.signedXdr);
2754
+ }
2755
+ const buildData = this._currentBuildData();
2756
+ const outcomeExtra = buildData ? { buildData } : {};
2757
+ this._setTransactionState({ step: "signing-submitting", ...buildData && { buildData } });
2395
2758
  const body = {
2396
2759
  network: this.getNetwork(),
2397
2760
  publicKey: this._session?.wallet?.publicKey ?? "",
@@ -2400,14 +2763,128 @@ var PollarClient = class {
2400
2763
  try {
2401
2764
  const { data, error } = await this._api.POST("/tx/sign-and-send", { body });
2402
2765
  if (!error && data?.success && data.content?.hash) {
2403
- this._setTransactionState({ step: "success", ...stateExtra, hash: data.content.hash });
2404
- } else {
2405
- const details = error?.details;
2406
- this._setTransactionState({ step: "error", ...stateExtra, ...details && { details } });
2766
+ const {
2767
+ hash,
2768
+ status: backendStatus,
2769
+ resultCode
2770
+ } = data.content;
2771
+ if (backendStatus === "SUCCESS") {
2772
+ this._setTransactionState({ step: "success", hash, ...buildData && { buildData } });
2773
+ return { status: "success", hash, ...outcomeExtra };
2774
+ }
2775
+ if (backendStatus === "PENDING") {
2776
+ this._setTransactionState({ step: "submitted", hash, ...buildData && { buildData } });
2777
+ return { status: "pending", hash, ...outcomeExtra };
2778
+ }
2779
+ this._setTransactionState({
2780
+ step: "error",
2781
+ phase: "signing-submitting",
2782
+ ...buildData && { buildData },
2783
+ ...resultCode && { details: resultCode }
2784
+ });
2785
+ return {
2786
+ status: "error",
2787
+ hash,
2788
+ ...outcomeExtra,
2789
+ ...resultCode && { details: resultCode, resultCode }
2790
+ };
2407
2791
  }
2408
- } catch {
2409
- this._setTransactionState({ step: "error", ...stateExtra });
2792
+ const details = error?.details;
2793
+ this._setTransactionState({
2794
+ step: "error",
2795
+ phase: "signing-submitting",
2796
+ ...buildData && { buildData },
2797
+ ...details && { details }
2798
+ });
2799
+ return { status: "error", ...outcomeExtra, ...details && { details } };
2800
+ } catch (err) {
2801
+ const details = err instanceof Error ? err.message : void 0;
2802
+ this._setTransactionState({
2803
+ step: "error",
2804
+ phase: "signing-submitting",
2805
+ ...buildData && { buildData },
2806
+ ...details && { details }
2807
+ });
2808
+ return { status: "error", ...outcomeExtra, ...details && { details } };
2809
+ }
2810
+ }
2811
+ /**
2812
+ * One-shot: build → sign → submit, returning the final {@link SubmitOutcome}.
2813
+ *
2814
+ * - **External wallets**: composes `buildTx` + `signAndSubmitTx` client-side.
2815
+ * State machine sees the full granular sequence (`building → built →
2816
+ * signing → signed → submitting → success`) because each composed call
2817
+ * emits its own transitions.
2818
+ * - **Custodial wallets**: single round-trip to `/tx/build-sign-submit`. The
2819
+ * signed XDR never leaves the backend. State machine emits the compound
2820
+ * `building-signing-submitting` step (the SDK can't observe individual
2821
+ * phase boundaries inside one atomic call) and then transitions to
2822
+ * `submitted` / `success` / `error[phase: 'building-signing-submitting']`.
2823
+ *
2824
+ * If you need granular UI feedback for custodial flows (separate
2825
+ * "Building…", "Signing…", "Submitting…" indicators), call `buildTx`,
2826
+ * `signTx`, and `submitTx` separately instead.
2827
+ */
2828
+ async buildAndSignAndSubmitTx(operation, params, options) {
2829
+ if (this._walletAdapter) {
2830
+ const built = await this.buildTx(operation, params, options);
2831
+ if (built.status === "error") {
2832
+ return { status: "error", ...built.details && { details: built.details } };
2833
+ }
2834
+ return this.signAndSubmitTx(built.buildData.unsignedXdr);
2410
2835
  }
2836
+ if (!this._session?.wallet?.publicKey) {
2837
+ this._setTransactionState({ step: "error", phase: "building-signing-submitting", details: "No wallet connected" });
2838
+ return { status: "error", details: "No wallet connected" };
2839
+ }
2840
+ this._setTransactionState({ step: "building-signing-submitting" });
2841
+ try {
2842
+ const { data, error } = await this._api.POST("/tx/build-sign-submit", {
2843
+ body: {
2844
+ network: this.getNetwork(),
2845
+ publicKey: this._session.wallet.publicKey,
2846
+ operation,
2847
+ params,
2848
+ options: options ?? {}
2849
+ }
2850
+ });
2851
+ if (!error && data?.success && data.content) {
2852
+ const { hash, status: backendStatus, resultCode } = data.content;
2853
+ if (backendStatus === "SUCCESS") {
2854
+ this._setTransactionState({ step: "success", hash });
2855
+ return { status: "success", hash };
2856
+ }
2857
+ if (backendStatus === "PENDING") {
2858
+ this._setTransactionState({ step: "submitted", hash });
2859
+ return { status: "pending", hash };
2860
+ }
2861
+ this._setTransactionState({
2862
+ step: "error",
2863
+ phase: "building-signing-submitting",
2864
+ ...resultCode && { details: resultCode }
2865
+ });
2866
+ return { status: "error", hash, ...resultCode && { details: resultCode, resultCode } };
2867
+ }
2868
+ const details = error?.details;
2869
+ this._setTransactionState({
2870
+ step: "error",
2871
+ phase: "building-signing-submitting",
2872
+ ...details && { details }
2873
+ });
2874
+ return { status: "error", ...details && { details } };
2875
+ } catch (err) {
2876
+ const details = err instanceof Error ? err.message : void 0;
2877
+ this._setTransactionState({
2878
+ step: "error",
2879
+ phase: "building-signing-submitting",
2880
+ ...details && { details }
2881
+ });
2882
+ return { status: "error", ...details && { details } };
2883
+ }
2884
+ }
2885
+ /** Alias for {@link buildAndSignAndSubmitTx} — shorter "just do the thing" name. */
2886
+ async runTx(operation, params, options) {
2887
+ return this.buildAndSignAndSubmitTx(operation, params, options);
2411
2888
  }
2412
2889
  // ─── App config ───────────────────────────────────────────────────────────
2413
2890
  async getAppConfig() {
@@ -2451,6 +2928,13 @@ var PollarClient = class {
2451
2928
  pollRampTransaction(txId, opts) {
2452
2929
  return pollRampTransaction(this._api, txId, opts);
2453
2930
  }
2931
+ // ─── Distribution ─────────────────────────────────────────────────────────
2932
+ listDistributionRules() {
2933
+ return listDistributionRules(this._api);
2934
+ }
2935
+ claimDistributionRule(body) {
2936
+ return claimDistributionRule(this._api, body);
2937
+ }
2454
2938
  _setTxHistoryState(next) {
2455
2939
  this._txHistoryState = next;
2456
2940
  for (const cb of this._txHistoryStateListeners) cb(next);
@@ -2489,7 +2973,22 @@ var PollarClient = class {
2489
2973
  */
2490
2974
  async _resolveWalletAdapter(id) {
2491
2975
  if (this._walletAdapterResolver) {
2492
- return Promise.resolve(this._walletAdapterResolver(id));
2976
+ const timeoutMs = this._walletResolverTimeoutMs;
2977
+ let timeoutHandle;
2978
+ const timeoutPromise = new Promise((_, reject) => {
2979
+ timeoutHandle = setTimeout(() => {
2980
+ reject(
2981
+ Object.assign(new Error(`[PollarClient] Wallet adapter resolver for "${id}" timed out after ${timeoutMs}ms`), {
2982
+ code: AUTH_ERROR_CODES.WALLET_RESOLVER_TIMEOUT
2983
+ })
2984
+ );
2985
+ }, timeoutMs);
2986
+ });
2987
+ try {
2988
+ return await Promise.race([Promise.resolve(this._walletAdapterResolver(id)), timeoutPromise]);
2989
+ } finally {
2990
+ if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
2991
+ }
2493
2992
  }
2494
2993
  if (id === "freighter" /* FREIGHTER */) return new FreighterAdapter();
2495
2994
  if (id === "albedo" /* ALBEDO */) return new AlbedoAdapter();
@@ -2503,6 +3002,16 @@ var PollarClient = class {
2503
3002
  this._setAuthState({ step: "idle" });
2504
3003
  return;
2505
3004
  }
3005
+ if (error instanceof Error && error.code === AUTH_ERROR_CODES.WALLET_RESOLVER_TIMEOUT) {
3006
+ console.error("[PollarClient]", error.message);
3007
+ this._setAuthState({
3008
+ step: "error",
3009
+ previousStep: this._authState.step,
3010
+ message: error.message,
3011
+ errorCode: AUTH_ERROR_CODES.WALLET_RESOLVER_TIMEOUT
3012
+ });
3013
+ return;
3014
+ }
2506
3015
  console.error("[PollarClient] Unexpected error in auth flow", error);
2507
3016
  this._setAuthState({
2508
3017
  step: "error",
@@ -2524,6 +3033,7 @@ var PollarClient = class {
2524
3033
  }
2525
3034
  console.info("[PollarClient] Session restored from storage");
2526
3035
  this._setAuthState({ step: "authenticated", session: this._session });
3036
+ this._scheduleNextRefresh();
2527
3037
  } else {
2528
3038
  console.info("[PollarClient] No session in storage");
2529
3039
  }
@@ -2550,9 +3060,11 @@ var PollarClient = class {
2550
3060
  }
2551
3061
  await writeStorage(this._storage, this.apiKeyHash, persisted);
2552
3062
  this._setAuthState({ step: "authenticated", session: persisted });
3063
+ this._scheduleNextRefresh();
2553
3064
  }
2554
3065
  async _clearSession() {
2555
3066
  console.info("[PollarClient] Session cleared");
3067
+ this._clearRefreshTimer();
2556
3068
  this._session = null;
2557
3069
  this._profile = null;
2558
3070
  this._walletAdapter = null;
@@ -2585,6 +3097,46 @@ var PollarClient = class {
2585
3097
  console.info(`[PollarClient] transaction:${next.step}`);
2586
3098
  for (const cb of this._transactionStateListeners) cb(next);
2587
3099
  }
3100
+ /**
3101
+ * Threads `buildData` through state transitions. When the user has already
3102
+ * called `buildTx`, every subsequent state (signing, signed, submitting,
3103
+ * submitted, success, error) should carry the build summary so modal UIs
3104
+ * can keep showing "Send 5 USDC to G..." through the whole flow.
3105
+ */
3106
+ _currentBuildData() {
3107
+ const s = this._transactionState;
3108
+ if (!s) return void 0;
3109
+ if ("buildData" in s && s.buildData) return s.buildData;
3110
+ return void 0;
3111
+ }
3112
+ };
3113
+
3114
+ // src/stellar/StellarClient.ts
3115
+ var HORIZON_URLS = {
3116
+ mainnet: "https://horizon.stellar.org",
3117
+ testnet: "https://horizon-testnet.stellar.org"
3118
+ };
3119
+ var StellarClient = class {
3120
+ constructor(config) {
3121
+ this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
3122
+ }
3123
+ async submitTransaction(signedXdr) {
3124
+ try {
3125
+ const response = await fetch(`${this.horizonUrl}/transactions`, {
3126
+ method: "POST",
3127
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3128
+ body: new URLSearchParams({ tx: signedXdr })
3129
+ });
3130
+ if (!response.ok) {
3131
+ const body = await response.json().catch(() => ({}));
3132
+ return { success: false, errorCode: body.extras?.result_codes?.transaction ?? "HORIZON_ERROR" };
3133
+ }
3134
+ const data = await response.json();
3135
+ return { success: true, hash: data.hash };
3136
+ } catch {
3137
+ return { success: false, errorCode: "NETWORK_ERROR" };
3138
+ }
3139
+ }
2588
3140
  };
2589
3141
 
2590
3142
  // src/index.rn.ts
@@ -2606,6 +3158,7 @@ exports.WalletType = WalletType;
2606
3158
  exports.WebCryptoKeyManager = WebCryptoKeyManager;
2607
3159
  exports.buildProof = buildProof;
2608
3160
  exports.canonicalEcJwk = canonicalEcJwk;
3161
+ exports.claimDistributionRule = claimDistributionRule;
2609
3162
  exports.computeJwkThumbprint = computeJwkThumbprint;
2610
3163
  exports.createLocalStorageAdapter = createLocalStorageAdapter;
2611
3164
  exports.createMemoryAdapter = createMemoryAdapter;
@@ -2618,6 +3171,7 @@ exports.getKycStatus = getKycStatus;
2618
3171
  exports.getRampTransaction = getRampTransaction;
2619
3172
  exports.getRampsQuote = getRampsQuote;
2620
3173
  exports.isValidSession = isValidSession;
3174
+ exports.listDistributionRules = listDistributionRules;
2621
3175
  exports.normalizeHtu = normalizeHtu;
2622
3176
  exports.pollKycStatus = pollKycStatus;
2623
3177
  exports.pollRampTransaction = pollRampTransaction;