@web3modal/base
Version:
#### 🔗 [Website](https://web3modal.com)
1,079 lines • 57.9 kB
JavaScript
import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils';
import { ConstantsUtil as CommonConstantsUtil } from '@web3modal/common';
import EthereumProvider, { OPTIONAL_METHODS } from '@walletconnect/ethereum-provider';
import { getChainsFromAccounts } from '@walletconnect/utils';
import { ConstantsUtil as CommonConstants } from '@web3modal/common';
import { formatEther, JsonRpcProvider, InfuraProvider, getAddress as getOriginalAddress, parseUnits, formatUnits, JsonRpcSigner, BrowserProvider, Contract, hexlify, toUtf8Bytes, isHexString } from 'ethers';
import { EthersConstantsUtil, EthersHelpersUtil, EthersStoreUtil } from '@web3modal/scaffold-utils/ethers';
import { W3mFrameProvider, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet';
import { NetworkUtil } from '@web3modal/common';
import { SafeLocalStorage } from '../../../utils/SafeLocalStorage.js';
export class EVMEthersClient {
constructor(options) {
this.appKit = undefined;
this.hasSyncedConnectedAccount = false;
this.EIP6963Providers = [];
this.projectId = '';
this.options = undefined;
this.chain = CommonConstantsUtil.CHAIN.EVM;
this.siweControllerClient = this.options?.siweConfig;
this.tokens = HelpersUtil.getCaipTokens(this.options?.tokens);
this.defaultChain = undefined;
const { ethersConfig, siweConfig, chains, defaultChain } = options;
if (!ethersConfig) {
throw new Error('web3modal:constructor - ethersConfig is undefined');
}
this.ethersConfig = ethersConfig;
this.siweControllerClient = this.options?.siweConfig;
this.tokens = HelpersUtil.getCaipTokens(options.tokens);
this.defaultChain = EthersHelpersUtil.getCaipDefaultChain(defaultChain);
this.chains = chains;
this.networkControllerClient = {
switchCaipNetwork: async (caipNetwork) => {
const chainId = NetworkUtil.caipNetworkIdToNumber(caipNetwork?.id);
if (chainId) {
try {
EthersStoreUtil.setError(undefined);
await this.switchNetwork(chainId);
}
catch (error) {
EthersStoreUtil.setError(error);
throw new Error('networkControllerClient:switchCaipNetwork - unable to switch chain');
}
}
},
getApprovedCaipNetworksData: async () => new Promise(async (resolve) => {
const walletChoice = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
if (walletChoice?.includes(ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID)) {
const provider = await this.getWalletConnectProvider();
if (!provider) {
throw new Error('networkControllerClient:getApprovedCaipNetworks - connector is undefined');
}
const ns = provider.signer?.session?.namespaces;
const nsMethods = ns?.[ConstantsUtil.EIP155]?.methods;
const nsChains = getChainsFromAccounts(ns?.[ConstantsUtil.EIP155]?.accounts || []);
const result = {
supportsAllNetworks: nsMethods?.includes(ConstantsUtil.ADD_CHAIN_METHOD) ?? false,
approvedCaipNetworkIds: nsChains
};
resolve(result);
}
else {
const result = {
approvedCaipNetworkIds: undefined,
supportsAllNetworks: true
};
resolve(result);
}
})
};
this.connectionControllerClient = {
connectWalletConnect: async (onUri) => {
const WalletConnectProvider = await this.getWalletConnectProvider();
if (!WalletConnectProvider) {
throw new Error('connectionControllerClient:getWalletConnectUri - provider is undefined');
}
WalletConnectProvider.on('display_uri', (uri) => {
onUri(uri);
});
const clientId = await WalletConnectProvider.signer?.client?.core?.crypto?.getClientId();
if (clientId) {
this.appKit?.setClientId(clientId);
}
const params = await siweConfig?.getMessageParams?.();
if (siweConfig?.options?.enabled && params && Object.keys(params || {}).length > 0) {
const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe');
const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id);
let reorderedChains = params.chains;
if (chainId) {
reorderedChains = [chainId, ...params.chains.filter(c => c !== chainId)];
}
const result = await WalletConnectProvider.authenticate({
nonce: await siweConfig.getNonce(),
methods: [...OPTIONAL_METHODS],
...params,
chains: reorderedChains
});
const signedCacao = result?.auths?.[0];
if (signedCacao) {
const { p, s } = signedCacao;
const cacaoChainId = getDidChainId(p.iss);
const address = getDidAddress(p.iss);
if (address && cacaoChainId) {
SIWEController.setSession({
address,
chainId: parseInt(cacaoChainId, 10)
});
}
try {
const message = WalletConnectProvider.signer.client.formatAuthMessage({
request: p,
iss: p.iss
});
await SIWEController.verifyMessage({
message,
signature: s.s,
cacao: signedCacao
});
}
catch (error) {
console.error('Error verifying message', error);
await WalletConnectProvider.disconnect().catch(console.error);
await SIWEController.signOut().catch(console.error);
throw error;
}
}
}
else {
await WalletConnectProvider.connect({ optionalChains: this.chains.map(c => c.chainId) });
}
await this.setWalletConnectProvider();
},
connectExternal: async ({ id, info, provider }) => {
this.appKit?.setClientId(null);
if (id === ConstantsUtil.INJECTED_CONNECTOR_ID) {
const InjectedProvider = ethersConfig.injected;
if (!InjectedProvider) {
throw new Error('connectionControllerClient:connectInjected - provider is undefined');
}
try {
EthersStoreUtil.setError(undefined);
await InjectedProvider.request({ method: 'eth_requestAccounts' });
this.setInjectedProvider(ethersConfig);
}
catch (error) {
EthersStoreUtil.setError(error);
}
}
else if (id === ConstantsUtil.EIP6963_CONNECTOR_ID && info && provider) {
try {
EthersStoreUtil.setError(undefined);
await provider.request({ method: 'eth_requestAccounts' });
this.setEIP6963Provider(provider, info.name);
}
catch (error) {
EthersStoreUtil.setError(error);
}
}
else if (id === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) {
const CoinbaseProvider = ethersConfig.coinbase;
if (!CoinbaseProvider) {
throw new Error('connectionControllerClient:connectCoinbase - connector is undefined');
}
try {
EthersStoreUtil.setError(undefined);
await CoinbaseProvider.request({ method: 'eth_requestAccounts' });
this.setCoinbaseProvider(ethersConfig);
}
catch (error) {
EthersStoreUtil.setError(error);
throw new Error(error.message);
}
}
else if (id === ConstantsUtil.AUTH_CONNECTOR_ID) {
await this.setAuthProvider();
}
},
checkInstalled: (ids) => {
if (!ids) {
return Boolean(window.ethereum);
}
if (ethersConfig.injected) {
if (!window?.ethereum) {
return false;
}
}
return ids.some(id => Boolean(window.ethereum?.[String(id)]));
},
disconnect: async () => {
const provider = EthersStoreUtil.state.provider;
const providerType = EthersStoreUtil.state.providerType;
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
this.appKit?.setClientId(null);
if (siweConfig?.options?.signOutOnDisconnect) {
const { SIWEController } = await import('@web3modal/siwe');
await SIWEController.signOut();
}
if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID ||
providerType === 'coinbaseWalletSDK') {
const ethProvider = provider;
await ethProvider.disconnect();
}
else if (providerType === ConstantsUtil.AUTH_CONNECTOR_ID) {
await this.authProvider?.disconnect();
}
else if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID && provider) {
await this.revokeProviderPermissions(provider);
}
else if (providerType === ConstantsUtil.INJECTED_CONNECTOR_ID) {
const InjectedProvider = ethersConfig.injected;
if (InjectedProvider) {
await this.revokeProviderPermissions(InjectedProvider);
}
}
provider?.emit?.('disconnect');
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
},
signMessage: async (message) => {
const provider = EthersStoreUtil.state.provider;
if (!provider) {
throw new Error('connectionControllerClient:signMessage - provider is undefined');
}
const hexMessage = isHexString(message) ? message : hexlify(toUtf8Bytes(message));
const signature = await provider.request({
method: 'personal_sign',
params: [hexMessage, this.getAddress()]
});
return signature;
},
parseUnits: (value, decimals) => parseUnits(value, decimals),
formatUnits: (value, decimals) => formatUnits(value, decimals),
async estimateGas(data) {
const { chainId, provider, address } = EthersStoreUtil.state;
if (data.chainNamespace && data.chainNamespace !== 'eip155') {
throw new Error('connectionControllerClient:estimateGas - invalid chain namespace');
}
if (!provider) {
throw new Error('connectionControllerClient:sendTransaction - provider is undefined');
}
if (!address) {
throw new Error('connectionControllerClient:sendTransaction - address is undefined');
}
const txParams = {
from: data.address,
to: data.to,
data: data.data,
type: 0
};
const browserProvider = new BrowserProvider(provider, chainId);
const signer = new JsonRpcSigner(browserProvider, address);
const gas = await signer.estimateGas(txParams);
return gas;
},
sendTransaction: async (data) => {
const { chainId, provider, address } = EthersStoreUtil.state;
if (data.chainNamespace && data.chainNamespace !== 'eip155') {
throw new Error('ethersClient:sendTransaction - invalid chain namespace');
}
if (!provider) {
throw new Error('ethersClient:sendTransaction - provider is undefined');
}
if (!address) {
throw new Error('ethersClient:sendTransaction - address is undefined');
}
const txParams = {
to: data.to,
value: data.value,
gasLimit: data.gas,
gasPrice: data.gasPrice,
data: data.data,
type: 0
};
const browserProvider = new BrowserProvider(provider, chainId);
const signer = new JsonRpcSigner(browserProvider, address);
const txResponse = await signer.sendTransaction(txParams);
const txReceipt = await txResponse.wait();
return txReceipt?.hash || null;
},
writeContract: async (data) => {
const { chainId, provider, address } = EthersStoreUtil.state;
if (!provider) {
throw new Error('ethersClient:writeContract - provider is undefined');
}
if (!address) {
throw new Error('ethersClient:writeContract - address is undefined');
}
const browserProvider = new BrowserProvider(provider, chainId);
const signer = new JsonRpcSigner(browserProvider, address);
const contract = new Contract(data.tokenAddress, data.abi, signer);
if (!contract || !data.method) {
throw new Error('Contract method is undefined');
}
const method = contract[data.method];
if (method) {
const tx = await method(data.receiverAddress, data.tokenAmount);
return tx;
}
throw new Error('Contract method is undefined');
},
getEnsAddress: async (value) => {
try {
const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id);
let ensName = null;
let wcName = false;
if (value?.endsWith(CommonConstants.WC_NAME_SUFFIX)) {
wcName = (await this.appKit?.resolveWalletConnectName(value)) || false;
}
if (chainId === 1) {
const ensProvider = new InfuraProvider('mainnet');
ensName = await ensProvider.resolveName(value);
}
return ensName || wcName || false;
}
catch {
return false;
}
},
getEnsAvatar: async (value) => {
const { chainId } = EthersStoreUtil.state;
if (chainId && chainId === 1) {
const ensProvider = new InfuraProvider('mainnet');
const avatar = await ensProvider.getAvatar(value);
if (avatar) {
return avatar;
}
return false;
}
return false;
}
};
}
construct(appKit, options) {
if (!options.projectId) {
throw new Error('web3modal:initialize - projectId is undefined');
}
this.appKit = appKit;
this.options = options;
this.projectId = options.projectId;
this.metadata = this.ethersConfig.metadata;
if (this.defaultChain) {
this.appKit?.setCaipNetwork(this.defaultChain);
}
this.createProvider();
EthersStoreUtil.subscribeKey('address', () => {
this.syncAccount();
});
EthersStoreUtil.subscribeKey('chainId', () => {
this.syncNetwork();
});
this.appKit?.subscribeCaipNetworkChange(network => {
if (!this.getChainId() && network) {
EthersStoreUtil.setChainId(NetworkUtil.caipNetworkIdToNumber(network.id));
}
});
this.appKit?.subscribeShouldUpdateToAddress((address) => {
if (!address) {
return;
}
EthersStoreUtil.setAddress(getOriginalAddress(address));
});
this.syncRequestedNetworks(this.chains, this.options?.chainImages);
this.syncConnectors(this.ethersConfig);
if (typeof window !== 'undefined') {
this.listenConnectors(true);
this.checkActive6963Provider();
}
this.appKit?.setEIP6963Enabled(this.ethersConfig.EIP6963);
if (this.ethersConfig.injected) {
this.checkActiveInjectedProvider(this.ethersConfig);
}
if (this.ethersConfig.auth?.email || this.ethersConfig.auth?.socials?.length) {
this.syncAuthConnector(this.options.projectId, this.ethersConfig.auth);
}
if (this.ethersConfig.coinbase) {
this.checkActiveCoinbaseProvider(this.ethersConfig);
}
}
getState() {
const state = this.appKit?.getState();
return {
...state,
selectedNetworkId: NetworkUtil.caipNetworkIdToNumber(state?.selectedNetworkId)
};
}
subscribeState(callback) {
return this.appKit?.subscribeState(state => callback({
...state,
selectedNetworkId: NetworkUtil.caipNetworkIdToNumber(state.selectedNetworkId)
}));
}
setAddress(address) {
const originalAddress = address ? getOriginalAddress(address) : undefined;
EthersStoreUtil.setAddress(originalAddress);
}
getAddress() {
const { address } = EthersStoreUtil.state;
return address ? getOriginalAddress(address) : undefined;
}
getError() {
return EthersStoreUtil.state.error;
}
getChainId() {
const storeChainId = EthersStoreUtil.state.chainId;
const networkControllerChainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id);
return storeChainId ?? networkControllerChainId;
}
getStatus() {
return EthersStoreUtil.state.status;
}
getIsConnected() {
return EthersStoreUtil.state.isConnected;
}
getWalletProvider() {
return EthersStoreUtil.state.provider;
}
getWalletProviderType() {
return EthersStoreUtil.state.providerType;
}
subscribeProvider(callback) {
return EthersStoreUtil.subscribe(callback);
}
async disconnect() {
const { provider, providerType } = EthersStoreUtil.state;
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
this.appKit?.setClientId(null);
if (providerType === ConstantsUtil.AUTH_CONNECTOR_ID) {
await this.authProvider?.disconnect();
}
else if (provider && (providerType === 'injected' || providerType === 'eip6963')) {
await this.revokeProviderPermissions(provider);
provider?.emit('disconnect');
}
else if (providerType === 'walletConnect' || providerType === 'coinbaseWalletSDK') {
const ethereumProvider = provider;
if (ethereumProvider) {
try {
EthersStoreUtil.setError(undefined);
await ethereumProvider.disconnect();
}
catch (error) {
EthersStoreUtil.setError(error);
}
}
}
}
createProvider() {
if (!this.walletConnectProviderInitPromise && typeof window !== 'undefined') {
this.walletConnectProviderInitPromise = this.initWalletConnectProvider();
}
return this.walletConnectProviderInitPromise;
}
async initWalletConnectProvider() {
const rpcMap = this.chains
? this.chains.reduce((map, chain) => {
map[chain.chainId] = chain.rpcUrl;
return map;
}, {})
: {};
const walletConnectProviderOptions = {
projectId: this.projectId,
showQrModal: false,
rpcMap,
optionalChains: [...this.chains.map(chain => chain.chainId)],
metadata: {
name: this.metadata ? this.metadata.name : '',
description: this.metadata ? this.metadata.description : '',
url: this.metadata ? this.metadata.url : '',
icons: this.metadata ? this.metadata.icons : ['']
}
};
this.walletConnectProvider = await EthereumProvider.init(walletConnectProviderOptions);
await this.checkActiveWalletConnectProvider();
}
async revokeProviderPermissions(provider) {
try {
const permissions = await provider.request({
method: 'wallet_getPermissions'
});
const ethAccountsPermission = permissions.find(permission => permission.parentCapability === 'eth_accounts');
if (ethAccountsPermission) {
await provider.request({
method: 'wallet_revokePermissions',
params: [{ eth_accounts: {} }]
});
}
}
catch (error) {
console.info('Could not revoke permissions from wallet. Disconnecting...', error);
}
}
async getWalletConnectProvider() {
if (!this.walletConnectProvider) {
try {
EthersStoreUtil.setError(undefined);
await this.createProvider();
}
catch (error) {
EthersStoreUtil.setError(error);
}
}
return this.walletConnectProvider;
}
syncRequestedNetworks(chains, chainImages) {
const requestedCaipNetworks = chains?.map(chain => ({
id: `${ConstantsUtil.EIP155}:${chain.chainId}`,
name: chain.name,
imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId],
imageUrl: chainImages?.[chain.chainId],
chain: CommonConstantsUtil.CHAIN.EVM
}));
this.appKit?.setRequestedCaipNetworks(requestedCaipNetworks ?? [], this.chain);
}
async checkActiveWalletConnectProvider() {
const WalletConnectProvider = await this.getWalletConnectProvider();
const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
if (WalletConnectProvider) {
if (walletId === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) {
await this.setWalletConnectProvider();
}
}
const isConnected = EthersStoreUtil.state.isConnected;
EthersStoreUtil.setStatus(isConnected ? 'connected' : 'disconnected');
}
checkActiveInjectedProvider(config) {
const InjectedProvider = config.injected;
const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
if (InjectedProvider) {
if (walletId === ConstantsUtil.INJECTED_CONNECTOR_ID) {
this.setInjectedProvider(config);
this.watchInjected(config);
}
}
}
checkActiveCoinbaseProvider(config) {
const CoinbaseProvider = config.coinbase;
const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
if (CoinbaseProvider) {
if (walletId === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) {
if (CoinbaseProvider.accounts && CoinbaseProvider.accounts?.length > 0) {
this.setCoinbaseProvider(config);
this.watchCoinbase(config);
}
else {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
}
}
}
}
checkActive6963Provider() {
const currentActiveWallet = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
if (currentActiveWallet) {
const currentProvider = this.EIP6963Providers.find(provider => provider.info.name === currentActiveWallet);
if (currentProvider) {
this.setEIP6963Provider(currentProvider.provider, currentProvider.info.name);
}
}
}
async setWalletConnectProvider() {
SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID);
const WalletConnectProvider = await this.getWalletConnectProvider();
if (WalletConnectProvider) {
EthersStoreUtil.setChainId(WalletConnectProvider.chainId);
EthersStoreUtil.setProviderType('walletConnect');
EthersStoreUtil.setProvider(WalletConnectProvider);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
this.appKit?.setAllAccounts(WalletConnectProvider.accounts.map(address => ({ address, type: 'eoa' })), this.chain);
const session = WalletConnectProvider.signer?.session;
for (const address of WalletConnectProvider.accounts) {
const label = session?.sessionProperties?.[address];
if (label) {
this.appKit?.addAddressLabel(address, label, this.chain);
}
}
this.setAddress(WalletConnectProvider.accounts?.[0]);
this.watchWalletConnect();
}
}
async setInjectedProvider(config) {
SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.INJECTED_CONNECTOR_ID);
const InjectedProvider = config.injected;
if (InjectedProvider) {
const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(InjectedProvider);
if (addresses?.[0] && chainId) {
EthersStoreUtil.setChainId(chainId);
EthersStoreUtil.setProviderType('injected');
EthersStoreUtil.setProvider(config.injected);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain);
this.setAddress(addresses[0]);
this.watchCoinbase(config);
}
}
}
async setEIP6963Provider(provider, name) {
SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, name);
if (provider) {
const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(provider);
if (addresses?.[0] && chainId) {
EthersStoreUtil.setChainId(chainId);
EthersStoreUtil.setProviderType('eip6963');
EthersStoreUtil.setProvider(provider);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain);
this.setAddress(addresses[0]);
this.watchEIP6963(provider);
}
}
}
async setCoinbaseProvider(config) {
SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.COINBASE_SDK_CONNECTOR_ID);
const CoinbaseProvider = config.coinbase;
if (CoinbaseProvider) {
const { addresses, chainId } = await EthersHelpersUtil.getUserInfo(CoinbaseProvider);
if (addresses?.[0] && chainId) {
EthersStoreUtil.setChainId(chainId);
EthersStoreUtil.setProviderType('coinbaseWalletSDK');
EthersStoreUtil.setProvider(config.coinbase);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
this.appKit?.setAllAccounts(addresses.map(address => ({ address, type: 'eoa' })), this.chain);
this.setAddress(addresses[0]);
this.watchCoinbase(config);
}
}
}
async setAuthProvider() {
SafeLocalStorage.setItem(EthersConstantsUtil.WALLET_ID, ConstantsUtil.AUTH_CONNECTOR_ID);
if (this.authProvider) {
this.appKit?.setLoading(true);
const { address, chainId, smartAccountDeployed, preferredAccountType, accounts = [] } = await this.authProvider.connect({ chainId: this.getChainId() });
const { smartAccountEnabledNetworks } = await this.authProvider.getSmartAccountEnabledNetworks();
this.appKit?.setSmartAccountEnabledNetworks(smartAccountEnabledNetworks, this.chain);
if (address && chainId) {
this.appKit?.setAllAccounts(accounts.length > 0
? accounts
: [{ address, type: preferredAccountType }], this.chain);
EthersStoreUtil.setChainId(NetworkUtil.parseEvmChainId(chainId));
EthersStoreUtil.setProviderType(ConstantsUtil.AUTH_CONNECTOR_ID);
EthersStoreUtil.setProvider(this.authProvider);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
EthersStoreUtil.setAddress(address);
EthersStoreUtil.setPreferredAccountType(preferredAccountType);
this.appKit?.setSmartAccountDeployed(Boolean(smartAccountDeployed), this.chain);
this.watchAuth();
this.watchModal();
}
this.appKit?.setLoading(false);
}
}
async watchWalletConnect() {
const provider = await this.getWalletConnectProvider();
function disconnectHandler() {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
provider?.removeListener('disconnect', disconnectHandler);
provider?.removeListener('accountsChanged', accountsChangedHandler);
provider?.removeListener('chainChanged', chainChangedHandler);
}
function chainChangedHandler(chainId) {
if (chainId) {
const chain = EthersHelpersUtil.hexStringToNumber(chainId);
EthersStoreUtil.setChainId(chain);
}
}
const accountsChangedHandler = async (accounts) => {
if (accounts.length > 0) {
await this.setWalletConnectProvider();
}
};
if (provider) {
provider.on('disconnect', disconnectHandler);
provider.on('accountsChanged', accountsChangedHandler);
provider.on('chainChanged', chainChangedHandler);
}
}
watchInjected(config) {
const provider = config.injected;
function disconnectHandler() {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
provider?.removeListener('disconnect', disconnectHandler);
provider?.removeListener('accountsChanged', accountsChangedHandler);
provider?.removeListener('chainChanged', chainChangedHandler);
}
function accountsChangedHandler(accounts) {
const currentAccount = accounts?.[0];
if (currentAccount) {
EthersStoreUtil.setAddress(getOriginalAddress(currentAccount));
}
else {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
}
}
function chainChangedHandler(chainId) {
if (chainId) {
const chain = typeof chainId === 'string'
? EthersHelpersUtil.hexStringToNumber(chainId)
: Number(chainId);
EthersStoreUtil.setChainId(chain);
}
}
if (provider) {
provider.on('disconnect', disconnectHandler);
provider.on('accountsChanged', accountsChangedHandler);
provider.on('chainChanged', chainChangedHandler);
}
}
watchEIP6963(provider) {
function disconnectHandler() {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
provider.removeListener('disconnect', disconnectHandler);
provider.removeListener('accountsChanged', accountsChangedHandler);
provider.removeListener('chainChanged', chainChangedHandler);
}
const accountsChangedHandler = (accounts) => {
const currentAccount = accounts?.[0];
if (currentAccount) {
EthersStoreUtil.setAddress(getOriginalAddress(currentAccount));
this.appKit?.setAllAccounts(accounts.map(address => ({ address, type: 'eoa' })), this.chain);
}
else {
this.appKit?.setAllAccounts([], this.chain);
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
}
};
function chainChangedHandler(chainId) {
if (chainId) {
const chain = typeof chainId === 'string'
? EthersHelpersUtil.hexStringToNumber(chainId)
: Number(chainId);
EthersStoreUtil.setChainId(chain);
}
}
if (provider) {
provider.on('disconnect', disconnectHandler);
provider.on('accountsChanged', accountsChangedHandler);
provider.on('chainChanged', chainChangedHandler);
}
}
watchCoinbase(config) {
const provider = config.coinbase;
const walletId = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
function disconnectHandler() {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
provider?.removeListener('disconnect', disconnectHandler);
provider?.removeListener('accountsChanged', accountsChangedHandler);
provider?.removeListener('chainChanged', chainChangedHandler);
}
function accountsChangedHandler(accounts) {
const currentAccount = accounts?.[0];
if (currentAccount) {
EthersStoreUtil.setAddress(getOriginalAddress(currentAccount));
}
else {
SafeLocalStorage.removeItem(EthersConstantsUtil.WALLET_ID);
EthersStoreUtil.reset();
}
}
function chainChangedHandler(chainId) {
if (chainId && walletId === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) {
const chain = Number(chainId);
EthersStoreUtil.setChainId(chain);
}
}
if (provider) {
provider.on('disconnect', disconnectHandler);
provider.on('accountsChanged', accountsChangedHandler);
provider.on('chainChanged', chainChangedHandler);
}
}
watchAuth() {
if (this.authProvider) {
this.authProvider.onRpcRequest(request => {
if (W3mFrameHelpers.checkIfRequestExists(request)) {
if (!W3mFrameHelpers.checkIfRequestIsSafe(request)) {
this.appKit?.handleUnsafeRPCRequest();
}
}
else {
this.appKit?.open();
console.error(W3mFrameRpcConstants.RPC_METHOD_NOT_ALLOWED_MESSAGE, {
method: request.method
});
setTimeout(() => {
this.appKit?.showErrorMessage(W3mFrameRpcConstants.RPC_METHOD_NOT_ALLOWED_UI_MESSAGE);
}, 300);
}
});
this.authProvider.onRpcError(() => {
const isModalOpen = this.appKit?.isOpen();
if (isModalOpen) {
if (this.appKit?.isTransactionStackEmpty()) {
this.appKit?.close();
}
else {
this.appKit?.popTransactionStack(true);
}
}
});
this.authProvider.onRpcSuccess((_, request) => {
const isSafeRequest = W3mFrameHelpers.checkIfRequestIsSafe(request);
if (isSafeRequest) {
return;
}
if (this.appKit?.isTransactionStackEmpty()) {
this.appKit?.close();
}
else {
this.appKit?.popTransactionStack();
}
});
this.authProvider.onNotConnected(() => {
this.appKit?.setIsConnected(false, this.chain);
this.appKit?.setLoading(false);
});
this.authProvider.onIsConnected(({ preferredAccountType }) => {
this.appKit?.setIsConnected(true, this.chain);
this.appKit?.setLoading(false);
EthersStoreUtil.setPreferredAccountType(preferredAccountType);
});
this.authProvider.onSetPreferredAccount(({ address, type }) => {
if (!address) {
return;
}
this.appKit?.setLoading(true);
const chainId = NetworkUtil.caipNetworkIdToNumber(this.appKit?.getCaipNetwork()?.id);
EthersStoreUtil.setAddress(address);
EthersStoreUtil.setChainId(chainId);
EthersStoreUtil.setStatus('connected');
EthersStoreUtil.setIsConnected(true);
EthersStoreUtil.setPreferredAccountType(type);
this.syncAccount().then(() => this.appKit?.setLoading(false));
});
}
}
watchModal() {
if (this.authProvider) {
this.subscribeState(val => {
if (!val.open) {
this.authProvider?.rejectRpcRequests();
}
});
}
}
async syncAccount() {
const address = EthersStoreUtil.state.address;
const chainId = EthersStoreUtil.state.chainId;
const isConnected = EthersStoreUtil.state.isConnected;
const preferredAccountType = EthersStoreUtil.state.preferredAccountType;
this.appKit?.resetAccount(this.chain);
if (isConnected && address && chainId) {
const caipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`;
this.appKit?.setIsConnected(isConnected, this.chain);
this.appKit?.setPreferredAccountType(preferredAccountType, this.chain);
this.appKit?.setCaipAddress(caipAddress, this.chain);
this.syncConnectedWalletInfo();
const chain = this.chains.find(c => c.chainId === chainId);
if (chain?.explorerUrl) {
this.appKit?.setAddressExplorerUrl(`${chain.explorerUrl}/address/${address}`, this.chain);
}
await Promise.all([
this.syncProfile(address),
this.syncBalance(address),
this.appKit?.setApprovedCaipNetworksData(this.chain)
]);
this.hasSyncedConnectedAccount = true;
}
else if (!isConnected && this.hasSyncedConnectedAccount) {
this.appKit?.resetWcConnection();
this.appKit?.resetNetwork();
this.appKit?.setAllAccounts([], this.chain);
}
}
async syncNetwork() {
const chainImages = this.options?.chainImages;
const address = EthersStoreUtil.state.address;
const chainId = EthersStoreUtil.state.chainId;
const isConnected = EthersStoreUtil.state.isConnected;
if (this.chains) {
const chain = this.chains.find(c => c.chainId === chainId);
if (chain) {
const caipChainId = `${ConstantsUtil.EIP155}:${chain.chainId}`;
this.appKit?.setCaipNetwork({
id: caipChainId,
name: chain.name,
imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId],
imageUrl: chainImages?.[chain.chainId],
chain: this.chain
});
if (isConnected && address) {
const caipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`;
this.appKit?.setCaipAddress(caipAddress, this.chain);
if (chain.explorerUrl) {
const url = `${chain.explorerUrl}/address/${address}`;
this.appKit?.setAddressExplorerUrl(url, this.chain);
}
else {
this.appKit?.setAddressExplorerUrl(undefined, this.chain);
}
if (this.hasSyncedConnectedAccount) {
await this.syncProfile(address);
await this.syncBalance(address);
}
}
}
else if (isConnected) {
this.appKit?.setCaipNetwork({
id: `${ConstantsUtil.EIP155}:${chainId}`,
chain: this.chain
});
}
}
}
async syncWalletConnectName(address) {
try {
const registeredWcNames = await this.appKit?.getWalletConnectName(address);
if (registeredWcNames?.[0]) {
const wcName = registeredWcNames[0];
this.appKit?.setProfileName(wcName.name, this.chain);
}
else {
this.appKit?.setProfileName(null, this.chain);
}
}
catch {
this.appKit?.setProfileName(null, this.chain);
}
}
async syncProfile(address) {
const chainId = EthersStoreUtil.state.chainId;
try {
const identity = await this.appKit?.fetchIdentity({
address
});
const name = identity?.name;
const avatar = identity?.avatar;
this.appKit?.setProfileName(name, this.chain);
this.appKit?.setProfileImage(avatar, this.chain);
if (!name) {
await this.syncWalletConnectName(address);
}
}
catch {
if (chainId === 1) {
const ensProvider = new InfuraProvider('mainnet');
const name = await ensProvider.lookupAddress(address);
const avatar = await ensProvider.getAvatar(address);
if (name) {
this.appKit?.setProfileName(name, this.chain);
}
else {
await this.syncWalletConnectName(address);
}
if (avatar) {
this.appKit?.setProfileImage(avatar, this.chain);
}
}
else {
await this.syncWalletConnectName(address);
this.appKit?.setProfileImage(null, this.chain);
}
}
}
async syncBalance(address) {
const chainId = EthersStoreUtil.state.chainId;
if (chainId && this.chains) {
const chain = this.chains.find(c => c.chainId === chainId);
if (chain) {
const jsonRpcProvider = new JsonRpcProvider(chain.rpcUrl, {
chainId,
name: chain.name
});
if (jsonRpcProvider && jsonRpcProvider.ready) {
const balance = await jsonRpcProvider.getBalance(address);
const formattedBalance = formatEther(balance);
this.appKit?.setBalance(formattedBalance, chain.currency, this.chain);
}
}
}
}
syncConnectedWalletInfo() {
const currentActiveWallet = SafeLocalStorage.getItem(EthersConstantsUtil.WALLET_ID);
const providerType = EthersStoreUtil.state.providerType;
if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID) {
if (currentActiveWallet) {
const currentProvider = this.EIP6963Providers.find(provider => provider.info.name === currentActiveWallet);
if (currentProvider) {
this.appKit?.setConnectedWalletInfo({ ...currentProvider.info }, this.chain);
}
}
}
else if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) {
const provider = EthersStoreUtil.state.provider;
if (provider.session) {
this.appKit?.setConnectedWalletInfo({
...provider.session.peer.metadata,
name: provider.session.peer.metadata.name,
icon: provider.session.peer.metadata.icons?.[0]
}, this.chain);
}
}
else if (providerType === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID) {
const connector = this.appKit
?.getConnectors()
.find(c => c.id === ConstantsUtil.COINBASE_SDK_CONNECTOR_ID);
this.appKit?.setConnectedWalletInfo({
name: 'Coinbase Wallet',
icon: this.appKit?.getConnectorImage(connector)
}, this.chain);
}
else if (currentActiveWallet) {
this.appKit?.setConnectedWalletInfo({ name: currentActiveWallet }, this.chain);
}
}
async switchNetwork(chainId) {
const provider = EthersStoreUtil.state.provider;
const providerType = EthersStoreUtil.state.providerType;
if (this.chains) {
const chain = this.chains.find(c => c.chainId === chainId);
if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID && chain) {
const WalletConnectProvider = provider;
if (WalletConnectProvider) {
try {
await WalletConnectProvider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }]
});
EthersStoreUtil.setChainId(chainId);
}
catch (switchError) {
const message = switchError?.message;
if (/(?<temp1>user rejected)/u.test(message?.toLowerCase())) {
throw new Error('Chain is not supported');
}
await EthersHelpersUtil.addEthereumChain(WalletConnectProvider, chain);
}
}
}
else if (providerType === ConstantsUtil.INJECTED_CONNECTOR_ID && chain) {
const InjectedProvider = provider;
if (InjectedProvider) {
try {
await InjectedProvider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chainId) }]
});
EthersStoreUtil.setChainId(chain.chainId);
}
catch (switchError) {
if (switchError.code === EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID ||
switchError.code === EthersConstantsUtil.ERROR_CODE_DEFAULT ||
switchError?.data?.originalError?.code ===
EthersConstantsUtil.ERROR_CODE_UNRECOGNIZED_CHAIN_ID) {
await EthersHelpersUtil.addEthereumChain(InjectedProvider, chain);
}
else {
throw new Error('Chain is not supported');
}
}
}
}
else if (providerType === ConstantsUtil.EIP6963_CONNECTOR_ID && chain) {
const EIP6963Provider = provider;
if (EIP6963Provider) {
try {
await EIP6963Provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: EthersHelpersUtil.numberToHexString(chain.chai