@thirdweb-dev/wallets
Version:
<p align="center"> <br /> <a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/legacy_packages/sdk/logo.svg?raw=true" width="200" alt=""/></a> <br /> </p> <h1 align="center">thirdweb Wallet SDK</h1> <p align="center"> <a h
361 lines (335 loc) • 9.54 kB
JavaScript
import { defaultChains, updateChainRPCs } from '@thirdweb-dev/chains';
import { AbstractWallet } from '../evm/wallets/abstract/dist/thirdweb-dev-wallets-evm-wallets-abstract.browser.esm.js';
import { g as getAnalyticsHeaders } from './headers-733a8199.browser.esm.js';
const PREFIX = "__TW__";
/**
* @internal
*/
class AsyncLocalStorage {
constructor(name) {
this.name = name;
}
getItem(key) {
return new Promise(res => {
res(localStorage.getItem(`${PREFIX}/${this.name}/${key}`));
});
}
setItem(key, value) {
return new Promise((res, rej) => {
try {
localStorage.setItem(`${PREFIX}/${this.name}/${key}`, value);
res();
} catch (e) {
rej(e);
}
});
}
removeItem(key) {
return new Promise(res => {
localStorage.removeItem(`${PREFIX}/${this.name}/${key}`);
res();
});
}
}
/**
* @internal
*/
function createAsyncLocalStorage(name) {
return new AsyncLocalStorage(name);
}
/**
* @internal
*/
const DEFAULT_DAPP_META = {
name: "thirdweb powered dApp",
url: "https://thirdweb.com",
description: "thirdweb powered dApp",
logoUrl: "https://thirdweb.com/favicon.ico",
isDarkMode: true
};
let walletAnalyticsEnabled = true;
/**
* Check if Wallet Analytics is enabled or not
*
* @example
* ```ts
* import { isWalletAnalyticsEnabled } from '@thirdweb/wallets';
*
* const isAnalyticsEnabled = isWalletAnalyticsEnabled();
* ```
*
* The Wallet analytics can be used to track:
* - Total and Unique users
* - Users connected over time
* - Type of wallets connected
* - Distribution of wallets connected
*
* You can view these Analytics in the [ThirdWeb Wallet Analytics dashboard](https://thirdweb.com/dashboard/wallets/analytics)
*
* By default it is enabled. You can disable it by calling `setWalletAnalyticsEnabled(false)`
*
* ```ts
* import { setWalletAnalyticsEnabled } from '@thirdweb/wallets';
*
* setWalletAnalyticsEnabled(false);
* ```
*
*/
function isWalletAnalyticsEnabled() {
return walletAnalyticsEnabled;
}
/**
* Enable or disable Wallet Analytics
*
* @example
* ```ts
* import { setWalletAnalyticsEnabled } from '@thirdweb/wallets';
*
* setWalletAnalyticsEnabled(false);
* ```
*
* The Wallet analytics can be used to track:
* - Total and Unique users
* - Users connected over time
* - Type of wallets connected
* - Distribution of wallets connected
*
* You can view these Analytics in the [ThirdWeb Wallet Analytics dashboard](https://thirdweb.com/dashboard/wallets/analytics)
*
* By default, The Wallet Analytics is enabled
*
* You can check if it is enabled or not by calling `isWalletAnalyticsEnabled()`
* ```ts
* import { isWalletAnalyticsEnabled } from '@thirdweb/wallets';
*
* const isAnalyticsEnabled = isWalletAnalyticsEnabled();
* ```
*
*/
function setWalletAnalyticsEnabled(enabled) {
walletAnalyticsEnabled = enabled;
}
const ANALYTICS_ENDPOINT = "https://c.thirdweb.com/event";
function track(args) {
if (!isWalletAnalyticsEnabled()) {
return;
}
const {
clientId,
walletType,
walletAddress,
source,
action
} = args;
const body = {
source,
action,
walletAddress,
walletType
};
// don't block on analytic calls
fetch(ANALYTICS_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-client-id": clientId,
...getAnalyticsHeaders()
},
body: JSON.stringify(body)
});
}
/* eslint-disable @typescript-eslint/ban-types */
/**
* General options required for creating a wallet instance
*/
/**
* The base class for all client-side wallets (web, mobile) in the Wallet SDK. It extends AbstractWallet and adds client side specific logic.
* A client side wallet delegates the wallet-specific connection logic to a Connector.
*
* This wallet is not meant to be used directly, but instead be extended to [build your own wallet](https://portal.thirdweb.com/wallet-sdk/v2/build)
*
* @abstractWallet
*/
class AbstractClientWallet extends AbstractWallet {
/**
* @internal
*/
/**
* @internal
*/
getMeta() {
return this.constructor.meta;
}
/**
* Creates an returns instance of `AbstractClientWallet`
*
* @param walletId - A Unique identifier for the wallet ( name of the wallet )
* @param options - Options for creating wallet instance
*/
constructor(walletId, options) {
super();
this.walletId = walletId;
this.options = options;
this.chains = (options?.chains || defaultChains).map(c => updateChainRPCs(c, options?.clientId));
this.dappMetadata = options?.dappMetadata || DEFAULT_DAPP_META;
this.walletStorage = options?.walletStorage || createAsyncLocalStorage(this.walletId);
}
/**
* Returns the Wallet Connector used by the wallet
*/
/**
* auto-connect the wallet if possible
* @returns
*/
async autoConnect(connectOptions) {
// remove chainId when auto-connecting to prevent switch-network popup on page load
const options = connectOptions ? {
...connectOptions,
chainId: undefined
} : undefined;
return this._connect(true, options);
}
/**
* Connect wallet
* @param connectOptions - Options for connecting to the wallet
* @returns
*/
async connect(connectOptions) {
this._connectParams = connectOptions;
const address = await this._connect(false, connectOptions);
if (!address) {
throw new Error("Failed to connect to the wallet.");
}
return address;
}
/**
* @internal
* Get the options used for connecting to the wallet
* @returns
*/
getConnectParams() {
return this._connectParams;
}
/**
* @internal
* Get the options used for creating the wallet instance
*/
getOptions() {
return this.options;
}
async _connect(isAutoConnect, connectOptions) {
const connector = await this.getConnector();
this._subscribeToEvents(connector);
const isConnected = await connector.isConnected();
// if already connected, return the address and setup listeners
if (isConnected) {
const address = await connector.getAddress();
connector.setupListeners();
// ensure that connector is connected to the correct chain
if (connectOptions?.chainId) {
await connector.switchChain(connectOptions?.chainId);
}
this.emit("connect", {
address,
chainId: await this.getChainId()
});
this._trackConnection(address);
return address;
}
if (isAutoConnect) {
throw new Error("Failed to auto connect to the wallet.");
}
try {
const address = await connector.connect(connectOptions);
this._trackConnection(address);
return address;
} catch (error) {
throw new Error(error.message);
}
}
_trackConnection(address) {
track({
clientId: this.options?.clientId || "",
source: "connectWallet",
action: "connect",
walletType: this.walletId,
walletAddress: address
});
}
async _subscribeToEvents(connector) {
// subscribe to connector for events
connector.on("connect", data => {
this.emit("connect", {
address: data.account,
chainId: data.chain?.id
});
});
connector.on("change", data => {
this.emit("change", {
address: data.account,
chainId: data.chain?.id
});
});
connector.on("message", data => {
this.emit("message", data);
});
connector.on("disconnect", async () => {
this.emit("disconnect");
});
connector.on("error", error => this.emit("error", error));
}
/**
* Get [ethers Signer](https://docs.ethers.org/v5/api/signer/) object of the connected wallet
*/
async getSigner() {
const connector = await this.getConnector();
if (!connector) {
throw new Error("Wallet not connected");
}
return await connector.getSigner();
}
/**
* Disconnect the wallet
*/
async disconnect() {
const connector = await this.getConnector();
if (connector) {
await connector.disconnect();
this.emit("disconnect");
connector.removeAllListeners();
}
}
/**
* Switch to different Network/Blockchain in the connected wallet
* @param chainId - The chainId of the network to switch to
*/
async switchChain(chainId) {
const connector = await this.getConnector();
if (!connector) {
throw new Error("Wallet not connected");
}
if (!connector.switchChain) {
throw new Error("Wallet does not support switching chains");
}
return await connector.switchChain(chainId);
}
/**
* Update the chains supported by the wallet. This is useful if wallet was initialized with some chains and this needs to be updated without re-initializing the wallet
*/
async updateChains(chains) {
this.chains = chains.map(c => {
return updateChainRPCs(c, this.options?.clientId);
});
const connector = await this.getConnector();
connector.updateChains(this.chains);
}
/**
* If the wallet uses another "personal wallet" under the hood, return it
*
* This is only useful for wallets like Safe or Smart Wallet uses a "personal wallet" under the hood to sign transactions. This method returns that wallet
*/
getPersonalWallet() {
return undefined;
}
}
export { AsyncLocalStorage as A, DEFAULT_DAPP_META as D, AbstractClientWallet as a, createAsyncLocalStorage as c, isWalletAnalyticsEnabled as i, setWalletAnalyticsEnabled as s };