@bounded-sh/client 0.0.37 → 0.0.39
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/auth/index.d.ts +8 -0
- package/dist/auth/providers/injected-wallet-provider.d.ts +30 -0
- package/dist/auth/providers/injected-wallet-types.d.ts +53 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +383 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +384 -72
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +382 -70
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +383 -71
- package/dist/index.native.mjs.map +1 -1
- package/package.json +2 -2
package/dist/auth/index.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ import { AuthProvider, User, ClientConfig } from "@bounded-sh/core";
|
|
|
8
8
|
* are properly cleared.
|
|
9
9
|
*/
|
|
10
10
|
export declare function clearIncompatibleSession(): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the app opted into Solana WALLET LOGIN at init(). OPT-IN, default OFF:
|
|
13
|
+
* enabled only when the developer explicitly passes `walletLogin: true` (or a
|
|
14
|
+
* `walletLogin` / `injectedWalletConfig` object). This gate is what keeps wallet
|
|
15
|
+
* login from being usable (or advertised) by the vast majority of apps that
|
|
16
|
+
* never want it — existing apps that don't opt in see ZERO behavior change.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isWalletLoginEnabled(config: Partial<ClientConfig>): boolean;
|
|
11
19
|
export { SOLANA_DEVNET_RPC_URL, SOLANA_MAINNET_RPC_URL, SURFNET_RPC_URL, } from "./solana-rpc";
|
|
12
20
|
export declare function getAuthProvider(config?: Partial<ClientConfig>): Promise<AuthProvider>;
|
|
13
21
|
/** Per-call overrides forwarded to the active auth provider's modal. */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { EVMTransaction, SolTransaction, TransactionResult, User, AuthProvider, SetOptions } from '@bounded-sh/core';
|
|
2
|
+
import { PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
3
|
+
import { defaultInjectedProvider, type InjectedSolanaProvider, type InjectedWalletConfig } from './injected-wallet-types';
|
|
4
|
+
export { defaultInjectedProvider };
|
|
5
|
+
export type { InjectedSolanaProvider, InjectedWalletConfig };
|
|
6
|
+
export declare class InjectedWalletProvider implements AuthProvider {
|
|
7
|
+
private static instance;
|
|
8
|
+
private config;
|
|
9
|
+
private networkUrl;
|
|
10
|
+
private configuredNetwork;
|
|
11
|
+
constructor(config?: InjectedWalletConfig, networkUrl?: string | null, configuredNetwork?: string | null);
|
|
12
|
+
private resolveProvider;
|
|
13
|
+
/** Connect the injected wallet (if needed) and return its Solana address. */
|
|
14
|
+
private connectAddress;
|
|
15
|
+
login(): Promise<User | null>;
|
|
16
|
+
private markAuthMethod;
|
|
17
|
+
restoreSession(): Promise<User | null>;
|
|
18
|
+
logout(): Promise<void>;
|
|
19
|
+
signMessage(message: string): Promise<string>;
|
|
20
|
+
signTransaction(transaction: Transaction | VersionedTransaction): Promise<Transaction | VersionedTransaction>;
|
|
21
|
+
signAndSubmitTransaction(transaction: Transaction | VersionedTransaction, _feePayer?: PublicKey): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Onchain write path (mirrors the other wallet providers): build the Bounded
|
|
24
|
+
* set-documents transaction with the injected wallet as payer, then sign +
|
|
25
|
+
* submit with the wallet's own keypair.
|
|
26
|
+
*/
|
|
27
|
+
runTransaction(_evmTransactionData?: EVMTransaction, solTransactionData?: SolTransaction, options?: SetOptions): Promise<TransactionResult>;
|
|
28
|
+
getNativeMethods(): Promise<any>;
|
|
29
|
+
private getRpcUrl;
|
|
30
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape of an injected Solana wallet provider (Wallet-Standard
|
|
4
|
+
* compatible). Phantom's `window.phantom.solana` / `window.solana` and most
|
|
5
|
+
* other Solana browser wallets implement this. We only require the methods the
|
|
6
|
+
* login + signing surface needs.
|
|
7
|
+
*/
|
|
8
|
+
export interface InjectedSolanaProvider {
|
|
9
|
+
isPhantom?: boolean;
|
|
10
|
+
publicKey?: {
|
|
11
|
+
toString(): string;
|
|
12
|
+
} | null;
|
|
13
|
+
isConnected?: boolean;
|
|
14
|
+
connect(options?: {
|
|
15
|
+
onlyIfTrusted?: boolean;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
publicKey: {
|
|
18
|
+
toString(): string;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
disconnect(): Promise<void>;
|
|
22
|
+
/** Wallet-Standard: signMessage(Uint8Array) -> { signature, publicKey } */
|
|
23
|
+
signMessage(message: Uint8Array, display?: 'utf8' | 'hex'): Promise<{
|
|
24
|
+
signature: Uint8Array;
|
|
25
|
+
publicKey: {
|
|
26
|
+
toString(): string;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
29
|
+
signTransaction<T extends Transaction | VersionedTransaction>(tx: T): Promise<T>;
|
|
30
|
+
signAllTransactions?<T extends Transaction | VersionedTransaction>(txs: T[]): Promise<T[]>;
|
|
31
|
+
signAndSendTransaction?(tx: Transaction | VersionedTransaction, options?: any): Promise<{
|
|
32
|
+
signature: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface InjectedWalletConfig {
|
|
36
|
+
/**
|
|
37
|
+
* Resolve/pick the injected provider. Defaults to Phantom
|
|
38
|
+
* (`window.phantom.solana`) then a generic `window.solana`. Pass this to
|
|
39
|
+
* point at a specific wallet, or to bridge a custom/mock provider (e.g. an
|
|
40
|
+
* app that already discovered a Wallet-Standard wallet).
|
|
41
|
+
*/
|
|
42
|
+
getProvider?: () => InjectedSolanaProvider | null | undefined;
|
|
43
|
+
/** Solana network for transaction RPC: solana_devnet | solana_mainnet | surfnet. */
|
|
44
|
+
network?: string | null;
|
|
45
|
+
/** Explicit RPC URL override (wins over `network`). */
|
|
46
|
+
rpcUrl?: string | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Default provider discovery: Phantom-first, then any Wallet-Standard
|
|
50
|
+
* `window.solana`. Kept tiny + synchronous (no heavy imports) so it works inside
|
|
51
|
+
* a user gesture and can be eagerly exported from the SDK entry.
|
|
52
|
+
*/
|
|
53
|
+
export declare function defaultInjectedProvider(): InjectedSolanaProvider | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -20,3 +20,6 @@ export type { PlatformAdapter, StorageAdapter } from "./platform";
|
|
|
20
20
|
export { OffchainAuthProvider, OffchainAuthProviderConfig, } from "./auth/providers/offchain-auth-provider";
|
|
21
21
|
export { PrivyExpoProvider } from "./auth/providers/privy-expo-provider";
|
|
22
22
|
export type { PrivyExpoMethods } from "./auth/providers/privy-expo-provider";
|
|
23
|
+
export { defaultInjectedProvider } from "./auth/providers/injected-wallet-types";
|
|
24
|
+
export type { InjectedWalletConfig, InjectedSolanaProvider, } from "./auth/providers/injected-wallet-types";
|
|
25
|
+
export type { InjectedWalletProvider } from "./auth/providers/injected-wallet-provider";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@bounded-sh/core');
|
|
4
|
-
var React = require('react');
|
|
5
4
|
var web3_js = require('@solana/web3.js');
|
|
6
5
|
var anchor = require('@coral-xyz/anchor');
|
|
6
|
+
var React = require('react');
|
|
7
7
|
|
|
8
8
|
function _interopNamespaceDefault(e) {
|
|
9
9
|
var n = Object.create(null);
|
|
@@ -22,8 +22,8 @@ function _interopNamespaceDefault(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
26
25
|
var anchor__namespace = /*#__PURE__*/_interopNamespaceDefault(anchor);
|
|
26
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Platform abstraction layer for React Native compatibility.
|
|
@@ -3445,7 +3445,7 @@ const IDB_STORE = 'keys';
|
|
|
3445
3445
|
const IDB_KEY = 'ed25519';
|
|
3446
3446
|
const GUEST_KEY_REQUIREMENT = 'Guest auth requires non-extractable WebCrypto Ed25519 keys persisted in IndexedDB; ' +
|
|
3447
3447
|
'stored-secret guest credentials are disabled.';
|
|
3448
|
-
function toBase64
|
|
3448
|
+
function toBase64(bytes) {
|
|
3449
3449
|
let bin = '';
|
|
3450
3450
|
for (let i = 0; i < bytes.length; i++)
|
|
3451
3451
|
bin += String.fromCharCode(bytes[i]);
|
|
@@ -3509,7 +3509,7 @@ async function webCryptoKey() {
|
|
|
3509
3509
|
async sign(message) {
|
|
3510
3510
|
const data = new TextEncoder().encode(message);
|
|
3511
3511
|
const sig = new Uint8Array(await s.sign({ name: 'Ed25519' }, pair.privateKey, data));
|
|
3512
|
-
return toBase64
|
|
3512
|
+
return toBase64(sig);
|
|
3513
3513
|
},
|
|
3514
3514
|
};
|
|
3515
3515
|
}
|
|
@@ -3785,19 +3785,22 @@ async function signViaSignerPopup(action) {
|
|
|
3785
3785
|
} }, 5 * 60 * 1000);
|
|
3786
3786
|
});
|
|
3787
3787
|
}
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
//
|
|
3792
|
-
//
|
|
3793
|
-
//
|
|
3788
|
+
// Serialize a legacy or versioned Solana transaction to BASE58 for the signer
|
|
3789
|
+
// page. Crossmint's smart-wallet createTransaction endpoint requires the
|
|
3790
|
+
// `serializedTransaction` string to be base58 — it rejects base64 with
|
|
3791
|
+
// "Invalid base58 string" (verified against Crossmint staging 2026-07-07). The
|
|
3792
|
+
// `wallets-sdk` object path bs58-encodes internally, so base58 is the contract.
|
|
3793
|
+
//
|
|
3794
|
+
// We do NOT require existing signatures (the smart wallet signs on the page);
|
|
3795
|
+
// Crossmint extracts the instructions, overrides the fee payer to its sponsor,
|
|
3796
|
+
// and re-wraps them for the smart wallet.
|
|
3794
3797
|
function serializeTransaction(transaction) {
|
|
3795
3798
|
const isLegacy = 'recentBlockhash' in transaction &&
|
|
3796
3799
|
!('message' in transaction && 'staticAccountKeys' in transaction.message);
|
|
3797
3800
|
const bytes = isLegacy
|
|
3798
3801
|
? transaction.serialize({ requireAllSignatures: false, verifySignatures: false })
|
|
3799
3802
|
: transaction.serialize();
|
|
3800
|
-
return
|
|
3803
|
+
return bs58.encode(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
3801
3804
|
}
|
|
3802
3805
|
/**
|
|
3803
3806
|
* Whether the active session carries an embedded wallet (so the provider should
|
|
@@ -3923,6 +3926,306 @@ function resolveSolanaRpcUrl(explicitRpcUrl, network, context) {
|
|
|
3923
3926
|
throw new Error(`${context} requires an explicit Solana RPC URL. Pass config.rpcUrl before signing or submitting onchain transactions.`);
|
|
3924
3927
|
}
|
|
3925
3928
|
|
|
3929
|
+
/**
|
|
3930
|
+
* Extracts a human-readable error message from Solana transaction logs.
|
|
3931
|
+
*/
|
|
3932
|
+
function extractTransactionError(err, logMessages) {
|
|
3933
|
+
if (logMessages) {
|
|
3934
|
+
return JSON.stringify(logMessages);
|
|
3935
|
+
}
|
|
3936
|
+
if (err) {
|
|
3937
|
+
try {
|
|
3938
|
+
return JSON.stringify(err);
|
|
3939
|
+
}
|
|
3940
|
+
catch (_a) {
|
|
3941
|
+
return String(err);
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
return 'Unknown transaction error';
|
|
3945
|
+
}
|
|
3946
|
+
/**
|
|
3947
|
+
* Polls getSignatureStatuses until the transaction is confirmed, then checks for errors.
|
|
3948
|
+
* Throws if the transaction failed on-chain. Returns parsed transaction info on success.
|
|
3949
|
+
*/
|
|
3950
|
+
async function confirmAndCheckTransaction(connection, signature) {
|
|
3951
|
+
var _a;
|
|
3952
|
+
const maxAttempts = 15;
|
|
3953
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
3954
|
+
const { value } = await connection.getSignatureStatuses([signature]);
|
|
3955
|
+
const status = value[0];
|
|
3956
|
+
if (status) {
|
|
3957
|
+
if (status.err) {
|
|
3958
|
+
const txInfo = await connection.getParsedTransaction(signature, {
|
|
3959
|
+
maxSupportedTransactionVersion: 0,
|
|
3960
|
+
commitment: 'confirmed'
|
|
3961
|
+
});
|
|
3962
|
+
const errorMessage = extractTransactionError(status.err, (_a = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _a === void 0 ? void 0 : _a.logMessages);
|
|
3963
|
+
throw new Error(`Transaction failed: ${errorMessage}`);
|
|
3964
|
+
}
|
|
3965
|
+
if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized') {
|
|
3966
|
+
return await connection.getParsedTransaction(signature, {
|
|
3967
|
+
maxSupportedTransactionVersion: 0,
|
|
3968
|
+
commitment: 'confirmed'
|
|
3969
|
+
});
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
3973
|
+
}
|
|
3974
|
+
throw new Error('Transaction confirmation timeout');
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
/**
|
|
3978
|
+
* Default provider discovery: Phantom-first, then any Wallet-Standard
|
|
3979
|
+
* `window.solana`. Kept tiny + synchronous (no heavy imports) so it works inside
|
|
3980
|
+
* a user gesture and can be eagerly exported from the SDK entry.
|
|
3981
|
+
*/
|
|
3982
|
+
function defaultInjectedProvider() {
|
|
3983
|
+
var _a;
|
|
3984
|
+
if (typeof window === 'undefined')
|
|
3985
|
+
return null;
|
|
3986
|
+
const w = window;
|
|
3987
|
+
const phantom = (_a = w === null || w === void 0 ? void 0 : w.phantom) === null || _a === void 0 ? void 0 : _a.solana;
|
|
3988
|
+
if (phantom && typeof phantom.signMessage === 'function')
|
|
3989
|
+
return phantom;
|
|
3990
|
+
const generic = w === null || w === void 0 ? void 0 : w.solana;
|
|
3991
|
+
if (generic && typeof generic.signMessage === 'function')
|
|
3992
|
+
return generic;
|
|
3993
|
+
return null;
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
const STORED_AUTH_METHOD_KEY$2 = 'bounded_last_auth_method';
|
|
3997
|
+
const INJECTED_AUTH_METHOD = 'phantom';
|
|
3998
|
+
const NO_WALLET_ERR = 'No injected Solana wallet found. Install a wallet like Phantom (phantom.app) and try again, ' +
|
|
3999
|
+
'or pass config.injectedWalletConfig.getProvider to point at your wallet.';
|
|
4000
|
+
class InjectedWalletProvider {
|
|
4001
|
+
constructor(config = {}, networkUrl = null, configuredNetwork = null) {
|
|
4002
|
+
var _a, _b;
|
|
4003
|
+
if (typeof window === 'undefined') {
|
|
4004
|
+
throw new Error('InjectedWalletProvider can only be instantiated in a browser environment');
|
|
4005
|
+
}
|
|
4006
|
+
// Singleton so the SDK reuses one instance (mirrors the other providers).
|
|
4007
|
+
if (InjectedWalletProvider.instance) {
|
|
4008
|
+
InjectedWalletProvider.instance.config = Object.assign(Object.assign({}, InjectedWalletProvider.instance.config), config);
|
|
4009
|
+
return InjectedWalletProvider.instance;
|
|
4010
|
+
}
|
|
4011
|
+
this.config = config;
|
|
4012
|
+
this.networkUrl = (_a = config.rpcUrl) !== null && _a !== void 0 ? _a : networkUrl;
|
|
4013
|
+
this.configuredNetwork = (_b = config.network) !== null && _b !== void 0 ? _b : configuredNetwork;
|
|
4014
|
+
InjectedWalletProvider.instance = this;
|
|
4015
|
+
}
|
|
4016
|
+
resolveProvider() {
|
|
4017
|
+
var _a;
|
|
4018
|
+
const provider = (_a = (this.config.getProvider ? this.config.getProvider() : null)) !== null && _a !== void 0 ? _a : defaultInjectedProvider();
|
|
4019
|
+
if (!provider)
|
|
4020
|
+
throw new Error(NO_WALLET_ERR);
|
|
4021
|
+
return provider;
|
|
4022
|
+
}
|
|
4023
|
+
/** Connect the injected wallet (if needed) and return its Solana address. */
|
|
4024
|
+
async connectAddress(provider, onlyIfTrusted = false) {
|
|
4025
|
+
var _a, _b;
|
|
4026
|
+
// Already connected — reuse the pubkey.
|
|
4027
|
+
if (provider.publicKey && ((_a = provider.isConnected) !== null && _a !== void 0 ? _a : true)) {
|
|
4028
|
+
return provider.publicKey.toString();
|
|
4029
|
+
}
|
|
4030
|
+
const res = await provider.connect(onlyIfTrusted ? { onlyIfTrusted: true } : undefined);
|
|
4031
|
+
const pk = (_b = res === null || res === void 0 ? void 0 : res.publicKey) !== null && _b !== void 0 ? _b : provider.publicKey;
|
|
4032
|
+
if (!pk)
|
|
4033
|
+
throw new Error('Wallet did not return a public key on connect');
|
|
4034
|
+
return pk.toString();
|
|
4035
|
+
}
|
|
4036
|
+
async login() {
|
|
4037
|
+
var _a;
|
|
4038
|
+
setAuthLoading(true);
|
|
4039
|
+
try {
|
|
4040
|
+
const provider = this.resolveProvider();
|
|
4041
|
+
const address = await this.connectAddress(provider);
|
|
4042
|
+
// Reuse a still-valid session for THIS wallet.
|
|
4043
|
+
const existing = await core.getActiveSessionManager().getSession();
|
|
4044
|
+
if (existing && existing.address === address) {
|
|
4045
|
+
const user = { address, id: address, provider: this };
|
|
4046
|
+
setCurrentUser(user);
|
|
4047
|
+
this.markAuthMethod();
|
|
4048
|
+
return user;
|
|
4049
|
+
}
|
|
4050
|
+
// SIWS: nonce -> canonical prompt -> the wallet signs it locally.
|
|
4051
|
+
const nonce = await core.genAuthNonce();
|
|
4052
|
+
const message = await core.genSolanaMessage(address, nonce);
|
|
4053
|
+
const messageBytes = getPlatform().textEncode(message);
|
|
4054
|
+
const signResult = await provider.signMessage(messageBytes, 'utf8');
|
|
4055
|
+
const signatureBytes = (_a = signResult === null || signResult === void 0 ? void 0 : signResult.signature) !== null && _a !== void 0 ? _a : signResult;
|
|
4056
|
+
// base64 detached ed25519 signature — matches guest/privy providers and
|
|
4057
|
+
// the bounded-auth issuer's base64ToBytes() verify path.
|
|
4058
|
+
const signature = bufferExports.Buffer.from(signatureBytes).toString('base64');
|
|
4059
|
+
const result = await core.createSessionWithSignature(address, message, signature);
|
|
4060
|
+
await core.getActiveSessionManager().storeSession(address, result.accessToken, result.idToken, result.refreshToken);
|
|
4061
|
+
this.markAuthMethod();
|
|
4062
|
+
const user = { address, id: address, provider: this };
|
|
4063
|
+
setCurrentUser(user);
|
|
4064
|
+
return user;
|
|
4065
|
+
}
|
|
4066
|
+
finally {
|
|
4067
|
+
setAuthLoading(false);
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
markAuthMethod() {
|
|
4071
|
+
// Mark the auth method so clearIncompatibleSession() doesn't wipe this
|
|
4072
|
+
// wallet session on the next init().
|
|
4073
|
+
try {
|
|
4074
|
+
getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY$2, INJECTED_AUTH_METHOD);
|
|
4075
|
+
}
|
|
4076
|
+
catch ( /* no storage */_a) { /* no storage */ }
|
|
4077
|
+
}
|
|
4078
|
+
async restoreSession() {
|
|
4079
|
+
const session = await core.getActiveSessionManager().getSession();
|
|
4080
|
+
if (!session)
|
|
4081
|
+
return null;
|
|
4082
|
+
const user = { address: session.address, id: session.address, provider: this };
|
|
4083
|
+
setCurrentUser(user);
|
|
4084
|
+
return user;
|
|
4085
|
+
}
|
|
4086
|
+
async logout() {
|
|
4087
|
+
// Clear the Bounded session; best-effort disconnect the wallet.
|
|
4088
|
+
core.getActiveSessionManager().clearSession();
|
|
4089
|
+
try {
|
|
4090
|
+
const provider = this.config.getProvider ? this.config.getProvider() : defaultInjectedProvider();
|
|
4091
|
+
if (provider && typeof provider.disconnect === 'function')
|
|
4092
|
+
await provider.disconnect();
|
|
4093
|
+
}
|
|
4094
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
4095
|
+
setCurrentUser(null);
|
|
4096
|
+
}
|
|
4097
|
+
// --- Full local-keypair signing surface (the wallet's OWN methods) --------
|
|
4098
|
+
async signMessage(message) {
|
|
4099
|
+
var _a;
|
|
4100
|
+
const provider = this.resolveProvider();
|
|
4101
|
+
await this.connectAddress(provider, true).catch(() => this.connectAddress(provider));
|
|
4102
|
+
const messageBytes = getPlatform().textEncode(message);
|
|
4103
|
+
const res = await provider.signMessage(messageBytes, 'utf8');
|
|
4104
|
+
const signatureBytes = (_a = res === null || res === void 0 ? void 0 : res.signature) !== null && _a !== void 0 ? _a : res;
|
|
4105
|
+
// base58 is the standard Solana signature encoding for app-facing signMessage.
|
|
4106
|
+
return bs58.encode(signatureBytes instanceof Uint8Array ? signatureBytes : new Uint8Array(signatureBytes));
|
|
4107
|
+
}
|
|
4108
|
+
async signTransaction(transaction) {
|
|
4109
|
+
const provider = this.resolveProvider();
|
|
4110
|
+
await this.connectAddress(provider, true).catch(() => this.connectAddress(provider));
|
|
4111
|
+
return provider.signTransaction(transaction);
|
|
4112
|
+
}
|
|
4113
|
+
async signAndSubmitTransaction(transaction, _feePayer) {
|
|
4114
|
+
const provider = this.resolveProvider();
|
|
4115
|
+
const address = await this.connectAddress(provider, true).catch(() => this.connectAddress(provider));
|
|
4116
|
+
const rpcUrl = this.getRpcUrl();
|
|
4117
|
+
const connection = new web3_js.Connection(rpcUrl, 'confirmed');
|
|
4118
|
+
const isSurfnet = rpcUrl === SURFNET_RPC_URL;
|
|
4119
|
+
// Set a fresh blockhash on the tx (mirrors the other providers).
|
|
4120
|
+
const { blockhash } = await connection.getLatestBlockhash('confirmed');
|
|
4121
|
+
const isLegacy = 'recentBlockhash' in transaction &&
|
|
4122
|
+
!('message' in transaction && 'staticAccountKeys' in transaction.message);
|
|
4123
|
+
if (isLegacy) {
|
|
4124
|
+
const legacy = transaction;
|
|
4125
|
+
legacy.recentBlockhash = blockhash;
|
|
4126
|
+
if (!legacy.feePayer)
|
|
4127
|
+
legacy.feePayer = new web3_js.PublicKey(address);
|
|
4128
|
+
}
|
|
4129
|
+
else {
|
|
4130
|
+
transaction.message.recentBlockhash = blockhash;
|
|
4131
|
+
}
|
|
4132
|
+
// Prefer the wallet's native signAndSend on live networks; fall back to
|
|
4133
|
+
// sign + manual submit (needed for surfnet / wallets without send).
|
|
4134
|
+
if (!isSurfnet && typeof provider.signAndSendTransaction === 'function') {
|
|
4135
|
+
const res = await provider.signAndSendTransaction(transaction);
|
|
4136
|
+
const signature = res.signature;
|
|
4137
|
+
await confirmAndCheckTransaction(connection, signature);
|
|
4138
|
+
return signature;
|
|
4139
|
+
}
|
|
4140
|
+
const signed = await provider.signTransaction(transaction);
|
|
4141
|
+
const raw = signed.serialize();
|
|
4142
|
+
const signature = await connection.sendRawTransaction(raw);
|
|
4143
|
+
await confirmAndCheckTransaction(connection, signature);
|
|
4144
|
+
return signature;
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* Onchain write path (mirrors the other wallet providers): build the Bounded
|
|
4148
|
+
* set-documents transaction with the injected wallet as payer, then sign +
|
|
4149
|
+
* submit with the wallet's own keypair.
|
|
4150
|
+
*/
|
|
4151
|
+
async runTransaction(_evmTransactionData, solTransactionData, options) {
|
|
4152
|
+
var _a, _b;
|
|
4153
|
+
if (!solTransactionData) {
|
|
4154
|
+
throw new Error('Solana transaction data is required for wallet login');
|
|
4155
|
+
}
|
|
4156
|
+
const provider = this.resolveProvider();
|
|
4157
|
+
const address = await this.connectAddress(provider, true).catch(() => this.connectAddress(provider));
|
|
4158
|
+
const publicKey = new web3_js.PublicKey(address);
|
|
4159
|
+
const rpcUrl = this.getRpcUrl(solTransactionData.network);
|
|
4160
|
+
const connection = new web3_js.Connection(rpcUrl, 'confirmed');
|
|
4161
|
+
const isSurfnet = rpcUrl === SURFNET_RPC_URL;
|
|
4162
|
+
const app_id = solTransactionData.appId;
|
|
4163
|
+
if (!app_id)
|
|
4164
|
+
throw new Error('App ID is required');
|
|
4165
|
+
const walletAdapter = {
|
|
4166
|
+
publicKey,
|
|
4167
|
+
signTransaction: async (tx) => provider.signTransaction(tx),
|
|
4168
|
+
signAllTransactions: async (txs) => provider.signAllTransactions
|
|
4169
|
+
? provider.signAllTransactions(txs)
|
|
4170
|
+
: Promise.all(txs.map((t) => provider.signTransaction(t))),
|
|
4171
|
+
};
|
|
4172
|
+
const anchorProvider = new anchor__namespace.AnchorProvider(connection, walletAdapter, anchor__namespace.AnchorProvider.defaultOptions());
|
|
4173
|
+
const remainingAccounts = core.convertRemainingAccounts(solTransactionData.txArgs[0].remainingAccounts);
|
|
4174
|
+
const finalDeduped = [];
|
|
4175
|
+
for (const acc of remainingAccounts) {
|
|
4176
|
+
const existing = finalDeduped.find((d) => d.pubkey.equals(acc.pubkey));
|
|
4177
|
+
if (existing) {
|
|
4178
|
+
existing.isSigner = existing.isSigner || acc.isSigner;
|
|
4179
|
+
existing.isWritable = existing.isWritable || acc.isWritable;
|
|
4180
|
+
}
|
|
4181
|
+
else {
|
|
4182
|
+
finalDeduped.push(acc);
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
let tx;
|
|
4186
|
+
if (solTransactionData.signedTransaction) {
|
|
4187
|
+
// Server co-signed (CPI attestation) — just add the wallet signature.
|
|
4188
|
+
tx = web3_js.VersionedTransaction.deserialize(bufferExports.Buffer.from(solTransactionData.signedTransaction, 'base64'));
|
|
4189
|
+
}
|
|
4190
|
+
else {
|
|
4191
|
+
const result = await core.buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, publicKey, {
|
|
4192
|
+
app_id,
|
|
4193
|
+
documents: solTransactionData.txArgs[0].setDocumentData,
|
|
4194
|
+
delete_paths: solTransactionData.txArgs[0].deletePaths,
|
|
4195
|
+
txData: solTransactionData.txArgs[0].txData,
|
|
4196
|
+
}, finalDeduped, solTransactionData.lutKey, solTransactionData.preInstructions, false, solTransactionData.additionalLutAddresses);
|
|
4197
|
+
tx = result.tx;
|
|
4198
|
+
}
|
|
4199
|
+
if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
|
|
4200
|
+
const signedTx = await walletAdapter.signTransaction(tx);
|
|
4201
|
+
return { signedTransaction: signedTx, blockNumber: 0, gasUsed: '0', data: '' };
|
|
4202
|
+
}
|
|
4203
|
+
let signature;
|
|
4204
|
+
if (!isSurfnet && typeof provider.signAndSendTransaction === 'function') {
|
|
4205
|
+
const res = await provider.signAndSendTransaction(tx);
|
|
4206
|
+
signature = res.signature;
|
|
4207
|
+
}
|
|
4208
|
+
else {
|
|
4209
|
+
const signedTx = await walletAdapter.signTransaction(tx);
|
|
4210
|
+
signature = await connection.sendRawTransaction(signedTx.serialize());
|
|
4211
|
+
}
|
|
4212
|
+
const txInfo = await confirmAndCheckTransaction(connection, signature);
|
|
4213
|
+
return {
|
|
4214
|
+
transactionSignature: signature,
|
|
4215
|
+
blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
|
|
4216
|
+
gasUsed: ((_b = (_a = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _a === void 0 ? void 0 : _a.fee) === null || _b === void 0 ? void 0 : _b.toString()) || '0',
|
|
4217
|
+
data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
async getNativeMethods() {
|
|
4221
|
+
return this.resolveProvider();
|
|
4222
|
+
}
|
|
4223
|
+
getRpcUrl(network) {
|
|
4224
|
+
return resolveSolanaRpcUrl(this.networkUrl, network !== null && network !== void 0 ? network : this.configuredNetwork, 'Wallet-login Solana transaction');
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
InjectedWalletProvider.instance = null;
|
|
4228
|
+
|
|
3926
4229
|
// Same message the email provider uses when a hosted (OIDC) login has no
|
|
3927
4230
|
// embedded wallet — enable `auth.wallets` in policy to get one.
|
|
3928
4231
|
const OIDC_NO_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
@@ -4414,7 +4717,7 @@ function stableConfigValue(value, seen = new WeakSet()) {
|
|
|
4414
4717
|
return String(value);
|
|
4415
4718
|
}
|
|
4416
4719
|
function authProviderConfigKey(config) {
|
|
4417
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4720
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
4418
4721
|
return JSON.stringify({
|
|
4419
4722
|
authMethod: (_a = config.authMethod) !== null && _a !== void 0 ? _a : null,
|
|
4420
4723
|
chain: (_b = config.chain) !== null && _b !== void 0 ? _b : null,
|
|
@@ -4422,9 +4725,11 @@ function authProviderConfigKey(config) {
|
|
|
4422
4725
|
name: (_c = config.name) !== null && _c !== void 0 ? _c : null,
|
|
4423
4726
|
logoUrl: (_d = config.logoUrl) !== null && _d !== void 0 ? _d : null,
|
|
4424
4727
|
phantomConfig: stableConfigValue((_e = config.phantomConfig) !== null && _e !== void 0 ? _e : null),
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4728
|
+
walletLogin: stableConfigValue((_f = config.walletLogin) !== null && _f !== void 0 ? _f : null),
|
|
4729
|
+
injectedWalletConfig: stableConfigValue((_g = config.injectedWalletConfig) !== null && _g !== void 0 ? _g : null),
|
|
4730
|
+
privyConfig: stableConfigValue((_h = config.privyConfig) !== null && _h !== void 0 ? _h : null),
|
|
4731
|
+
mobileWalletConfig: stableConfigValue((_j = config.mobileWalletConfig) !== null && _j !== void 0 ? _j : null),
|
|
4732
|
+
privyExpoProvider: objectIdentityKey((_k = config.privyExpoProvider) !== null && _k !== void 0 ? _k : null),
|
|
4428
4733
|
});
|
|
4429
4734
|
}
|
|
4430
4735
|
/**
|
|
@@ -4457,6 +4762,47 @@ function getConfiguredSolanaNetwork(config) {
|
|
|
4457
4762
|
return null;
|
|
4458
4763
|
return chain;
|
|
4459
4764
|
}
|
|
4765
|
+
// Error thrown when an app tries wallet login WITHOUT opting in at init().
|
|
4766
|
+
// Wallet login is OFF by default — most apps never support wallets, so the
|
|
4767
|
+
// wallet choice is only usable when the developer explicitly asked for it.
|
|
4768
|
+
function walletLoginNotEnabledError(method) {
|
|
4769
|
+
return new Error(`Bounded: Solana wallet login (authMethod:"${method}") is OFF by default and not enabled for this app. ` +
|
|
4770
|
+
`Wallet login is opt-in — pass \`walletLogin: true\` (or a \`walletLogin\` / \`injectedWalletConfig\` object) ` +
|
|
4771
|
+
`to init() to enable the "connect wallet" choice. Most apps don't need it; use hosted login ` +
|
|
4772
|
+
`loginWithRedirect(...) for email/social, or signInAnonymously() for zero-friction guests.`);
|
|
4773
|
+
}
|
|
4774
|
+
/**
|
|
4775
|
+
* Whether the app opted into Solana WALLET LOGIN at init(). OPT-IN, default OFF:
|
|
4776
|
+
* enabled only when the developer explicitly passes `walletLogin: true` (or a
|
|
4777
|
+
* `walletLogin` / `injectedWalletConfig` object). This gate is what keeps wallet
|
|
4778
|
+
* login from being usable (or advertised) by the vast majority of apps that
|
|
4779
|
+
* never want it — existing apps that don't opt in see ZERO behavior change.
|
|
4780
|
+
*/
|
|
4781
|
+
function isWalletLoginEnabled(config) {
|
|
4782
|
+
const wl = config.walletLogin;
|
|
4783
|
+
if (wl === true)
|
|
4784
|
+
return true;
|
|
4785
|
+
if (wl && typeof wl === 'object')
|
|
4786
|
+
return true;
|
|
4787
|
+
if (config.injectedWalletConfig && typeof config.injectedWalletConfig === 'object')
|
|
4788
|
+
return true;
|
|
4789
|
+
return false;
|
|
4790
|
+
}
|
|
4791
|
+
/**
|
|
4792
|
+
* Resolve the injected-wallet-config the provider should use, merging the
|
|
4793
|
+
* `walletLogin` object form (if the app passed one) over `injectedWalletConfig`.
|
|
4794
|
+
* `walletLogin: true` (bare boolean) enables login with default provider
|
|
4795
|
+
* discovery (Phantom-first) and no RPC override.
|
|
4796
|
+
*/
|
|
4797
|
+
function resolveInjectedWalletConfig(config) {
|
|
4798
|
+
const base = (config.injectedWalletConfig && typeof config.injectedWalletConfig === 'object')
|
|
4799
|
+
? config.injectedWalletConfig
|
|
4800
|
+
: {};
|
|
4801
|
+
const wl = config.walletLogin;
|
|
4802
|
+
if (wl && typeof wl === 'object')
|
|
4803
|
+
return Object.assign(Object.assign({}, base), wl);
|
|
4804
|
+
return base;
|
|
4805
|
+
}
|
|
4460
4806
|
function optionalProviderError(method) {
|
|
4461
4807
|
return new Error(`Bounded authMethod "${method}" uses an optional wallet/provider SDK that is no longer ` +
|
|
4462
4808
|
`pulled into the default @bounded-sh/client entry. Use hosted login ` +
|
|
@@ -4479,8 +4825,8 @@ async function getAuthProvider(config) {
|
|
|
4479
4825
|
}
|
|
4480
4826
|
initConfig = config;
|
|
4481
4827
|
const authMethod = (_a = config.authMethod) !== null && _a !== void 0 ? _a : "email";
|
|
4482
|
-
normalizeSolanaRpcUrl(config.rpcUrl);
|
|
4483
|
-
getConfiguredSolanaNetwork(config);
|
|
4828
|
+
const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
|
|
4829
|
+
const configuredNetwork = getConfiguredSolanaNetwork(config);
|
|
4484
4830
|
currentAuthMethod = authMethod;
|
|
4485
4831
|
// Clear, actionable error when the BROWSER build (`bounded-sh`) is loaded in a
|
|
4486
4832
|
// non-browser (Node) runtime. The wallet providers below need `window`; without
|
|
@@ -4488,7 +4834,7 @@ async function getAuthProvider(config) {
|
|
|
4488
4834
|
// browser environment" deep in init(). The server build (`bounded-sh/server`)
|
|
4489
4835
|
// never resolves these methods (it signs with a keypair), so this can't fire for
|
|
4490
4836
|
// it — it only converts the cryptic browser-in-Node crash into a useful message.
|
|
4491
|
-
const browserWalletMethods = ['phantom', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'mobile-wallet-adapter', 'privy'];
|
|
4837
|
+
const browserWalletMethods = ['phantom', 'wallet', 'rainbowkit', 'coinbase-smart-wallet', 'onboard', 'mobile-wallet-adapter', 'privy'];
|
|
4492
4838
|
if (typeof window === 'undefined' && browserWalletMethods.includes(authMethod)) {
|
|
4493
4839
|
throw new Error(`bounded-sh: '${authMethod}' auth requires a browser, but no \`window\` was found ` +
|
|
4494
4840
|
`(you're likely running in Node). In server/Node code, import from 'bounded-sh/server' ` +
|
|
@@ -4504,14 +4850,28 @@ async function getAuthProvider(config) {
|
|
|
4504
4850
|
currentAuthProvider = new EmailAuthProvider();
|
|
4505
4851
|
break;
|
|
4506
4852
|
case "wallet":
|
|
4507
|
-
|
|
4853
|
+
case "phantom": {
|
|
4854
|
+
// First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
|
|
4855
|
+
// wallet" choice). Slim path: rides the injected Solana provider a
|
|
4856
|
+
// browser wallet exposes (Phantom's window.phantom.solana, or any
|
|
4857
|
+
// Wallet-Standard window.solana). The user's REAL wallet becomes
|
|
4858
|
+
// @user.address; login = SIWS; full local signing surface. No React
|
|
4859
|
+
// and no heavy wallet-SDK peer dep. 'wallet' is an alias for the same
|
|
4860
|
+
// path.
|
|
4861
|
+
//
|
|
4862
|
+
// OPT-IN, DEFAULT OFF: only usable when the app explicitly enabled it
|
|
4863
|
+
// at init() (walletLogin: true). Otherwise throw a clear, actionable
|
|
4864
|
+
// error — most apps never support wallets, so we never construct the
|
|
4865
|
+
// provider (or pay its lazy chunk) for them.
|
|
4866
|
+
if (!isWalletLoginEnabled(config)) {
|
|
4867
|
+
throw walletLoginNotEnabledError(authMethod);
|
|
4868
|
+
}
|
|
4869
|
+
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
4508
4870
|
break;
|
|
4871
|
+
}
|
|
4509
4872
|
case "rainbowkit":
|
|
4510
4873
|
console.warn("Rainbow Kit auth is not yet supported.");
|
|
4511
4874
|
break;
|
|
4512
|
-
case "phantom": {
|
|
4513
|
-
throw optionalProviderError("phantom");
|
|
4514
|
-
}
|
|
4515
4875
|
case "privy": {
|
|
4516
4876
|
throw optionalProviderError("privy");
|
|
4517
4877
|
}
|
|
@@ -5017,54 +5377,6 @@ async function getIdToken() {
|
|
|
5017
5377
|
return core.getIdToken(false);
|
|
5018
5378
|
}
|
|
5019
5379
|
|
|
5020
|
-
/**
|
|
5021
|
-
* Extracts a human-readable error message from Solana transaction logs.
|
|
5022
|
-
*/
|
|
5023
|
-
function extractTransactionError(err, logMessages) {
|
|
5024
|
-
if (logMessages) {
|
|
5025
|
-
return JSON.stringify(logMessages);
|
|
5026
|
-
}
|
|
5027
|
-
if (err) {
|
|
5028
|
-
try {
|
|
5029
|
-
return JSON.stringify(err);
|
|
5030
|
-
}
|
|
5031
|
-
catch (_a) {
|
|
5032
|
-
return String(err);
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
return 'Unknown transaction error';
|
|
5036
|
-
}
|
|
5037
|
-
/**
|
|
5038
|
-
* Polls getSignatureStatuses until the transaction is confirmed, then checks for errors.
|
|
5039
|
-
* Throws if the transaction failed on-chain. Returns parsed transaction info on success.
|
|
5040
|
-
*/
|
|
5041
|
-
async function confirmAndCheckTransaction(connection, signature) {
|
|
5042
|
-
var _a;
|
|
5043
|
-
const maxAttempts = 15;
|
|
5044
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
5045
|
-
const { value } = await connection.getSignatureStatuses([signature]);
|
|
5046
|
-
const status = value[0];
|
|
5047
|
-
if (status) {
|
|
5048
|
-
if (status.err) {
|
|
5049
|
-
const txInfo = await connection.getParsedTransaction(signature, {
|
|
5050
|
-
maxSupportedTransactionVersion: 0,
|
|
5051
|
-
commitment: 'confirmed'
|
|
5052
|
-
});
|
|
5053
|
-
const errorMessage = extractTransactionError(status.err, (_a = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _a === void 0 ? void 0 : _a.logMessages);
|
|
5054
|
-
throw new Error(`Transaction failed: ${errorMessage}`);
|
|
5055
|
-
}
|
|
5056
|
-
if (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized') {
|
|
5057
|
-
return await connection.getParsedTransaction(signature, {
|
|
5058
|
-
maxSupportedTransactionVersion: 0,
|
|
5059
|
-
commitment: 'confirmed'
|
|
5060
|
-
});
|
|
5061
|
-
}
|
|
5062
|
-
}
|
|
5063
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
5064
|
-
}
|
|
5065
|
-
throw new Error('Transaction confirmation timeout');
|
|
5066
|
-
}
|
|
5067
|
-
|
|
5068
5380
|
/**
|
|
5069
5381
|
* Privy Expo Auth Provider — React Native implementation of AuthProvider.
|
|
5070
5382
|
*
|
|
@@ -5505,6 +5817,7 @@ exports.OffchainAuthProvider = OffchainAuthProvider;
|
|
|
5505
5817
|
exports.PrivyExpoProvider = PrivyExpoProvider;
|
|
5506
5818
|
exports.completeLoginFromRedirect = completeLoginFromRedirect;
|
|
5507
5819
|
exports.completeLoginInPopup = completeLoginInPopup;
|
|
5820
|
+
exports.defaultInjectedProvider = defaultInjectedProvider;
|
|
5508
5821
|
exports.deserializeTransaction = deserializeTransaction;
|
|
5509
5822
|
exports.getAuthLoading = getAuthLoading;
|
|
5510
5823
|
exports.getAuthProvider = getAuthProvider;
|