@bounded-sh/client 0.0.35 → 0.0.37
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/README.md +20 -1
- package/dist/auth/crossmint-signer-bridge.d.ts +37 -0
- package/dist/auth/hooks/useAuth.d.ts +2 -1
- package/dist/auth/providers/email-auth-provider.d.ts +2 -1
- package/dist/auth/providers/offchain-auth-provider.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +284 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +269 -13
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.d.ts +2 -0
- package/dist/index.native.js +284 -12
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +269 -13
- package/dist/index.native.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -111,7 +111,26 @@ const unsubscribe = onAuthStateChanged((user) => {
|
|
|
111
111
|
unsubscribe();
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
### Embedded wallets — signing
|
|
115
|
+
|
|
116
|
+
When an app enables embedded wallets in `policy.json` (`"auth": { "wallets": true }`),
|
|
117
|
+
every email login also gets a non-custodial Solana wallet (`@user.address`). That user
|
|
118
|
+
can approve transactions with it — signing runs in a Bounded-hosted popup that does a
|
|
119
|
+
one-time email verification; Bounded never holds the key.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { signAndSubmitTransaction } from '@bounded-sh/client';
|
|
123
|
+
import { VersionedTransaction } from '@solana/web3.js';
|
|
124
|
+
|
|
125
|
+
// Call from a user gesture (click/tap) — signing opens a popup.
|
|
126
|
+
async function pay(tx: VersionedTransaction) {
|
|
127
|
+
const signature = await signAndSubmitTransaction(tx); // signs + submits, returns the hash
|
|
128
|
+
console.log('submitted', signature);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`signMessage` and `signTransaction` (sign without submit) throw for embedded smart
|
|
133
|
+
wallets — they sign and submit atomically, so use `signAndSubmitTransaction`.
|
|
115
134
|
|
|
116
135
|
## Usage
|
|
117
136
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
2
|
+
export type SignerAction = {
|
|
3
|
+
kind: 'sendToken';
|
|
4
|
+
recipient: string;
|
|
5
|
+
token: string;
|
|
6
|
+
amount: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'transaction';
|
|
9
|
+
serializedTransaction: string;
|
|
10
|
+
};
|
|
11
|
+
export interface SignerResult {
|
|
12
|
+
hash?: string;
|
|
13
|
+
explorerLink?: string;
|
|
14
|
+
transactionId?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const EMBEDDED_WALLET_UNSUPPORTED: string;
|
|
17
|
+
/**
|
|
18
|
+
* Open the Bounded signer popup, send `action` to it, and resolve with the
|
|
19
|
+
* submitted transaction result. Must be called from a USER GESTURE (click/tap) or
|
|
20
|
+
* the browser will block the popup — in which case this rejects with a clear
|
|
21
|
+
* "Popup blocked" error telling the app to call from a user gesture.
|
|
22
|
+
*
|
|
23
|
+
* Non-custodial: the popup gates signing behind an email OTP delivered to the
|
|
24
|
+
* wallet's owner, and validates that the wallet address matches the account.
|
|
25
|
+
*/
|
|
26
|
+
export declare function signViaSignerPopup(action: SignerAction): Promise<SignerResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the active session carries an embedded wallet (so the provider should
|
|
29
|
+
* route signing through the popup rather than throw "no wallet").
|
|
30
|
+
*/
|
|
31
|
+
export declare function hasEmbeddedWallet(): boolean;
|
|
32
|
+
/** signAndSubmitTransaction for embedded wallets: serialize + route through the
|
|
33
|
+
* signer popup, resolve with the on-chain transaction signature/hash. */
|
|
34
|
+
export declare function embeddedSignAndSubmit(transaction: Transaction | VersionedTransaction): Promise<string>;
|
|
35
|
+
/** Send a token from the embedded wallet (Crossmint `wallet.send`). Convenience
|
|
36
|
+
* primitive for the common "send crypto" case; returns the tx result. */
|
|
37
|
+
export declare function embeddedSendToken(recipient: string, token: string, amount: string): Promise<SignerResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { User } from '@bounded-sh/core';
|
|
2
|
+
import { type LoginOptions } from '../../global';
|
|
2
3
|
export interface UseAuthResult {
|
|
3
|
-
login: () => Promise<void>;
|
|
4
|
+
login: (options?: LoginOptions) => Promise<void>;
|
|
4
5
|
logout: () => Promise<void>;
|
|
5
6
|
loading: boolean;
|
|
6
7
|
user: User | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AuthProvider, User, TransactionResult, SolTransaction, EVMTransaction, SetOptions } from '@bounded-sh/core';
|
|
2
2
|
import { Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
3
|
+
import { PublicKey } from '@solana/web3.js';
|
|
3
4
|
export declare class EmailAuthProvider implements AuthProvider {
|
|
4
5
|
login(): Promise<User | null>;
|
|
5
6
|
restoreSession(): Promise<User | null>;
|
|
@@ -7,7 +8,7 @@ export declare class EmailAuthProvider implements AuthProvider {
|
|
|
7
8
|
signMessage(_message: string): Promise<string>;
|
|
8
9
|
runTransaction(_evm?: EVMTransaction, _sol?: SolTransaction, _options?: SetOptions): Promise<TransactionResult>;
|
|
9
10
|
signTransaction(_t: Transaction | VersionedTransaction): Promise<Transaction | VersionedTransaction>;
|
|
10
|
-
signAndSubmitTransaction(): Promise<string>;
|
|
11
|
+
signAndSubmitTransaction(transaction: Transaction | VersionedTransaction, _feePayer?: PublicKey): Promise<string>;
|
|
11
12
|
getNativeMethods(): Promise<any>;
|
|
12
13
|
}
|
|
13
14
|
declare const emailProviderSingleton: EmailAuthProvider;
|
|
@@ -18,6 +18,7 @@ export declare class OffchainAuthProvider implements AuthProvider {
|
|
|
18
18
|
private modalContainer;
|
|
19
19
|
constructor(wrappedProvider: AuthProvider, config?: OffchainAuthProviderConfig);
|
|
20
20
|
login(): Promise<User | null>;
|
|
21
|
+
setLoginOverrides(opts: any): void;
|
|
21
22
|
logout(): Promise<void>;
|
|
22
23
|
restoreSession(): Promise<User | null>;
|
|
23
24
|
runTransaction(evmTransactionData?: EVMTransaction, solTransactionData?: SolTransaction, options?: SetOptions): Promise<TransactionResult>;
|
|
@@ -29,6 +30,7 @@ export declare class OffchainAuthProvider implements AuthProvider {
|
|
|
29
30
|
*/
|
|
30
31
|
signAndSubmitTransaction(_transaction: Transaction | VersionedTransaction, _feePayer?: any): Promise<string>;
|
|
31
32
|
getNativeMethods(): Promise<any>;
|
|
33
|
+
exportWallet(address?: string): Promise<void>;
|
|
32
34
|
signMessage(message: string): Promise<string>;
|
|
33
35
|
private showUnsupportedTransactionModal;
|
|
34
36
|
private showTransactionModal;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type { HostedAuthMethod, OidcLoginOptions, PopupLoginOptions } from "./au
|
|
|
7
7
|
export { get, getMany, docId, set, setMany, setFile, getFiles, search, queryAggregate, runQuery, runQueryMany, runExpression, runExpressionMany, signMessage, signTransaction, signAndSubmitTransaction, count, aggregate, GetOptions, SearchOptions, SetOptions, CountOptions, AggregateOptions, AggregateOperation, AggregateResult, AggregateSpec, AggregateRow, QueryAggregateOptions, } from "@bounded-sh/core";
|
|
8
8
|
export type { GetOptions as BoundedGetOptions } from "@bounded-sh/core";
|
|
9
9
|
export { subscribe } from "@bounded-sh/core";
|
|
10
|
+
export { BoundedDeclineError, deriveDecline, dispatchDeclineEvent, DECLINE_EVENT_NAME } from "@bounded-sh/core";
|
|
11
|
+
export type { BoundedDecline, DeclineReason, DeclineBoundary } from "@bounded-sh/core";
|
|
10
12
|
export * from "@bounded-sh/core";
|
|
11
13
|
export { useAuth } from "./auth/hooks/useAuth";
|
|
12
14
|
export { useQuery } from "./hooks/useQuery";
|
package/dist/index.js
CHANGED
|
@@ -165,6 +165,15 @@ class OffchainAuthProvider {
|
|
|
165
165
|
}
|
|
166
166
|
return null;
|
|
167
167
|
}
|
|
168
|
+
// Forward per-call login overrides (theme/title/subtitle and the `method`
|
|
169
|
+
// directive) to the wrapped provider. Without this, the top-level login()
|
|
170
|
+
// forwarder (which duck-types `setLoginOverrides` on the active provider)
|
|
171
|
+
// skips the wrapper and the override — including `method` — is dropped on
|
|
172
|
+
// offchain/Poofnet builds.
|
|
173
|
+
setLoginOverrides(opts) {
|
|
174
|
+
var _a, _b;
|
|
175
|
+
(_b = (_a = this.wrappedProvider).setLoginOverrides) === null || _b === void 0 ? void 0 : _b.call(_a, opts);
|
|
176
|
+
}
|
|
168
177
|
async logout() {
|
|
169
178
|
await this.wrappedProvider.logout();
|
|
170
179
|
}
|
|
@@ -198,6 +207,15 @@ class OffchainAuthProvider {
|
|
|
198
207
|
async getNativeMethods() {
|
|
199
208
|
return this.wrappedProvider.getNativeMethods();
|
|
200
209
|
}
|
|
210
|
+
// Forward wallet export to the wrapped provider (only Privy embedded wallets
|
|
211
|
+
// implement it). Duck-typed so wrappers around non-embedded providers throw
|
|
212
|
+
// a clear error rather than a "not a function" crash.
|
|
213
|
+
async exportWallet(address) {
|
|
214
|
+
if (typeof this.wrappedProvider.exportWallet !== 'function') {
|
|
215
|
+
throw new Error('Wallet export is not available for this wallet.');
|
|
216
|
+
}
|
|
217
|
+
await this.wrappedProvider.exportWallet(address);
|
|
218
|
+
}
|
|
201
219
|
// ============ signMessage delegates to wrapped provider ============
|
|
202
220
|
async signMessage(message) {
|
|
203
221
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -3427,7 +3445,7 @@ const IDB_STORE = 'keys';
|
|
|
3427
3445
|
const IDB_KEY = 'ed25519';
|
|
3428
3446
|
const GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +
|
|
3429
3447
|
'stored-secret guest credentials are disabled.';
|
|
3430
|
-
function toBase64(bytes) {
|
|
3448
|
+
function toBase64$1(bytes) {
|
|
3431
3449
|
let bin = '';
|
|
3432
3450
|
for (let i = 0; i < bytes.length; i++)
|
|
3433
3451
|
bin += String.fromCharCode(bytes[i]);
|
|
@@ -3491,7 +3509,7 @@ async function webCryptoKey() {
|
|
|
3491
3509
|
async sign(message) {
|
|
3492
3510
|
const data = new TextEncoder().encode(message);
|
|
3493
3511
|
const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));
|
|
3494
|
-
return toBase64(sig);
|
|
3512
|
+
return toBase64$1(sig);
|
|
3495
3513
|
},
|
|
3496
3514
|
};
|
|
3497
3515
|
}
|
|
@@ -3594,14 +3612,221 @@ class GuestAuthProvider {
|
|
|
3594
3612
|
}
|
|
3595
3613
|
}
|
|
3596
3614
|
|
|
3615
|
+
var bufferExports = requireBuffer();
|
|
3616
|
+
|
|
3617
|
+
// crossmint-signer-bridge.ts — routes embedded-wallet signing through the
|
|
3618
|
+
// Bounded-owned signer popup (auth.bounded.sh/wallet/signer).
|
|
3619
|
+
//
|
|
3620
|
+
// Slice-1 attached a non-custodial Crossmint smart wallet to email logins and
|
|
3621
|
+
// populated `@user.address`. This bridge is the slice-2 counterpart: a user
|
|
3622
|
+
// whose address came from `auth.wallets` can APPROVE transactions with that
|
|
3623
|
+
// wallet from ANY app, non-custodially, by having the Crossmint client SDK run
|
|
3624
|
+
// ONLY on the Bounded signer page (one whitelisted origin) and talking to it via
|
|
3625
|
+
// postMessage.
|
|
3626
|
+
//
|
|
3627
|
+
// SEMANTICS (Crossmint Solana smart wallets): these wallets cannot produce raw
|
|
3628
|
+
// detached signatures — there is no signMessage and no sign-without-submit. The
|
|
3629
|
+
// only honest primitive is EXECUTION (sign + submit) via the wallet's email-OTP
|
|
3630
|
+
// authorization. So this bridge implements `signAndSubmitTransaction` and a
|
|
3631
|
+
// token-send; `signMessage` / `signTransaction` throw a clear unsupported error.
|
|
3632
|
+
const EMBEDDED_WALLET_UNSUPPORTED = 'Unsupported for embedded smart wallets — Crossmint Solana smart wallets sign ' +
|
|
3633
|
+
'and submit atomically. Use signAndSubmitTransaction() (or bounded.sendWalletToken) ' +
|
|
3634
|
+
'instead of signMessage/signTransaction.';
|
|
3635
|
+
const NO_EMBEDDED_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
3636
|
+
'the app policy so email logins get a non-custodial wallet (@user.address), ' +
|
|
3637
|
+
'then sign from a user gesture.';
|
|
3638
|
+
function hasDOM$1() {
|
|
3639
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
3640
|
+
}
|
|
3641
|
+
function decodeJwt$2(token) {
|
|
3642
|
+
try {
|
|
3643
|
+
let b64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
3644
|
+
const pad = b64.length % 4;
|
|
3645
|
+
if (pad === 2)
|
|
3646
|
+
b64 += '==';
|
|
3647
|
+
else if (pad === 3)
|
|
3648
|
+
b64 += '=';
|
|
3649
|
+
return JSON.parse(bufferExports.Buffer.from(b64, 'base64').toString('utf8'));
|
|
3650
|
+
}
|
|
3651
|
+
catch (_a) {
|
|
3652
|
+
return {};
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
// Pull the embedded-wallet identity out of the active session's id token.
|
|
3656
|
+
// Returns null when this session has no wallet (so callers can throw the
|
|
3657
|
+
// informative NO_EMBEDDED_WALLET error).
|
|
3658
|
+
function walletClaimsFromSession() {
|
|
3659
|
+
let idToken = null;
|
|
3660
|
+
try {
|
|
3661
|
+
idToken = core.getActiveSessionManager().getIdToken();
|
|
3662
|
+
}
|
|
3663
|
+
catch (_a) {
|
|
3664
|
+
idToken = null;
|
|
3665
|
+
}
|
|
3666
|
+
if (!idToken)
|
|
3667
|
+
return null;
|
|
3668
|
+
const c = decodeJwt$2(idToken);
|
|
3669
|
+
const address = c['custom:walletAddress'];
|
|
3670
|
+
const email = c['email'];
|
|
3671
|
+
if (typeof address !== 'string' || !address)
|
|
3672
|
+
return null;
|
|
3673
|
+
if (typeof email !== 'string' || !email)
|
|
3674
|
+
return null;
|
|
3675
|
+
const envClaim = c['custom:walletEnvironment'];
|
|
3676
|
+
const environment = envClaim === 'staging' || envClaim === 'production' ? envClaim : undefined;
|
|
3677
|
+
return { email, address, environment };
|
|
3678
|
+
}
|
|
3679
|
+
async function signerPageUrl() {
|
|
3680
|
+
const cfg = await core.getConfig();
|
|
3681
|
+
const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
|
|
3682
|
+
return { url: `${base}/wallet/signer`, origin: new URL(base).origin };
|
|
3683
|
+
}
|
|
3684
|
+
let requestSeq = 0;
|
|
3685
|
+
/**
|
|
3686
|
+
* Open the Bounded signer popup, send `action` to it, and resolve with the
|
|
3687
|
+
* submitted transaction result. Must be called from a USER GESTURE (click/tap) or
|
|
3688
|
+
* the browser will block the popup — in which case this rejects with a clear
|
|
3689
|
+
* "Popup blocked" error telling the app to call from a user gesture.
|
|
3690
|
+
*
|
|
3691
|
+
* Non-custodial: the popup gates signing behind an email OTP delivered to the
|
|
3692
|
+
* wallet's owner, and validates that the wallet address matches the account.
|
|
3693
|
+
*/
|
|
3694
|
+
async function signViaSignerPopup(action) {
|
|
3695
|
+
if (!hasDOM$1()) {
|
|
3696
|
+
throw new Error('Embedded-wallet signing is only available in the browser (popup-based).');
|
|
3697
|
+
}
|
|
3698
|
+
const claims = walletClaimsFromSession();
|
|
3699
|
+
if (!claims)
|
|
3700
|
+
throw new Error(NO_EMBEDDED_WALLET);
|
|
3701
|
+
const { url, origin: signerOrigin } = await signerPageUrl();
|
|
3702
|
+
const requestId = `bsign_${Date.now()}_${++requestSeq}`;
|
|
3703
|
+
// Centered popup, sized for the OTP form (matches the OIDC login popup shape).
|
|
3704
|
+
const w = 420, h = 600;
|
|
3705
|
+
const left = window.screenX + Math.max(0, (window.outerWidth - w) / 2);
|
|
3706
|
+
const top = window.screenY + Math.max(0, (window.outerHeight - h) / 2);
|
|
3707
|
+
const popup = window.open(url, 'bounded-wallet-signer', `width=${w},height=${h},left=${left},top=${top}`);
|
|
3708
|
+
if (!popup) {
|
|
3709
|
+
throw new Error('Popup blocked — call signAndSubmitTransaction() from a user gesture (e.g. a click handler) so the wallet approval window can open.');
|
|
3710
|
+
}
|
|
3711
|
+
const request = {
|
|
3712
|
+
type: 'bounded:sign',
|
|
3713
|
+
requestId,
|
|
3714
|
+
environment: claims.environment, // may be undefined → page auto-detects
|
|
3715
|
+
email: claims.email,
|
|
3716
|
+
address: claims.address,
|
|
3717
|
+
action,
|
|
3718
|
+
};
|
|
3719
|
+
return await new Promise((resolve, reject) => {
|
|
3720
|
+
let settled = false;
|
|
3721
|
+
let closeTimer;
|
|
3722
|
+
let sentRequest = false;
|
|
3723
|
+
const cleanup = () => {
|
|
3724
|
+
settled = true;
|
|
3725
|
+
window.removeEventListener('message', onMsg);
|
|
3726
|
+
if (closeTimer)
|
|
3727
|
+
clearInterval(closeTimer);
|
|
3728
|
+
};
|
|
3729
|
+
const sendRequest = () => {
|
|
3730
|
+
if (sentRequest)
|
|
3731
|
+
return;
|
|
3732
|
+
sentRequest = true;
|
|
3733
|
+
try {
|
|
3734
|
+
popup.postMessage(request, signerOrigin);
|
|
3735
|
+
}
|
|
3736
|
+
catch ( /* popup gone */_a) { /* popup gone */ }
|
|
3737
|
+
};
|
|
3738
|
+
const onMsg = (ev) => {
|
|
3739
|
+
if (ev.origin !== signerOrigin)
|
|
3740
|
+
return; // only trust the signer page origin
|
|
3741
|
+
const d = ev.data;
|
|
3742
|
+
if (!d || typeof d !== 'object')
|
|
3743
|
+
return;
|
|
3744
|
+
if (d.type === 'bounded:signer:ready') {
|
|
3745
|
+
sendRequest();
|
|
3746
|
+
return;
|
|
3747
|
+
}
|
|
3748
|
+
if (d.type === 'bounded:signer:result' && d.requestId === requestId) {
|
|
3749
|
+
cleanup();
|
|
3750
|
+
try {
|
|
3751
|
+
popup.close();
|
|
3752
|
+
}
|
|
3753
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
3754
|
+
if (d.ok)
|
|
3755
|
+
resolve({ hash: d.hash, explorerLink: d.explorerLink, transactionId: d.transactionId });
|
|
3756
|
+
else
|
|
3757
|
+
reject(new Error(String(d.error || 'Wallet signing failed')));
|
|
3758
|
+
}
|
|
3759
|
+
};
|
|
3760
|
+
window.addEventListener('message', onMsg);
|
|
3761
|
+
// Fallback: if the ready ping was missed (fast load / focus race), push the
|
|
3762
|
+
// request shortly after opening. The page ignores it until a bounded:sign
|
|
3763
|
+
// arrives and only acts on the first one, so a duplicate is harmless.
|
|
3764
|
+
setTimeout(sendRequest, 1200);
|
|
3765
|
+
// If the user closes the popup before completing, reject (unless a result
|
|
3766
|
+
// just arrived — give any in-flight message a beat to settle first).
|
|
3767
|
+
closeTimer = setInterval(() => {
|
|
3768
|
+
if (popup.closed) {
|
|
3769
|
+
clearInterval(closeTimer);
|
|
3770
|
+
setTimeout(() => { if (!settled) {
|
|
3771
|
+
cleanup();
|
|
3772
|
+
reject(new Error('Wallet approval window was closed before signing completed.'));
|
|
3773
|
+
} }, 400);
|
|
3774
|
+
}
|
|
3775
|
+
}, 500);
|
|
3776
|
+
// Overall safety timeout (OTP + signing). Generous — the user may take a
|
|
3777
|
+
// while to fetch the code from their email.
|
|
3778
|
+
setTimeout(() => { if (!settled) {
|
|
3779
|
+
cleanup();
|
|
3780
|
+
try {
|
|
3781
|
+
popup.close();
|
|
3782
|
+
}
|
|
3783
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
3784
|
+
reject(new Error('Wallet approval timed out.'));
|
|
3785
|
+
} }, 5 * 60 * 1000);
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
function toBase64(bytes) {
|
|
3789
|
+
return bufferExports.Buffer.from(bytes).toString('base64');
|
|
3790
|
+
}
|
|
3791
|
+
// Serialize a legacy or versioned Solana transaction to base64 for the signer
|
|
3792
|
+
// page. We do NOT require existing signatures (the smart wallet signs on the
|
|
3793
|
+
// page); Crossmint extracts the instructions and wraps them for the smart wallet.
|
|
3794
|
+
function serializeTransaction(transaction) {
|
|
3795
|
+
const isLegacy = 'recentBlockhash' in transaction &&
|
|
3796
|
+
!('message' in transaction && 'staticAccountKeys' in transaction.message);
|
|
3797
|
+
const bytes = isLegacy
|
|
3798
|
+
? transaction.serialize({ requireAllSignatures: false, verifySignatures: false })
|
|
3799
|
+
: transaction.serialize();
|
|
3800
|
+
return toBase64(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
3801
|
+
}
|
|
3802
|
+
/**
|
|
3803
|
+
* Whether the active session carries an embedded wallet (so the provider should
|
|
3804
|
+
* route signing through the popup rather than throw "no wallet").
|
|
3805
|
+
*/
|
|
3806
|
+
function hasEmbeddedWallet() {
|
|
3807
|
+
return walletClaimsFromSession() != null;
|
|
3808
|
+
}
|
|
3809
|
+
/** signAndSubmitTransaction for embedded wallets: serialize + route through the
|
|
3810
|
+
* signer popup, resolve with the on-chain transaction signature/hash. */
|
|
3811
|
+
async function embeddedSignAndSubmit(transaction) {
|
|
3812
|
+
const serializedTransaction = serializeTransaction(transaction);
|
|
3813
|
+
const result = await signViaSignerPopup({ kind: 'transaction', serializedTransaction });
|
|
3814
|
+
const hash = result.hash || result.transactionId;
|
|
3815
|
+
if (!hash)
|
|
3816
|
+
throw new Error('Signer returned no transaction hash.');
|
|
3817
|
+
return hash;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3597
3820
|
// Web uses localStorage (WebSessionManager); React Native uses the configured
|
|
3598
3821
|
// ReactNativeSessionManager. getActiveSessionManager() picks the right one by
|
|
3599
3822
|
// whether the RN manager was configured (NOT `typeof window`, which is truthy on
|
|
3600
3823
|
// RN too and would wrongly route to localStorage). Same selector the core
|
|
3601
3824
|
// request path uses, so hosted Bounded Auth sessions persist + authenticate on BOTH.
|
|
3602
3825
|
const sessionManager$1 = core.getActiveSessionManager;
|
|
3603
|
-
const NO_WALLET = 'Email login
|
|
3604
|
-
'
|
|
3826
|
+
const NO_WALLET = 'Email login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
3827
|
+
'the app policy so email logins get a non-custodial Crossmint wallet ' +
|
|
3828
|
+
'(@user.address), then sign from a user gesture. Otherwise use a wallet ' +
|
|
3829
|
+
'provider (e.g. Phantom) for onchain actions.';
|
|
3605
3830
|
// ---------------------------------------------------------------------------
|
|
3606
3831
|
// token / session helpers
|
|
3607
3832
|
// ---------------------------------------------------------------------------
|
|
@@ -3651,10 +3876,23 @@ class EmailAuthProvider {
|
|
|
3651
3876
|
sessionManager$1().clearSession();
|
|
3652
3877
|
setCurrentUser(null);
|
|
3653
3878
|
}
|
|
3654
|
-
|
|
3879
|
+
// Crossmint Solana smart wallets sign+submit atomically and cannot produce a
|
|
3880
|
+
// raw signature — signMessage/signTransaction are structurally unsupported
|
|
3881
|
+
// (throw the clear "use signAndSubmitTransaction" error when a wallet exists;
|
|
3882
|
+
// the no-wallet error otherwise). signAndSubmitTransaction routes through the
|
|
3883
|
+
// Bounded signer popup for wallets-enabled email logins.
|
|
3884
|
+
async signMessage(_message) {
|
|
3885
|
+
throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : NO_WALLET);
|
|
3886
|
+
}
|
|
3655
3887
|
async runTransaction(_evm, _sol, _options) { throw new Error(NO_WALLET); }
|
|
3656
|
-
async signTransaction(_t) {
|
|
3657
|
-
|
|
3888
|
+
async signTransaction(_t) {
|
|
3889
|
+
throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : NO_WALLET);
|
|
3890
|
+
}
|
|
3891
|
+
async signAndSubmitTransaction(transaction, _feePayer) {
|
|
3892
|
+
if (!hasEmbeddedWallet())
|
|
3893
|
+
throw new Error(NO_WALLET);
|
|
3894
|
+
return embeddedSignAndSubmit(transaction);
|
|
3895
|
+
}
|
|
3658
3896
|
async getNativeMethods() { return {}; }
|
|
3659
3897
|
}
|
|
3660
3898
|
const emailProviderSingleton = new EmailAuthProvider();
|
|
@@ -3685,6 +3923,11 @@ function resolveSolanaRpcUrl(explicitRpcUrl, network, context) {
|
|
|
3685
3923
|
throw new Error(`${context} requires an explicit Solana RPC URL. Pass config.rpcUrl before signing or submitting onchain transactions.`);
|
|
3686
3924
|
}
|
|
3687
3925
|
|
|
3926
|
+
// Same message the email provider uses when a hosted (OIDC) login has no
|
|
3927
|
+
// embedded wallet — enable `auth.wallets` in policy to get one.
|
|
3928
|
+
const OIDC_NO_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
3929
|
+
'the app policy so logins get a non-custodial Crossmint wallet (@user.address), ' +
|
|
3930
|
+
'then sign from a user gesture.';
|
|
3688
3931
|
const PKCE_STORE_KEY = 'bounded.oidc.pkce';
|
|
3689
3932
|
const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
|
|
3690
3933
|
// Active session store (Web on web, RN on React Native) — selected by config,
|
|
@@ -3709,6 +3952,10 @@ function decodeJwt(token) {
|
|
|
3709
3952
|
}
|
|
3710
3953
|
}
|
|
3711
3954
|
// A minimal AuthProvider so getSession()/logout() work for OIDC sessions, mirroring email.
|
|
3955
|
+
// Signing surface (slice 2): a hosted login on a wallets-enabled app carries an
|
|
3956
|
+
// embedded Crossmint wallet (@user.address). signAndSubmitTransaction routes
|
|
3957
|
+
// through the Bounded signer popup; signMessage/signTransaction are structurally
|
|
3958
|
+
// unsupported for Crossmint smart wallets (sign+submit atomically, no raw sig).
|
|
3712
3959
|
const oidcProviderSingleton = {
|
|
3713
3960
|
async login() {
|
|
3714
3961
|
const idToken = sessionManager().getIdToken();
|
|
@@ -3719,6 +3966,17 @@ const oidcProviderSingleton = {
|
|
|
3719
3966
|
return idToken ? userFromIdToken(idToken) : null;
|
|
3720
3967
|
},
|
|
3721
3968
|
async logout() { sessionManager().clearSession(); },
|
|
3969
|
+
async signMessage(_message) {
|
|
3970
|
+
throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : OIDC_NO_WALLET);
|
|
3971
|
+
},
|
|
3972
|
+
async signTransaction(_t) {
|
|
3973
|
+
throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : OIDC_NO_WALLET);
|
|
3974
|
+
},
|
|
3975
|
+
async signAndSubmitTransaction(transaction, _feePayer) {
|
|
3976
|
+
if (!hasEmbeddedWallet())
|
|
3977
|
+
throw new Error(OIDC_NO_WALLET);
|
|
3978
|
+
return embeddedSignAndSubmit(transaction);
|
|
3979
|
+
},
|
|
3722
3980
|
};
|
|
3723
3981
|
function userFromIdToken(idToken) {
|
|
3724
3982
|
const c = decodeJwt(idToken);
|
|
@@ -4624,7 +4882,7 @@ function useAuth() {
|
|
|
4624
4882
|
// Provide a fallback so server render doesn't break
|
|
4625
4883
|
if (isSSR$1) {
|
|
4626
4884
|
return {
|
|
4627
|
-
login: async () => undefined,
|
|
4885
|
+
login: async (_options) => undefined,
|
|
4628
4886
|
logout: async () => undefined,
|
|
4629
4887
|
loading: true,
|
|
4630
4888
|
user: null,
|
|
@@ -4646,10 +4904,10 @@ function useAuth() {
|
|
|
4646
4904
|
stopLoading();
|
|
4647
4905
|
};
|
|
4648
4906
|
}, []);
|
|
4649
|
-
const login$1 = async () => {
|
|
4907
|
+
const login$1 = async (options) => {
|
|
4650
4908
|
try {
|
|
4651
4909
|
setLoading(true);
|
|
4652
|
-
const user = await login();
|
|
4910
|
+
const user = await login(options);
|
|
4653
4911
|
setUser(user);
|
|
4654
4912
|
}
|
|
4655
4913
|
catch (error) {
|
|
@@ -4759,8 +5017,6 @@ async function getIdToken() {
|
|
|
4759
5017
|
return core.getIdToken(false);
|
|
4760
5018
|
}
|
|
4761
5019
|
|
|
4762
|
-
var bufferExports = requireBuffer();
|
|
4763
|
-
|
|
4764
5020
|
/**
|
|
4765
5021
|
* Extracts a human-readable error message from Solana transaction logs.
|
|
4766
5022
|
*/
|
|
@@ -5149,6 +5405,14 @@ class PrivyExpoProvider {
|
|
|
5149
5405
|
}
|
|
5150
5406
|
}
|
|
5151
5407
|
|
|
5408
|
+
Object.defineProperty(exports, "BoundedDeclineError", {
|
|
5409
|
+
enumerable: true,
|
|
5410
|
+
get: function () { return core.BoundedDeclineError; }
|
|
5411
|
+
});
|
|
5412
|
+
Object.defineProperty(exports, "DECLINE_EVENT_NAME", {
|
|
5413
|
+
enumerable: true,
|
|
5414
|
+
get: function () { return core.DECLINE_EVENT_NAME; }
|
|
5415
|
+
});
|
|
5152
5416
|
Object.defineProperty(exports, "aggregate", {
|
|
5153
5417
|
enumerable: true,
|
|
5154
5418
|
get: function () { return core.aggregate; }
|
|
@@ -5157,6 +5421,14 @@ Object.defineProperty(exports, "count", {
|
|
|
5157
5421
|
enumerable: true,
|
|
5158
5422
|
get: function () { return core.count; }
|
|
5159
5423
|
});
|
|
5424
|
+
Object.defineProperty(exports, "deriveDecline", {
|
|
5425
|
+
enumerable: true,
|
|
5426
|
+
get: function () { return core.deriveDecline; }
|
|
5427
|
+
});
|
|
5428
|
+
Object.defineProperty(exports, "dispatchDeclineEvent", {
|
|
5429
|
+
enumerable: true,
|
|
5430
|
+
get: function () { return core.dispatchDeclineEvent; }
|
|
5431
|
+
});
|
|
5160
5432
|
Object.defineProperty(exports, "docId", {
|
|
5161
5433
|
enumerable: true,
|
|
5162
5434
|
get: function () { return core.docId; }
|