@bounded-sh/client 0.0.59 → 0.0.60

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.
Files changed (54) hide show
  1. package/dist/{email-auth-provider-N-dfsNnE.js → email-auth-provider-B5yIZe1l.js} +2 -2
  2. package/dist/{email-auth-provider-N-dfsNnE.js.map → email-auth-provider-B5yIZe1l.js.map} +1 -1
  3. package/dist/{email-auth-provider-BSw9FRjg.js → email-auth-provider-BCEdcnRu.js} +2 -2
  4. package/dist/{email-auth-provider-BSw9FRjg.js.map → email-auth-provider-BCEdcnRu.js.map} +1 -1
  5. package/dist/{email-auth-provider-deB_ADc5.mjs → email-auth-provider-BF-vvbAt.mjs} +2 -2
  6. package/dist/{email-auth-provider-deB_ADc5.mjs.map → email-auth-provider-BF-vvbAt.mjs.map} +1 -1
  7. package/dist/{email-auth-provider-DhAMOILG.mjs → email-auth-provider-nBv7mW9q.mjs} +2 -2
  8. package/dist/{email-auth-provider-DhAMOILG.mjs.map → email-auth-provider-nBv7mW9q.mjs.map} +1 -1
  9. package/dist/{guest-auth-provider-BFodq9Rc.js → guest-auth-provider-CZ-CpNhD.js} +2 -2
  10. package/dist/{guest-auth-provider-BFodq9Rc.js.map → guest-auth-provider-CZ-CpNhD.js.map} +1 -1
  11. package/dist/{guest-auth-provider-Czl48mOP.js → guest-auth-provider-CrRreOyf.js} +2 -2
  12. package/dist/{guest-auth-provider-Czl48mOP.js.map → guest-auth-provider-CrRreOyf.js.map} +1 -1
  13. package/dist/{guest-auth-provider-D9wbogwi.mjs → guest-auth-provider-DFNAHmtY.mjs} +2 -2
  14. package/dist/{guest-auth-provider-B78Ugfar.mjs.map → guest-auth-provider-DFNAHmtY.mjs.map} +1 -1
  15. package/dist/{guest-auth-provider-B78Ugfar.mjs → guest-auth-provider-DGvQgIwm.mjs} +2 -2
  16. package/dist/{guest-auth-provider-D9wbogwi.mjs.map → guest-auth-provider-DGvQgIwm.mjs.map} +1 -1
  17. package/dist/{index-SHt4VZZV.mjs → index-CQtGkKVd.mjs} +151 -43
  18. package/dist/index-CQtGkKVd.mjs.map +1 -0
  19. package/dist/{index-uu8hKP-p.js → index-iu8vB4ye.js} +152 -42
  20. package/dist/index-iu8vB4ye.js.map +1 -0
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +1 -1
  25. package/dist/{index.native-D48QQ0-q.js → index.native-CDfx0N6p.js} +151 -41
  26. package/dist/index.native-CDfx0N6p.js.map +1 -0
  27. package/dist/{index.native-D-8IAEy-.mjs → index.native-Crfn_NOB.mjs} +150 -42
  28. package/dist/index.native-Crfn_NOB.mjs.map +1 -0
  29. package/dist/index.native.d.ts +2 -2
  30. package/dist/index.native.js +3 -1
  31. package/dist/index.native.js.map +1 -1
  32. package/dist/index.native.mjs +1 -1
  33. package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs → injected-evm-wallet-provider-Bi9G3X35.mjs} +2 -2
  34. package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs.map → injected-evm-wallet-provider-Bi9G3X35.mjs.map} +1 -1
  35. package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js → injected-evm-wallet-provider-CGa692L1.js} +2 -2
  36. package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js.map → injected-evm-wallet-provider-CGa692L1.js.map} +1 -1
  37. package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs → injected-evm-wallet-provider-D8lYGcM2.mjs} +2 -2
  38. package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs.map → injected-evm-wallet-provider-D8lYGcM2.mjs.map} +1 -1
  39. package/dist/{injected-evm-wallet-provider-CItxws1D.js → injected-evm-wallet-provider-LTWiACl5.js} +2 -2
  40. package/dist/{injected-evm-wallet-provider-CItxws1D.js.map → injected-evm-wallet-provider-LTWiACl5.js.map} +1 -1
  41. package/dist/{injected-wallet-provider-CNYAynIB.mjs → injected-wallet-provider-CcyA5JBS.mjs} +2 -2
  42. package/dist/{injected-wallet-provider-CNYAynIB.mjs.map → injected-wallet-provider-CcyA5JBS.mjs.map} +1 -1
  43. package/dist/{injected-wallet-provider-CVeZgfEt.js → injected-wallet-provider-DYZFxIdd.js} +2 -2
  44. package/dist/{injected-wallet-provider-CVeZgfEt.js.map → injected-wallet-provider-DYZFxIdd.js.map} +1 -1
  45. package/dist/{injected-wallet-provider-CmXqreEE.js → injected-wallet-provider-TPx8Wzvq.js} +2 -2
  46. package/dist/{injected-wallet-provider-CmXqreEE.js.map → injected-wallet-provider-TPx8Wzvq.js.map} +1 -1
  47. package/dist/{injected-wallet-provider-BihII94U.mjs → injected-wallet-provider-kx8tcMKM.mjs} +2 -2
  48. package/dist/{injected-wallet-provider-BihII94U.mjs.map → injected-wallet-provider-kx8tcMKM.mjs.map} +1 -1
  49. package/dist/onramp.d.ts +51 -9
  50. package/package.json +2 -2
  51. package/dist/index-SHt4VZZV.mjs.map +0 -1
  52. package/dist/index-uu8hKP-p.js.map +0 -1
  53. package/dist/index.native-D-8IAEy-.mjs.map +0 -1
  54. package/dist/index.native-D48QQ0-q.js.map +0 -1
@@ -4536,7 +4536,7 @@ async function getAuthProvider(config) {
4536
4536
  // manager as legacy email sessions. Human login should be started
4537
4537
  // with loginWithRedirect(); this provider is kept as the restore and
4538
4538
  // logout surface for those sessions.
4539
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-N-dfsNnE.js'); })).EmailAuthProvider();
4539
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-B5yIZe1l.js'); })).EmailAuthProvider();
4540
4540
  break;
4541
4541
  case "wallet":
4542
4542
  case "phantom": {
@@ -4555,7 +4555,7 @@ async function getAuthProvider(config) {
4555
4555
  if (!isWalletLoginEnabled(config)) {
4556
4556
  throw walletLoginNotEnabledError(authMethod);
4557
4557
  }
4558
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CVeZgfEt.js'); });
4558
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-DYZFxIdd.js'); });
4559
4559
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
4560
4560
  break;
4561
4561
  }
@@ -4568,7 +4568,7 @@ async function getAuthProvider(config) {
4568
4568
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
4569
4569
  // to point at a specific wallet or set chainId/domain.
4570
4570
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
4571
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-CItxws1D.js'); });
4571
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-LTWiACl5.js'); });
4572
4572
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
4573
4573
  break;
4574
4574
  }
@@ -4596,7 +4596,7 @@ async function getAuthProvider(config) {
4596
4596
  break;
4597
4597
  case "guest":
4598
4598
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
4599
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BFodq9Rc.js'); })).GuestAuthProvider();
4599
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-CZ-CpNhD.js'); })).GuestAuthProvider();
4600
4600
  break;
4601
4601
  }
4602
4602
  if (!currentAuthProvider) {
@@ -4652,7 +4652,7 @@ async function signInAnonymously() {
4652
4652
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
4653
4653
  // the main bundle for data-only apps. Specifier is a static string literal
4654
4654
  // so bundle-splitting can see it.
4655
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BFodq9Rc.js'); })).GuestAuthProvider();
4655
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-CZ-CpNhD.js'); })).GuestAuthProvider();
4656
4656
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
4657
4657
  const commit = (user) => {
4658
4658
  // No await/interleaving inside this block: provider/method/config/marker
@@ -4803,7 +4803,7 @@ async function loginWithWallet(options) {
4803
4803
  ? Object.assign(Object.assign({}, base), { getProvider: options.getProvider }) : base;
4804
4804
  const rpcUrl = normalizeSolanaRpcUrl(cfg.rpcUrl);
4805
4805
  const network = getConfiguredSolanaNetwork(cfg);
4806
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CVeZgfEt.js'); });
4806
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-DYZFxIdd.js'); });
4807
4807
  let provider = new InjectedWalletProvider(injectedConfig, rpcUrl, network);
4808
4808
  if (coreConfig.chain === 'offchain')
4809
4809
  provider = new OffchainAuthProvider(provider);
@@ -4875,7 +4875,7 @@ function getCurrentAuthMethod() {
4875
4875
  // allowlist here: on a trusted origin we end the issuer session with a
4876
4876
  // top-level bounce; anywhere else (custom domains, http dev) we keep the
4877
4877
  // local-only logout instead of stranding the user on the issuer origin.
4878
- function issuerLogoutBounceUrl(issuerBase) {
4878
+ function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
4879
4879
  if (typeof window === 'undefined' || typeof document === 'undefined')
4880
4880
  return null; // RN/Node: no shared cookie jar to clear this way
4881
4881
  try {
@@ -4898,12 +4898,27 @@ function issuerLogoutBounceUrl(issuerBase) {
4898
4898
  if (!trusted)
4899
4899
  return null;
4900
4900
  const base = issuerBase.replace(/\/$/, '');
4901
- return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}`;
4901
+ // #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
4902
+ // session + app + registered origin. Without it the issuer safely falls back to its own
4903
+ // origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
4904
+ const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
4905
+ return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
4902
4906
  }
4903
4907
  catch (_b) {
4904
4908
  return null;
4905
4909
  }
4906
4910
  }
4911
+ // #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
4912
+ // against any `catch` so a revokeSession failure can never be swallowed, and a
4913
+ // missing id token is the one benign absence worth tolerating here.
4914
+ function idTokenHintOrNull(sm) {
4915
+ try {
4916
+ return sm.getIdToken();
4917
+ }
4918
+ catch (_a) {
4919
+ return null; /* no id token to hint with */
4920
+ }
4921
+ }
4907
4922
  async function logout$1(options) {
4908
4923
  // Re-initialize the provider if it was cleared by a prior logout — the exact mirror
4909
4924
  // of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
@@ -4928,6 +4943,9 @@ async function logout$1(options) {
4928
4943
  const sm = core.getActiveSessionManager();
4929
4944
  const refreshToken = sm.getRefreshToken();
4930
4945
  const sessionIssuer = sm.getIssuer();
4946
+ // #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
4947
+ // can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
4948
+ const idTokenHint = idTokenHintOrNull(sm);
4931
4949
  if (refreshToken)
4932
4950
  await core.revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
4933
4951
  // Hosted sessions ('email' covers the whole hosted surface: email OTP +
@@ -4948,7 +4966,7 @@ async function logout$1(options) {
4948
4966
  const issuerBase = sessionIssuer
4949
4967
  || (await core.getConfig()).humanAuthApiUrl
4950
4968
  || 'https://auth.bounded.sh';
4951
- const bounce = issuerLogoutBounceUrl(issuerBase);
4969
+ const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
4952
4970
  // Local state is already cleared, so even if the navigation is stopped
4953
4971
  // (tab close, extension) the app-side logout has fully happened.
4954
4972
  if (bounce)
@@ -7384,30 +7402,117 @@ async function signSolanaMessageViaTurnkey(message) {
7384
7402
  // session is bound server-side to the caller's verified Bounded JWT, and the
7385
7403
  // destination defaults to that user's embedded wallet.
7386
7404
  //
7405
+ // Amount semantics:
7406
+ // amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
7407
+ // minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
7408
+ // worst-fee payment method (card) and pads by `slippagePad` (default 1%),
7409
+ // so cheaper methods overshoot the floor rather than undershooting it. The
7410
+ // floor holds unless the market moves more than the pad while the user sits
7411
+ // on the checkout screen - which is why completion is VERIFIED: after the
7412
+ // popup closes we poll the platform's status leg and resolve with the
7413
+ // actual filled amount when Coinbase reports it.
7414
+ //
7387
7415
  // Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
7388
7416
  // local dev origin the session endpoint does not exist; we fail with a clear
7389
7417
  // error rather than guessing a host.
7390
7418
  function hasDOM() {
7391
7419
  return typeof window !== 'undefined' && typeof document !== 'undefined';
7392
7420
  }
7421
+ async function authedFetch(path, init) {
7422
+ const idToken = await getIdToken();
7423
+ if (!idToken)
7424
+ throw new Error('onramp: no active session - call login() first');
7425
+ return fetch(`${window.location.origin}${path}`, Object.assign(Object.assign({}, init), { headers: Object.assign(Object.assign({}, init === null || init === void 0 ? void 0 : init.headers), { Authorization: `Bearer ${idToken}` }) }));
7426
+ }
7427
+ async function readError(res) {
7428
+ var _a;
7429
+ try {
7430
+ return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
7431
+ }
7432
+ catch (_b) {
7433
+ return '';
7434
+ }
7435
+ }
7436
+ function sessionBody(options, extra) {
7437
+ return JSON.stringify(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (options.asset ? { asset: options.asset } : {})), (options.address ? { address: options.address } : {})), (options.amountUsd !== undefined ? { amountUsd: options.amountUsd } : {})), (options.minAmountOut !== undefined ? { minAmountOut: options.minAmountOut } : {})), (options.slippagePad !== undefined ? { slippagePad: options.slippagePad } : {})), extra));
7438
+ }
7439
+ function resolveAddress(options) {
7440
+ var _a, _b;
7441
+ const user = getCurrentUser();
7442
+ const address = (_b = (_a = options.address) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.address) !== null && _b !== void 0 ? _b : undefined;
7443
+ if (!address) {
7444
+ throw new Error(user
7445
+ ? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
7446
+ : 'onramp: no signed-in user - call login() first or pass { address }');
7447
+ }
7448
+ return address;
7449
+ }
7393
7450
  /**
7394
- * Open Coinbase Onramp for the signed-in user. Resolves when the popup closes.
7451
+ * Preview a quote (fees + exact/floor output) WITHOUT opening any window or
7452
+ * consuming a session. Show this to the user, then call onramp() to check out.
7453
+ */
7454
+ async function onrampQuote(options = {}) {
7455
+ if (!hasDOM())
7456
+ throw new Error('onrampQuote is web-only');
7457
+ resolveAddress(options);
7458
+ const res = await authedFetch('/__bounded/onramp/session', {
7459
+ method: 'POST',
7460
+ headers: { 'content-type': 'application/json' },
7461
+ body: sessionBody(options, { quoteOnly: true }),
7462
+ });
7463
+ if (!res.ok)
7464
+ throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
7465
+ return (await res.json());
7466
+ }
7467
+ /** The signed-in user's recent onramp transactions (newest first). */
7468
+ async function onrampStatus(sinceMs) {
7469
+ if (!hasDOM())
7470
+ throw new Error('onrampStatus is web-only');
7471
+ const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
7472
+ if (!res.ok)
7473
+ throw new Error(`onrampStatus: failed (${res.status})`);
7474
+ return (await res.json()).transactions;
7475
+ }
7476
+ function isSuccess(t) {
7477
+ return /SUCCESS|COMPLETED/i.test(t.status);
7478
+ }
7479
+ function isPending(t) {
7480
+ return /PROGRESS|PENDING|CREATED/i.test(t.status);
7481
+ }
7482
+ async function verifyCompletion(openedAt, asset, timeoutMs) {
7483
+ const deadline = Date.now() + timeoutMs;
7484
+ // 60s of clock-skew slack on `since`: Coinbase stamps server-side.
7485
+ const since = openedAt - 60000;
7486
+ for (;;) {
7487
+ let sawPending = false;
7488
+ try {
7489
+ const txs = await onrampStatus(since);
7490
+ const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
7491
+ if (done)
7492
+ return done;
7493
+ sawPending = txs.some(isPending);
7494
+ }
7495
+ catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
7496
+ // No completed fill and nothing in flight: one grace poll cycle still
7497
+ // runs (Coinbase's record can lag the redirect), but don't burn the
7498
+ // full window when the user plainly abandoned checkout.
7499
+ if (Date.now() >= deadline)
7500
+ return null;
7501
+ if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
7502
+ return null;
7503
+ await new Promise((r) => setTimeout(r, 3000));
7504
+ }
7505
+ }
7506
+ /**
7507
+ * Open Coinbase Onramp for the signed-in user. Resolves after the window
7508
+ * closes, with completion VERIFIED against Coinbase when possible.
7395
7509
  * Call from a click handler - popup blockers require user activation.
7396
7510
  */
7397
7511
  async function onramp(options = {}) {
7398
- var _a, _b, _c;
7512
+ var _a;
7399
7513
  if (!hasDOM())
7400
7514
  throw new Error('onramp() is web-only');
7401
- const user = getCurrentUser();
7402
- const address = (_b = (_a = options.address) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.address) !== null && _b !== void 0 ? _b : undefined;
7403
- if (!address) {
7404
- throw new Error(user
7405
- ? 'onramp(): the signed-in account has no wallet address - pass { address } explicitly'
7406
- : 'onramp(): no signed-in user - call login() first or pass { address }');
7407
- }
7408
- const idToken = await getIdToken();
7409
- if (!idToken)
7410
- throw new Error('onramp(): no active session - call login() first');
7515
+ resolveAddress(options);
7411
7516
  // Open the window synchronously so the click's user-activation window is not
7412
7517
  // consumed by the fetch below (same hard-won pattern as loginWithPopup).
7413
7518
  let popup = null;
@@ -7420,21 +7525,17 @@ async function onramp(options = {}) {
7420
7525
  }
7421
7526
  let session;
7422
7527
  try {
7423
- const res = await fetch(`${window.location.origin}/__bounded/onramp/session`, {
7528
+ const res = await authedFetch('/__bounded/onramp/session', {
7424
7529
  method: 'POST',
7425
- headers: { Authorization: `Bearer ${idToken}`, 'content-type': 'application/json' },
7426
- body: JSON.stringify(Object.assign(Object.assign(Object.assign({}, (options.asset ? { asset: options.asset } : {})), (options.address ? { address: options.address } : {})), (options.amountUsd !== undefined ? { amountUsd: options.amountUsd } : {}))),
7530
+ headers: { 'content-type': 'application/json' },
7531
+ body: sessionBody(options),
7427
7532
  });
7428
7533
  if (!res.ok) {
7429
- let detail = '';
7430
- try {
7431
- detail = (_c = (await res.json()).error) !== null && _c !== void 0 ? _c : '';
7432
- }
7433
- catch ( /* keep status only */_d) { /* keep status only */ }
7534
+ const detail = await readError(res);
7434
7535
  if (res.status === 404) {
7435
- throw new Error('onramp(): session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
7536
+ throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
7436
7537
  }
7437
- throw new Error(`onramp(): session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
7538
+ throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
7438
7539
  }
7439
7540
  session = (await res.json());
7440
7541
  }
@@ -7442,40 +7543,47 @@ async function onramp(options = {}) {
7442
7543
  try {
7443
7544
  popup === null || popup === void 0 ? void 0 : popup.close();
7444
7545
  }
7445
- catch ( /* ignore */_e) { /* ignore */ }
7546
+ catch ( /* ignore */_b) { /* ignore */ }
7446
7547
  throw error;
7447
7548
  }
7549
+ const openedAt = Date.now();
7550
+ const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
7448
7551
  if (popup) {
7449
7552
  try {
7450
7553
  popup.location.replace(session.url);
7451
7554
  }
7452
- catch (_f) {
7555
+ catch (_c) {
7453
7556
  try {
7454
7557
  popup.location.href = session.url;
7455
7558
  }
7456
- catch (_g) {
7559
+ catch (_d) {
7457
7560
  try {
7458
7561
  popup.close();
7459
7562
  }
7460
- catch ( /* ignore */_h) { /* ignore */ }
7461
- throw new Error('onramp(): popup failed to navigate - retry with { newTab: true }');
7563
+ catch ( /* ignore */_e) { /* ignore */ }
7564
+ throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
7462
7565
  }
7463
7566
  }
7464
- return await new Promise((resolve) => {
7567
+ await new Promise((resolve) => {
7465
7568
  const timer = setInterval(() => {
7466
7569
  if (popup.closed) {
7467
7570
  clearInterval(timer);
7468
- resolve({ status: 'closed', address: session.address, asset: session.asset });
7571
+ resolve();
7469
7572
  }
7470
7573
  }, 500);
7471
7574
  });
7575
+ const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
7576
+ if (fill) {
7577
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
7578
+ }
7579
+ return Object.assign(Object.assign({}, base), { status: 'closed' });
7472
7580
  }
7473
7581
  // New-tab path (popup was blocked or explicitly requested): cross-origin tab
7474
- // close is not observable, so resolve immediately.
7582
+ // close is not observable, so resolve immediately; verify via onrampStatus().
7475
7583
  const tab = window.open(session.url, '_blank');
7476
7584
  if (!tab)
7477
- throw new Error('onramp(): window.open was blocked - call onramp() from a click handler');
7478
- return { status: 'opened', address: session.address, asset: session.asset };
7585
+ throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
7586
+ return Object.assign(Object.assign({}, base), { status: 'opened' });
7479
7587
  }
7480
7588
 
7481
7589
  /**
@@ -7989,6 +8097,8 @@ exports.normalizeSolanaRpcUrl = normalizeSolanaRpcUrl;
7989
8097
  exports.onAuthLoadingChanged = onAuthLoadingChanged;
7990
8098
  exports.onAuthStateChanged = onAuthStateChanged;
7991
8099
  exports.onramp = onramp;
8100
+ exports.onrampQuote = onrampQuote;
8101
+ exports.onrampStatus = onrampStatus;
7992
8102
  exports.openBoundedWidget = openBoundedWidget;
7993
8103
  exports.requestEip6963Providers = requestEip6963Providers;
7994
8104
  exports.resolveSolanaRpcUrl = resolveSolanaRpcUrl;
@@ -8001,4 +8111,4 @@ exports.startTurnkeyEmailLogin = startTurnkeyEmailLogin;
8001
8111
  exports.useAuth = useAuth;
8002
8112
  exports.useBuildRun = useBuildRun;
8003
8113
  exports.useQuery = useQuery;
8004
- //# sourceMappingURL=index-uu8hKP-p.js.map
8114
+ //# sourceMappingURL=index-iu8vB4ye.js.map