Skip to content

Instantly share code, notes, and snippets.

@TABASCOatw
Created May 18, 2024 05:42
Show Gist options
  • Save TABASCOatw/f74c0127e0965a6dd705b2c6a49c03e6 to your computer and use it in GitHub Desktop.
Save TABASCOatw/f74c0127e0965a6dd705b2c6a49c03e6 to your computer and use it in GitHub Desktop.
Rough draft - AA-enabled Wagmi connector, hard-coded values + debugging
import { type ConnectParam, type EIP1193Provider } from '@particle-network/auth-core';
import { ChainNotConfiguredError, createConnector, normalizeChainId } from '@wagmi/core';
import { SmartAccount, AAWrapProvider, SendTransactionMode } from '@particle-network/aa';
import { SwitchChainError, UserRejectedRequestError, getAddress, numberToHex, type ProviderRpcError } from 'viem';
import { ethers } from 'ethers';
particleWagmiWallet.type = 'particleWallet' as const;
export function particleWagmiWallet(param?: ConnectParam) {
type Provider = EIP1193Provider;
type Properties = any;
return createConnector<Provider, Properties>((config) => ({
id: 'particleWalletSDK',
name: 'Particle Wallet',
type: particleWagmiWallet.type,
async connect({ chainId }: { chainId: number }) {
try {
console.log('Connecting to Particle Wallet...');
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
console.log('Provider retrieved:', provider);
const smartAccount = new SmartAccount(provider, {
projectId: '3e63e8c0-1df6-4efb-a96e-96836accebdc',
clientKey: 'cx5kWWPJ0AmG80U6ePLJ3U3EpEknGBYeVlWdF4xv',
appId: 'c98e6688-ffea-4a66-8282-f3c7b52c012a',
aaOptions: {
accountContracts: {
SIMPLE: [{ chainIds: [1], version: '1.0.0' }],
}
}
});
console.log('Smart account created:', smartAccount);
const customProvider = new ethers.providers.Web3Provider(new AAWrapProvider(smartAccount, SendTransactionMode.Gasless), "any");
const newProvider = customProvider.getSigner();
this.provider = newProvider;
console.log('Provider replaced:', newProvider);
const smartAccountAddress = await smartAccount.getAddress();
console.log('Smart account address:', smartAccountAddress);
const accounts = [getAddress(smartAccountAddress)];
newProvider.provider.on('accountsChanged', this.onAccountsChanged);
newProvider.provider.on('chainChanged', this.onChainChanged);
newProvider.provider.on('disconnect', this.onDisconnect.bind(this));
let currentChainId = await this.getChainId();
if (chainId && currentChainId !== chainId) {
const chain = await this.switchChain!({ chainId }).catch((error: any) => {
if (error.code === UserRejectedRequestError.code) throw error;
return { id: currentChainId };
});
currentChainId = chain?.id ?? currentChainId;
}
console.log('Connection successful:', { accounts, chainId: currentChainId });
return { accounts, chainId: currentChainId };
} catch (error: any) {
console.error('Error connecting to Particle Wallet:', error);
if (error.code == 4011) throw new UserRejectedRequestError(error as Error);
throw error;
}
},
async disconnect() {
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
provider.removeListener('accountsChanged', this.onAccountsChanged);
provider.removeListener('chainChanged', this.onChainChanged);
provider.removeListener('disconnect', this.onDisconnect.bind(this));
await (provider as any)?.disconnect?.();
},
async getAccounts() {
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
const smartAccount = new SmartAccount(provider, {
projectId: '3e63e8c0-1df6-4efb-a96e-96836accebdc',
clientKey: 'cx5kWWPJ0AmG80U6ePLJ3U3EpEknGBYeVlWdF4xv',
appId: 'c98e6688-ffea-4a66-8282-f3c7b52c012a',
aaOptions: {
accountContracts: {
SIMPLE: [{ chainIds: [1], version: '1.0.0' }],
}
}
});
const smartAccountAddress = await smartAccount.getAddress();
console.log('Fetched smart account address:', smartAccountAddress);
return [getAddress(smartAccountAddress)];
},
async getChainId() {
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
const chainId = await provider.request({ method: 'eth_chainId' });
return normalizeChainId(chainId);
},
async getProvider() {
if (typeof window === 'undefined') {
return;
}
while (!(window as any).particle?.ethereum) {
await new Promise((resolve) => setTimeout(() => resolve(true), 100));
}
return (window as any).particle?.ethereum;
},
async isAuthorized() {
try {
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
return (provider as any).isConnected();
} catch {
return false;
}
},
async switchChain({ chainId }: { chainId: number }) {
const chain = config.chains.find((chain) => chain.id === chainId);
if (!chain) throw new SwitchChainError(new ChainNotConfiguredError());
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
const chainId_ = numberToHex(chain.id);
console.log(chain.id);
try {
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId_ }],
});
return chain;
} catch (error) {
if ((error as ProviderRpcError).code === 4902) {
try {
await provider.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: chainId_,
chainName: chain.name,
nativeCurrency: chain.nativeCurrency,
rpcUrls: [chain.rpcUrls.default?.http[0] ?? ''],
blockExplorerUrls: [chain.blockExplorers?.default.url],
},
],
});
return chain;
} catch (error) {
throw new UserRejectedRequestError(error as Error);
}
}
throw new SwitchChainError(error as Error);
}
},
onAccountsChanged(accounts: string[]) {
if (accounts.length === 0) config.emitter.emit('disconnect');
else
config.emitter.emit('change', {
accounts: accounts.map((x) => getAddress(x)),
});
},
onChainChanged(chain: string) {
const chainId = normalizeChainId(chain);
config.emitter.emit('change', { chainId });
},
async onDisconnect(_error: any) {
config.emitter.emit('disconnect');
const provider = await this.getProvider();
if (!provider) {
console.error('Provider is undefined');
throw new Error('Provider is undefined');
}
provider.removeListener('accountsChanged', this.onAccountsChanged);
provider.removeListener('chainChanged', this.onChainChanged);
provider.removeListener('disconnect', this.onDisconnect.bind(this));
},
}));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment