Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/gentle-books-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Allow overriding storage for inApp and ecosystem wallets
1 change: 1 addition & 0 deletions packages/thirdweb/src/exports/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export {
resolveArweaveScheme,
type ResolveArweaveSchemeOptions,
} from "../utils/arweave.js";
export type { AsyncStorage } from "../utils/storage/AsyncStorage.js";
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@ export function ConnectButton(props: ConnectButtonProps) {

usePreloadWalletProviders({
wallets,
client: props.client,
});

// Add props.chain and props.chains to defined chains store
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ export function ConnectEmbed(props: ConnectEmbedProps) {

usePreloadWalletProviders({
wallets,
client: props.client,
});

const modalSize = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { useQueries } from "@tanstack/react-query";
import type { ThirdwebClient } from "../../../client/client.js";
import { COINBASE } from "../../../wallets/constants.js";
import { isEcosystemWallet } from "../../../wallets/ecosystem/is-ecosystem-wallet.js";
import type { Wallet } from "../../../wallets/interfaces/wallet.js";
import type { CreateWalletArgs } from "../../../wallets/wallet-types.js";
import type { EcosystemWalletId } from "../../../wallets/wallet-types.js";

export function usePreloadWalletProviders({
client,
wallets,
}: { client: ThirdwebClient; wallets: Wallet[] }) {
export function usePreloadWalletProviders({ wallets }: { wallets: Wallet[] }) {
useQueries({
queries: wallets
.filter(
Expand All @@ -29,49 +24,6 @@ export function usePreloadWalletProviders({
// return _something_
return true;
}
case "inApp" === w.id: {
const [
{ InAppWebConnector },
{ getOrCreateInAppWalletConnector },
] = await Promise.all([
import("../../../wallets/in-app/web/lib/web-connector.js"),
import("../../../wallets/in-app/core/wallet/in-app-core.js"),
]);
await getOrCreateInAppWalletConnector(client, async (client) => {
return new InAppWebConnector({
client,
});
});
// return _something_
return true;
}
case isEcosystemWallet(w.id): {
const [
{ InAppWebConnector },
{ getOrCreateInAppWalletConnector },
] = await Promise.all([
import("../../../wallets/in-app/web/lib/web-connector.js"),
import("../../../wallets/in-app/core/wallet/in-app-core.js"),
]);
const ecosystemWallet = w as Wallet<EcosystemWalletId>; // we know this is an ecosystem wallet
await getOrCreateInAppWalletConnector(
client,
async (client) => {
return new InAppWebConnector({
client,
ecosystem: {
id: ecosystemWallet.id,
partnerId: ecosystemWallet.getConfig()?.partnerId,
},
});
},
{
id: ecosystemWallet.id,
partnerId: ecosystemWallet.getConfig()?.partnerId,
},
);
return true;
}
// potentially add more wallets here
default: {
return false;
Expand Down
5 changes: 5 additions & 0 deletions packages/thirdweb/src/wallets/ecosystem/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { SupportedSmsCountry } from "../../react/web/wallets/in-app/supported-sms-countries.js";
import type { AsyncStorage } from "../../utils/storage/AsyncStorage.js";
import type {
InAppWalletAutoConnectOptions,
InAppWalletConnectionOptions,
Expand All @@ -23,6 +24,10 @@ export type EcosystemWalletCreationOptions = {
* The partnerId of the ecosystem wallet to connect to
*/
partnerId?: string;
/**
* The storage to use for storing wallet state
*/
storage?: AsyncStorage;
};

export type EcosystemWalletConnectionOptions = InAppWalletConnectionOptions;
Expand Down
5 changes: 5 additions & 0 deletions packages/thirdweb/src/wallets/in-app/core/wallet/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import type { SupportedSmsCountry } from "../../../../react/web/wallets/in-app/supported-sms-countries.js";
import type { AsyncStorage } from "../../../../utils/storage/AsyncStorage.js";
import type { Prettify } from "../../../../utils/type-utils.js";
import type { SmartWalletOptions } from "../../../smart/types.js";
import type {
Expand Down Expand Up @@ -87,5 +88,9 @@ export type InAppWalletCreationOptions =
* Whether to hide the private key export button in the Connect Modal
*/
hidePrivateKeyExport?: boolean;
/**
* The storage to use for storing wallet state
*/
storage?: AsyncStorage;
}
| undefined;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { concat } from "viem";
import type { ThirdwebClient } from "../../../../client/client.js";
import { toBytes } from "../../../../utils/encoding/to-bytes.js";
import { keccak256 } from "../../../../utils/hashing/keccak256.js";
import type { AsyncStorage } from "../../../../utils/storage/AsyncStorage.js";
import { nativeLocalStorage } from "../../../../utils/storage/nativeStorage.js";
import {
base64ToString,
Expand Down Expand Up @@ -121,13 +122,14 @@ export class PasskeyNativeClient implements PasskeyClient {
export async function hasStoredPasskey(
client: ThirdwebClient,
ecosystemId?: EcosystemWalletId,
storage?: AsyncStorage,
) {
const storage = new ClientScopedStorage({
storage: nativeLocalStorage,
const clientStorage = new ClientScopedStorage({
storage: storage ?? nativeLocalStorage,
clientId: client.clientId,
ecosystem: ecosystemId ? { id: ecosystemId } : undefined,
});
const credId = await storage.getPasskeyCredentialId();
const credId = await clientStorage.getPasskeyCredentialId();
return !!credId;
}

Expand Down
1 change: 1 addition & 0 deletions packages/thirdweb/src/wallets/in-app/native/ecosystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export function ecosystemWallet(
return new InAppNativeConnector({
client,
ecosystem,
storage: createOptions?.storage,
// TODO (enclave): passkeyDomain for ecosystem wallets
});
},
Expand Down
1 change: 1 addition & 0 deletions packages/thirdweb/src/wallets/in-app/native/in-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function inAppWallet(
return new InAppNativeConnector({
client,
passkeyDomain: createOptions?.auth?.passkeyDomain,
storage: createOptions?.storage,
});
},
}) as Wallet<"inApp">;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ThirdwebClient } from "../../../client/client.js";
import { stringify } from "../../../utils/json.js";
import type { AsyncStorage } from "../../../utils/storage/AsyncStorage.js";
import { nativeLocalStorage } from "../../../utils/storage/nativeStorage.js";
import type { Account } from "../../interfaces/wallet.js";
import { getUserStatus } from "../core/actions/get-enclave-user-status.js";
Expand Down Expand Up @@ -37,11 +38,11 @@ import { sendOtp, verifyOtp } from "../web/lib/auth/otp.js";
import { deleteActiveAccount, socialAuth } from "./auth/native-auth.js";
import { logoutUser } from "./helpers/auth/logout.js";
import { ShardedWallet } from "./helpers/wallet/sharded-wallet.js";

type NativeConnectorOptions = {
client: ThirdwebClient;
ecosystem?: Ecosystem;
passkeyDomain?: string;
storage?: AsyncStorage;
};

export class InAppNativeConnector implements InAppConnector {
Expand All @@ -56,7 +57,7 @@ export class InAppNativeConnector implements InAppConnector {
this.passkeyDomain = options.passkeyDomain;
this.ecosystem = options.ecosystem;
this.storage = new ClientScopedStorage({
storage: nativeLocalStorage,
storage: options.storage ?? nativeLocalStorage,
clientId: this.client.clientId,
ecosystem: options.ecosystem,
});
Expand Down
1 change: 1 addition & 0 deletions packages/thirdweb/src/wallets/in-app/web/ecosystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function ecosystemWallet(
return new InAppWebConnector({
client,
ecosystem,
storage: createOptions?.storage,
});
},
}) as Wallet<EcosystemWalletId>;
Expand Down
26 changes: 26 additions & 0 deletions packages/thirdweb/src/wallets/in-app/web/in-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,31 @@ import { createInAppWallet } from "../core/wallet/in-app-core.js";
* });
* ```
*
* ### Override storage for the wallet state
*
* By default, wallet state is stored in the browser's local storage. You can override this behavior by providing a custom storage object, useful for server side integrations.
*
* ```ts
* import { inAppWallet } from "thirdweb/wallets";
* import { AsyncStorage } from "thirdweb/storage";
*
* const myStorage: AsyncStorage = {
* getItem: async (key) => {
* return customGet(`CUSTOM_STORAGE_KEY${key}`);
* },
* setItem: async (key, value) => {
* return customSet(`CUSTOM_STORAGE_KEY${key}`, value);
* },
* removeItem: async (key) => {
* return customRemove(`CUSTOM_STORAGE_KEY${key}`);
* },
* };
*
* const wallet = inAppWallet({
* storage: myStorage,
* });
* ```
*
* @returns The created in-app wallet.
* @wallet
*/
Expand All @@ -235,6 +260,7 @@ export function inAppWallet(
return new InAppWebConnector({
client,
passkeyDomain: createOptions?.auth?.passkeyDomain,
storage: createOptions?.storage,
});
},
}) as Wallet<"inApp">;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { client, parsers } from "@passwordless-id/webauthn";
import type { ThirdwebClient } from "../../../../../client/client.js";
import type { AsyncStorage } from "../../../../../utils/storage/AsyncStorage.js";
import { webLocalStorage } from "../../../../../utils/storage/webStorage.js";
import {
base64ToString,
Expand Down Expand Up @@ -83,12 +84,13 @@ export class PasskeyWebClient implements PasskeyClient {
export async function hasStoredPasskey(
client: ThirdwebClient,
ecosystemId?: EcosystemWalletId,
storage?: AsyncStorage,
) {
const storage = new ClientScopedStorage({
storage: webLocalStorage, // TODO (passkey) react native variant of this fn
const clientStorage = new ClientScopedStorage({
storage: storage ?? webLocalStorage, // TODO (passkey) react native variant of this fn
clientId: client.clientId,
ecosystem: ecosystemId ? { id: ecosystemId } : undefined,
});
const credId = await storage.getPasskeyCredentialId();
const credId = await clientStorage.getPasskeyCredentialId();
return !!credId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class InAppWebConnector implements InAppConnector {
onAuthSuccess,
ecosystem,
passkeyDomain,
storage,
}: InAppWalletConstructorType) {
if (this.isClientIdLegacyPaper(client.clientId)) {
throw new Error(
Expand All @@ -85,7 +86,7 @@ export class InAppWebConnector implements InAppConnector {
this.ecosystem = ecosystem;
this.passkeyDomain = passkeyDomain;
this.storage = new ClientScopedStorage({
storage: webLocalStorage,
storage: storage ?? webLocalStorage,
clientId: client.clientId,
ecosystem: ecosystem,
});
Expand Down
7 changes: 6 additions & 1 deletion packages/thirdweb/src/wallets/in-app/web/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// types for class constructors still a little messy right now.

import type { ThirdwebClient } from "../../../client/client.js";
import type { AsyncStorage } from "../../../utils/storage/AsyncStorage.js";
import type { AuthAndWalletRpcReturnType } from "../core/authentication/types.js";
import type { Ecosystem } from "../core/wallet/types.js";
import type { InAppWalletIframeCommunicator } from "./utils/iFrameCommunication/InAppWalletIframeCommunicator.js";

// Open to PRs from whoever sees this and knows of a cleaner way to handle things
type ClientIdConstructorType = {
/**
* the clientId of your API Key. You can create an API key by creating a project on thirdweb dashboard.
Expand All @@ -29,6 +29,11 @@ export type InAppWalletConstructorType = ClientIdConstructorType & {
* The domain of the passkey to use for authentication
*/
passkeyDomain?: string;

/**
* The storage to use for storing wallet state
*/
storage?: AsyncStorage;
};

export type ClientIdWithQuerierType = ClientIdConstructorType & {
Expand Down
Loading