@bounded-sh/client 0.0.59 → 0.0.61
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/{email-auth-provider-N-dfsNnE.js → email-auth-provider-9BwSziWj.js} +2 -2
- package/dist/{email-auth-provider-N-dfsNnE.js.map → email-auth-provider-9BwSziWj.js.map} +1 -1
- package/dist/{email-auth-provider-DhAMOILG.mjs → email-auth-provider-B3XaJw-G.mjs} +2 -2
- package/dist/{email-auth-provider-deB_ADc5.mjs.map → email-auth-provider-B3XaJw-G.mjs.map} +1 -1
- package/dist/{email-auth-provider-BSw9FRjg.js → email-auth-provider-BttT2sZK.js} +2 -2
- package/dist/{email-auth-provider-BSw9FRjg.js.map → email-auth-provider-BttT2sZK.js.map} +1 -1
- package/dist/{email-auth-provider-deB_ADc5.mjs → email-auth-provider-Cb4VKA1E.mjs} +2 -2
- package/dist/{email-auth-provider-DhAMOILG.mjs.map → email-auth-provider-Cb4VKA1E.mjs.map} +1 -1
- package/dist/{guest-auth-provider-D9wbogwi.mjs → guest-auth-provider-BBPpydtM.mjs} +2 -2
- package/dist/{guest-auth-provider-B78Ugfar.mjs.map → guest-auth-provider-BBPpydtM.mjs.map} +1 -1
- package/dist/{guest-auth-provider-B78Ugfar.mjs → guest-auth-provider-BBSFawAr.mjs} +2 -2
- package/dist/{guest-auth-provider-D9wbogwi.mjs.map → guest-auth-provider-BBSFawAr.mjs.map} +1 -1
- package/dist/{guest-auth-provider-Czl48mOP.js → guest-auth-provider-BcR-MQ0g.js} +2 -2
- package/dist/{guest-auth-provider-Czl48mOP.js.map → guest-auth-provider-BcR-MQ0g.js.map} +1 -1
- package/dist/{guest-auth-provider-BFodq9Rc.js → guest-auth-provider-Di0TRg-z.js} +2 -2
- package/dist/{guest-auth-provider-BFodq9Rc.js.map → guest-auth-provider-Di0TRg-z.js.map} +1 -1
- package/dist/{index-uu8hKP-p.js → index-C_L497w1.js} +191 -51
- package/dist/index-C_L497w1.js.map +1 -0
- package/dist/{index-SHt4VZZV.mjs → index-L2R6C5Vl.mjs} +190 -52
- package/dist/index-L2R6C5Vl.mjs.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{index.native-D-8IAEy-.mjs → index.native-BIUs8t7F.mjs} +182 -51
- package/dist/index.native-BIUs8t7F.mjs.map +1 -0
- package/dist/{index.native-D48QQ0-q.js → index.native-BSheiGj2.js} +183 -50
- package/dist/index.native-BSheiGj2.js.map +1 -0
- package/dist/index.native.d.ts +2 -2
- package/dist/index.native.js +3 -1
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js → injected-evm-wallet-provider-7XawIUdJ.js} +2 -2
- package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js.map → injected-evm-wallet-provider-7XawIUdJ.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs → injected-evm-wallet-provider-DSFDC8fh.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs.map → injected-evm-wallet-provider-DSFDC8fh.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-CItxws1D.js → injected-evm-wallet-provider-DgWUaVtt.js} +2 -2
- package/dist/{injected-evm-wallet-provider-CItxws1D.js.map → injected-evm-wallet-provider-DgWUaVtt.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs → injected-evm-wallet-provider-DnOrVMDX.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs.map → injected-evm-wallet-provider-DnOrVMDX.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-CmXqreEE.js → injected-wallet-provider-BMDJntMD.js} +2 -2
- package/dist/{injected-wallet-provider-CmXqreEE.js.map → injected-wallet-provider-BMDJntMD.js.map} +1 -1
- package/dist/{injected-wallet-provider-BihII94U.mjs → injected-wallet-provider-DfJvN3sV.mjs} +2 -2
- package/dist/{injected-wallet-provider-BihII94U.mjs.map → injected-wallet-provider-DfJvN3sV.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-CNYAynIB.mjs → injected-wallet-provider-ZkPzVj5W.mjs} +2 -2
- package/dist/{injected-wallet-provider-CNYAynIB.mjs.map → injected-wallet-provider-ZkPzVj5W.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-CVeZgfEt.js → injected-wallet-provider-oCRGuls5.js} +2 -2
- package/dist/{injected-wallet-provider-CVeZgfEt.js.map → injected-wallet-provider-oCRGuls5.js.map} +1 -1
- package/dist/onramp.d.ts +51 -9
- package/package.json +2 -2
- package/dist/index-SHt4VZZV.mjs.map +0 -1
- package/dist/index-uu8hKP-p.js.map +0 -1
- package/dist/index.native-D-8IAEy-.mjs.map +0 -1
- package/dist/index.native-D48QQ0-q.js.map +0 -1
|
@@ -3707,6 +3707,7 @@ const OIDC_NO_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wa
|
|
|
3707
3707
|
'then sign from a user gesture.';
|
|
3708
3708
|
const PKCE_STORE_KEY = 'bounded.oidc.pkce';
|
|
3709
3709
|
const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
|
|
3710
|
+
const ISSUER_SESSION_KIND_KEY$1 = 'bounded_issuer_session_kind';
|
|
3710
3711
|
// Active session store (Web on web, RN on React Native) — selected by config,
|
|
3711
3712
|
// not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
|
|
3712
3713
|
// session it mints is read back through this same selector everywhere.
|
|
@@ -4213,6 +4214,13 @@ async function exchangeCode(code, verifier, redirectUri) {
|
|
|
4213
4214
|
getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY$1, 'email');
|
|
4214
4215
|
}
|
|
4215
4216
|
catch ( /* storage unavailable */_b) { /* storage unavailable */ }
|
|
4217
|
+
// OIDC login does establish the issuer's Better Auth browser cookie. Record
|
|
4218
|
+
// that fact separately from the provider name so logout clears the cookie
|
|
4219
|
+
// and returns through the registered app origin.
|
|
4220
|
+
try {
|
|
4221
|
+
getPlatform().storage.setItem(ISSUER_SESSION_KIND_KEY$1, 'hosted');
|
|
4222
|
+
}
|
|
4223
|
+
catch ( /* storage unavailable */_c) { /* storage unavailable */ }
|
|
4216
4224
|
setCurrentUser(user);
|
|
4217
4225
|
setAuthProviderInstance(oidcProviderSingleton);
|
|
4218
4226
|
return user;
|
|
@@ -4229,6 +4237,7 @@ const objectIdentityKeys = new WeakMap();
|
|
|
4229
4237
|
let nextObjectIdentityKey = 1;
|
|
4230
4238
|
// --- localStorage helpers: track which auth method created the active session ---
|
|
4231
4239
|
const STORED_AUTH_METHOD_KEY = 'bounded_last_auth_method';
|
|
4240
|
+
const ISSUER_SESSION_KIND_KEY = 'bounded_issuer_session_kind';
|
|
4232
4241
|
function getStoredAuthMethod() {
|
|
4233
4242
|
try {
|
|
4234
4243
|
return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY);
|
|
@@ -4245,11 +4254,25 @@ function setStoredAuthMethod(method) {
|
|
|
4245
4254
|
else {
|
|
4246
4255
|
getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY);
|
|
4247
4256
|
}
|
|
4257
|
+
// Non-email providers cannot own a Better Auth browser session. Clear
|
|
4258
|
+
// any prior hosted/inline classification when the active provider is
|
|
4259
|
+
// replaced or the session is removed.
|
|
4260
|
+
if (method !== 'email') {
|
|
4261
|
+
getPlatform().storage.removeItem(ISSUER_SESSION_KIND_KEY);
|
|
4262
|
+
}
|
|
4248
4263
|
}
|
|
4249
4264
|
catch (_a) {
|
|
4250
4265
|
// storage might not be available
|
|
4251
4266
|
}
|
|
4252
4267
|
}
|
|
4268
|
+
function getIssuerSessionKind() {
|
|
4269
|
+
try {
|
|
4270
|
+
return getPlatform().storage.getItem(ISSUER_SESSION_KIND_KEY);
|
|
4271
|
+
}
|
|
4272
|
+
catch (_a) {
|
|
4273
|
+
return null;
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4253
4276
|
function getStoredIdToken() {
|
|
4254
4277
|
try {
|
|
4255
4278
|
return core.getActiveSessionManager().getIdToken();
|
|
@@ -4474,7 +4497,7 @@ async function getAuthProvider(config) {
|
|
|
4474
4497
|
// manager as legacy email sessions. Human login should be started
|
|
4475
4498
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
4476
4499
|
// logout surface for those sessions.
|
|
4477
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-
|
|
4500
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BttT2sZK.js'); })).EmailAuthProvider();
|
|
4478
4501
|
break;
|
|
4479
4502
|
case "wallet":
|
|
4480
4503
|
case "phantom": {
|
|
@@ -4493,7 +4516,7 @@ async function getAuthProvider(config) {
|
|
|
4493
4516
|
if (!isWalletLoginEnabled(config)) {
|
|
4494
4517
|
throw walletLoginNotEnabledError(authMethod);
|
|
4495
4518
|
}
|
|
4496
|
-
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
4519
|
+
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BMDJntMD.js'); });
|
|
4497
4520
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
4498
4521
|
break;
|
|
4499
4522
|
}
|
|
@@ -4506,7 +4529,7 @@ async function getAuthProvider(config) {
|
|
|
4506
4529
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
4507
4530
|
// to point at a specific wallet or set chainId/domain.
|
|
4508
4531
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
4509
|
-
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-
|
|
4532
|
+
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-7XawIUdJ.js'); });
|
|
4510
4533
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
4511
4534
|
break;
|
|
4512
4535
|
}
|
|
@@ -4534,7 +4557,7 @@ async function getAuthProvider(config) {
|
|
|
4534
4557
|
break;
|
|
4535
4558
|
case "guest":
|
|
4536
4559
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
4537
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
4560
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BcR-MQ0g.js'); })).GuestAuthProvider();
|
|
4538
4561
|
break;
|
|
4539
4562
|
}
|
|
4540
4563
|
if (!currentAuthProvider) {
|
|
@@ -4590,7 +4613,7 @@ async function signInAnonymously() {
|
|
|
4590
4613
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
4591
4614
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
4592
4615
|
// so bundle-splitting can see it.
|
|
4593
|
-
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
4616
|
+
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BcR-MQ0g.js'); })).GuestAuthProvider();
|
|
4594
4617
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
4595
4618
|
const commit = (user) => {
|
|
4596
4619
|
// No await/interleaving inside this block: provider/method/config/marker
|
|
@@ -4775,7 +4798,7 @@ function getCurrentAuthMethod() {
|
|
|
4775
4798
|
// allowlist here: on a trusted origin we end the issuer session with a
|
|
4776
4799
|
// top-level bounce; anywhere else (custom domains, http dev) we keep the
|
|
4777
4800
|
// local-only logout instead of stranding the user on the issuer origin.
|
|
4778
|
-
function issuerLogoutBounceUrl(issuerBase) {
|
|
4801
|
+
function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
|
|
4779
4802
|
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
4780
4803
|
return null; // RN/Node: no shared cookie jar to clear this way
|
|
4781
4804
|
try {
|
|
@@ -4798,12 +4821,27 @@ function issuerLogoutBounceUrl(issuerBase) {
|
|
|
4798
4821
|
if (!trusted)
|
|
4799
4822
|
return null;
|
|
4800
4823
|
const base = issuerBase.replace(/\/$/, '');
|
|
4801
|
-
|
|
4824
|
+
// #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
|
|
4825
|
+
// session + app + registered origin. Without it the issuer safely falls back to its own
|
|
4826
|
+
// origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
|
|
4827
|
+
const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
|
|
4828
|
+
return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
|
|
4802
4829
|
}
|
|
4803
4830
|
catch (_b) {
|
|
4804
4831
|
return null;
|
|
4805
4832
|
}
|
|
4806
4833
|
}
|
|
4834
|
+
// #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
|
|
4835
|
+
// against any `catch` so a revokeSession failure can never be swallowed, and a
|
|
4836
|
+
// missing id token is the one benign absence worth tolerating here.
|
|
4837
|
+
function idTokenHintOrNull(sm) {
|
|
4838
|
+
try {
|
|
4839
|
+
return sm.getIdToken();
|
|
4840
|
+
}
|
|
4841
|
+
catch (_a) {
|
|
4842
|
+
return null; /* no id token to hint with */
|
|
4843
|
+
}
|
|
4844
|
+
}
|
|
4807
4845
|
async function logout$1(options) {
|
|
4808
4846
|
// Re-initialize the provider if it was cleared by a prior logout — the exact mirror
|
|
4809
4847
|
// of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
|
|
@@ -4828,17 +4866,20 @@ async function logout$1(options) {
|
|
|
4828
4866
|
const sm = core.getActiveSessionManager();
|
|
4829
4867
|
const refreshToken = sm.getRefreshToken();
|
|
4830
4868
|
const sessionIssuer = sm.getIssuer();
|
|
4869
|
+
// #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
|
|
4870
|
+
// can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
|
|
4871
|
+
const idTokenHint = idTokenHintOrNull(sm);
|
|
4831
4872
|
if (refreshToken)
|
|
4832
4873
|
await core.revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
|
|
4833
|
-
//
|
|
4834
|
-
//
|
|
4835
|
-
//
|
|
4836
|
-
//
|
|
4837
|
-
//
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4874
|
+
// Only OIDC-hosted sessions own an issuer browser cookie. Turnkey-native
|
|
4875
|
+
// email OTP is also an email auth method, but it mints the app session
|
|
4876
|
+
// inline and has no issuer cookie to clear. Use the explicit session-kind
|
|
4877
|
+
// marker when present. The auth-method fallback preserves correct logout
|
|
4878
|
+
// behavior for hosted sessions created by older SDK versions.
|
|
4879
|
+
const issuerSessionKind = getIssuerSessionKind();
|
|
4880
|
+
const wasHosted = issuerSessionKind === 'hosted'
|
|
4881
|
+
|| (issuerSessionKind === null
|
|
4882
|
+
&& (currentAuthMethod === 'email' || getStoredAuthMethod() === 'email'));
|
|
4842
4883
|
await currentAuthProvider.logout();
|
|
4843
4884
|
setStoredAuthMethod(null);
|
|
4844
4885
|
currentAuthProvider = null;
|
|
@@ -4848,7 +4889,7 @@ async function logout$1(options) {
|
|
|
4848
4889
|
const issuerBase = sessionIssuer
|
|
4849
4890
|
|| (await core.getConfig()).humanAuthApiUrl
|
|
4850
4891
|
|| 'https://auth.bounded.sh';
|
|
4851
|
-
const bounce = issuerLogoutBounceUrl(issuerBase);
|
|
4892
|
+
const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
|
|
4852
4893
|
// Local state is already cleared, so even if the navigation is stopped
|
|
4853
4894
|
// (tab close, extension) the app-side logout has fully happened.
|
|
4854
4895
|
if (bounce)
|
|
@@ -5831,30 +5872,117 @@ async function deserializeTransaction(tx) {
|
|
|
5831
5872
|
// session is bound server-side to the caller's verified Bounded JWT, and the
|
|
5832
5873
|
// destination defaults to that user's embedded wallet.
|
|
5833
5874
|
//
|
|
5875
|
+
// Amount semantics:
|
|
5876
|
+
// amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
|
|
5877
|
+
// minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
|
|
5878
|
+
// worst-fee payment method (card) and pads by `slippagePad` (default 1%),
|
|
5879
|
+
// so cheaper methods overshoot the floor rather than undershooting it. The
|
|
5880
|
+
// floor holds unless the market moves more than the pad while the user sits
|
|
5881
|
+
// on the checkout screen - which is why completion is VERIFIED: after the
|
|
5882
|
+
// popup closes we poll the platform's status leg and resolve with the
|
|
5883
|
+
// actual filled amount when Coinbase reports it.
|
|
5884
|
+
//
|
|
5834
5885
|
// Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
|
|
5835
5886
|
// local dev origin the session endpoint does not exist; we fail with a clear
|
|
5836
5887
|
// error rather than guessing a host.
|
|
5837
5888
|
function hasDOM() {
|
|
5838
5889
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
5839
5890
|
}
|
|
5891
|
+
async function authedFetch(path, init) {
|
|
5892
|
+
const idToken = await getIdToken();
|
|
5893
|
+
if (!idToken)
|
|
5894
|
+
throw new Error('onramp: no active session - call login() first');
|
|
5895
|
+
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}` }) }));
|
|
5896
|
+
}
|
|
5897
|
+
async function readError(res) {
|
|
5898
|
+
var _a;
|
|
5899
|
+
try {
|
|
5900
|
+
return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
|
|
5901
|
+
}
|
|
5902
|
+
catch (_b) {
|
|
5903
|
+
return '';
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
function sessionBody(options, extra) {
|
|
5907
|
+
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));
|
|
5908
|
+
}
|
|
5909
|
+
function resolveAddress(options) {
|
|
5910
|
+
var _a, _b;
|
|
5911
|
+
const user = getCurrentUser();
|
|
5912
|
+
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;
|
|
5913
|
+
if (!address) {
|
|
5914
|
+
throw new Error(user
|
|
5915
|
+
? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
|
|
5916
|
+
: 'onramp: no signed-in user - call login() first or pass { address }');
|
|
5917
|
+
}
|
|
5918
|
+
return address;
|
|
5919
|
+
}
|
|
5840
5920
|
/**
|
|
5841
|
-
*
|
|
5921
|
+
* Preview a quote (fees + exact/floor output) WITHOUT opening any window or
|
|
5922
|
+
* consuming a session. Show this to the user, then call onramp() to check out.
|
|
5923
|
+
*/
|
|
5924
|
+
async function onrampQuote(options = {}) {
|
|
5925
|
+
if (!hasDOM())
|
|
5926
|
+
throw new Error('onrampQuote is web-only');
|
|
5927
|
+
resolveAddress(options);
|
|
5928
|
+
const res = await authedFetch('/__bounded/onramp/session', {
|
|
5929
|
+
method: 'POST',
|
|
5930
|
+
headers: { 'content-type': 'application/json' },
|
|
5931
|
+
body: sessionBody(options, { quoteOnly: true }),
|
|
5932
|
+
});
|
|
5933
|
+
if (!res.ok)
|
|
5934
|
+
throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
|
|
5935
|
+
return (await res.json());
|
|
5936
|
+
}
|
|
5937
|
+
/** The signed-in user's recent onramp transactions (newest first). */
|
|
5938
|
+
async function onrampStatus(sinceMs) {
|
|
5939
|
+
if (!hasDOM())
|
|
5940
|
+
throw new Error('onrampStatus is web-only');
|
|
5941
|
+
const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
|
|
5942
|
+
if (!res.ok)
|
|
5943
|
+
throw new Error(`onrampStatus: failed (${res.status})`);
|
|
5944
|
+
return (await res.json()).transactions;
|
|
5945
|
+
}
|
|
5946
|
+
function isSuccess(t) {
|
|
5947
|
+
return /SUCCESS|COMPLETED/i.test(t.status);
|
|
5948
|
+
}
|
|
5949
|
+
function isPending(t) {
|
|
5950
|
+
return /PROGRESS|PENDING|CREATED/i.test(t.status);
|
|
5951
|
+
}
|
|
5952
|
+
async function verifyCompletion(openedAt, asset, timeoutMs) {
|
|
5953
|
+
const deadline = Date.now() + timeoutMs;
|
|
5954
|
+
// 60s of clock-skew slack on `since`: Coinbase stamps server-side.
|
|
5955
|
+
const since = openedAt - 60000;
|
|
5956
|
+
for (;;) {
|
|
5957
|
+
let sawPending = false;
|
|
5958
|
+
try {
|
|
5959
|
+
const txs = await onrampStatus(since);
|
|
5960
|
+
const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
|
|
5961
|
+
if (done)
|
|
5962
|
+
return done;
|
|
5963
|
+
sawPending = txs.some(isPending);
|
|
5964
|
+
}
|
|
5965
|
+
catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
|
|
5966
|
+
// No completed fill and nothing in flight: one grace poll cycle still
|
|
5967
|
+
// runs (Coinbase's record can lag the redirect), but don't burn the
|
|
5968
|
+
// full window when the user plainly abandoned checkout.
|
|
5969
|
+
if (Date.now() >= deadline)
|
|
5970
|
+
return null;
|
|
5971
|
+
if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
|
|
5972
|
+
return null;
|
|
5973
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Open Coinbase Onramp for the signed-in user. Resolves after the window
|
|
5978
|
+
* closes, with completion VERIFIED against Coinbase when possible.
|
|
5842
5979
|
* Call from a click handler - popup blockers require user activation.
|
|
5843
5980
|
*/
|
|
5844
5981
|
async function onramp(options = {}) {
|
|
5845
|
-
var _a
|
|
5982
|
+
var _a;
|
|
5846
5983
|
if (!hasDOM())
|
|
5847
5984
|
throw new Error('onramp() is web-only');
|
|
5848
|
-
|
|
5849
|
-
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;
|
|
5850
|
-
if (!address) {
|
|
5851
|
-
throw new Error(user
|
|
5852
|
-
? 'onramp(): the signed-in account has no wallet address - pass { address } explicitly'
|
|
5853
|
-
: 'onramp(): no signed-in user - call login() first or pass { address }');
|
|
5854
|
-
}
|
|
5855
|
-
const idToken = await getIdToken();
|
|
5856
|
-
if (!idToken)
|
|
5857
|
-
throw new Error('onramp(): no active session - call login() first');
|
|
5985
|
+
resolveAddress(options);
|
|
5858
5986
|
// Open the window synchronously so the click's user-activation window is not
|
|
5859
5987
|
// consumed by the fetch below (same hard-won pattern as loginWithPopup).
|
|
5860
5988
|
let popup = null;
|
|
@@ -5867,21 +5995,17 @@ async function onramp(options = {}) {
|
|
|
5867
5995
|
}
|
|
5868
5996
|
let session;
|
|
5869
5997
|
try {
|
|
5870
|
-
const res = await
|
|
5998
|
+
const res = await authedFetch('/__bounded/onramp/session', {
|
|
5871
5999
|
method: 'POST',
|
|
5872
|
-
headers: {
|
|
5873
|
-
body:
|
|
6000
|
+
headers: { 'content-type': 'application/json' },
|
|
6001
|
+
body: sessionBody(options),
|
|
5874
6002
|
});
|
|
5875
6003
|
if (!res.ok) {
|
|
5876
|
-
|
|
5877
|
-
try {
|
|
5878
|
-
detail = (_c = (await res.json()).error) !== null && _c !== void 0 ? _c : '';
|
|
5879
|
-
}
|
|
5880
|
-
catch ( /* keep status only */_d) { /* keep status only */ }
|
|
6004
|
+
const detail = await readError(res);
|
|
5881
6005
|
if (res.status === 404) {
|
|
5882
|
-
throw new Error('onramp
|
|
6006
|
+
throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
|
|
5883
6007
|
}
|
|
5884
|
-
throw new Error(`onramp
|
|
6008
|
+
throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
|
|
5885
6009
|
}
|
|
5886
6010
|
session = (await res.json());
|
|
5887
6011
|
}
|
|
@@ -5889,40 +6013,47 @@ async function onramp(options = {}) {
|
|
|
5889
6013
|
try {
|
|
5890
6014
|
popup === null || popup === void 0 ? void 0 : popup.close();
|
|
5891
6015
|
}
|
|
5892
|
-
catch ( /* ignore */
|
|
6016
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
5893
6017
|
throw error;
|
|
5894
6018
|
}
|
|
6019
|
+
const openedAt = Date.now();
|
|
6020
|
+
const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
|
|
5895
6021
|
if (popup) {
|
|
5896
6022
|
try {
|
|
5897
6023
|
popup.location.replace(session.url);
|
|
5898
6024
|
}
|
|
5899
|
-
catch (
|
|
6025
|
+
catch (_c) {
|
|
5900
6026
|
try {
|
|
5901
6027
|
popup.location.href = session.url;
|
|
5902
6028
|
}
|
|
5903
|
-
catch (
|
|
6029
|
+
catch (_d) {
|
|
5904
6030
|
try {
|
|
5905
6031
|
popup.close();
|
|
5906
6032
|
}
|
|
5907
|
-
catch ( /* ignore */
|
|
5908
|
-
throw new Error('onramp
|
|
6033
|
+
catch ( /* ignore */_e) { /* ignore */ }
|
|
6034
|
+
throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
|
|
5909
6035
|
}
|
|
5910
6036
|
}
|
|
5911
|
-
|
|
6037
|
+
await new Promise((resolve) => {
|
|
5912
6038
|
const timer = setInterval(() => {
|
|
5913
6039
|
if (popup.closed) {
|
|
5914
6040
|
clearInterval(timer);
|
|
5915
|
-
resolve(
|
|
6041
|
+
resolve();
|
|
5916
6042
|
}
|
|
5917
6043
|
}, 500);
|
|
5918
6044
|
});
|
|
6045
|
+
const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
|
|
6046
|
+
if (fill) {
|
|
6047
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
|
|
6048
|
+
}
|
|
6049
|
+
return Object.assign(Object.assign({}, base), { status: 'closed' });
|
|
5919
6050
|
}
|
|
5920
6051
|
// New-tab path (popup was blocked or explicitly requested): cross-origin tab
|
|
5921
|
-
// close is not observable, so resolve immediately.
|
|
6052
|
+
// close is not observable, so resolve immediately; verify via onrampStatus().
|
|
5922
6053
|
const tab = window.open(session.url, '_blank');
|
|
5923
6054
|
if (!tab)
|
|
5924
|
-
throw new Error('onramp
|
|
5925
|
-
return { status: 'opened'
|
|
6055
|
+
throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
|
|
6056
|
+
return Object.assign(Object.assign({}, base), { status: 'opened' });
|
|
5926
6057
|
}
|
|
5927
6058
|
|
|
5928
6059
|
/**
|
|
@@ -6347,6 +6478,8 @@ exports.normalizeSolanaRpcUrl = normalizeSolanaRpcUrl;
|
|
|
6347
6478
|
exports.onAuthLoadingChanged = onAuthLoadingChanged;
|
|
6348
6479
|
exports.onAuthStateChanged = onAuthStateChanged;
|
|
6349
6480
|
exports.onramp = onramp;
|
|
6481
|
+
exports.onrampQuote = onrampQuote;
|
|
6482
|
+
exports.onrampStatus = onrampStatus;
|
|
6350
6483
|
exports.resolveSolanaRpcUrl = resolveSolanaRpcUrl;
|
|
6351
6484
|
exports.setAuthLoading = setAuthLoading;
|
|
6352
6485
|
exports.setCurrentUser = setCurrentUser;
|
|
@@ -6355,4 +6488,4 @@ exports.signInAnonymously = signInAnonymously;
|
|
|
6355
6488
|
exports.useAuth = useAuth;
|
|
6356
6489
|
exports.useBuildRun = useBuildRun;
|
|
6357
6490
|
exports.useQuery = useQuery;
|
|
6358
|
-
//# sourceMappingURL=index.native-
|
|
6491
|
+
//# sourceMappingURL=index.native-BSheiGj2.js.map
|