@wagmi/core
Version:
VanillaJS library for Ethereum
220 lines • 9.81 kB
JavaScript
import { RpcRequestError, SwitchChainError, UserRejectedRequestError, custom, fromHex, getAddress, keccak256, numberToHex, stringToHex, } from 'viem';
import { rpc } from 'viem/utils';
import { ChainNotConfiguredError, ConnectorNotConnectedError, } from '../errors/config.js';
import { createConnector } from './createConnector.js';
mock.type = 'mock';
export function mock(parameters) {
const transactionCache = new Map();
const features = parameters.features ??
{ defaultConnected: false };
let connected = features.defaultConnected;
let connectedChainId;
return createConnector((config) => ({
id: 'mock',
name: 'Mock Connector',
type: mock.type,
async setup() {
connectedChainId = config.chains[0].id;
},
async connect({ chainId } = {}) {
if (features.connectError) {
if (typeof features.connectError === 'boolean')
throw new UserRejectedRequestError(new Error('Failed to connect.'));
throw features.connectError;
}
const provider = await this.getProvider();
const accounts = await provider.request({
method: 'eth_requestAccounts',
});
let currentChainId = await this.getChainId();
if (chainId && currentChainId !== chainId) {
const chain = await this.switchChain({ chainId });
currentChainId = chain.id;
}
connected = true;
return {
accounts: accounts.map((x) => getAddress(x)),
chainId: currentChainId,
};
},
async disconnect() {
connected = false;
},
async getAccounts() {
if (!connected)
throw new ConnectorNotConnectedError();
const provider = await this.getProvider();
const accounts = await provider.request({ method: 'eth_accounts' });
return accounts.map((x) => getAddress(x));
},
async getChainId() {
const provider = await this.getProvider();
const hexChainId = await provider.request({ method: 'eth_chainId' });
return fromHex(hexChainId, 'number');
},
async isAuthorized() {
if (!features.reconnect)
return false;
if (!connected)
return false;
const accounts = await this.getAccounts();
return !!accounts.length;
},
async switchChain({ chainId }) {
const provider = await this.getProvider();
const chain = config.chains.find((x) => x.id === chainId);
if (!chain)
throw new SwitchChainError(new ChainNotConfiguredError());
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: numberToHex(chainId) }],
});
return chain;
},
onAccountsChanged(accounts) {
if (accounts.length === 0)
this.onDisconnect();
else
config.emitter.emit('change', {
accounts: accounts.map((x) => getAddress(x)),
});
},
onChainChanged(chain) {
const chainId = Number(chain);
config.emitter.emit('change', { chainId });
},
async onDisconnect(_error) {
config.emitter.emit('disconnect');
connected = false;
},
async getProvider({ chainId } = {}) {
const chain = config.chains.find((x) => x.id === chainId) ?? config.chains[0];
const url = chain.rpcUrls.default.http[0];
const request = async ({ method, params }) => {
// eth methods
if (method === 'eth_chainId')
return numberToHex(connectedChainId);
if (method === 'eth_requestAccounts')
return parameters.accounts;
if (method === 'eth_signTypedData_v4')
if (features.signTypedDataError) {
if (typeof features.signTypedDataError === 'boolean')
throw new UserRejectedRequestError(new Error('Failed to sign typed data.'));
throw features.signTypedDataError;
}
// wallet methods
if (method === 'wallet_switchEthereumChain') {
if (features.switchChainError) {
if (typeof features.switchChainError === 'boolean')
throw new UserRejectedRequestError(new Error('Failed to switch chain.'));
throw features.switchChainError;
}
connectedChainId = fromHex(params[0].chainId, 'number');
this.onChainChanged(connectedChainId.toString());
return;
}
if (method === 'wallet_watchAsset') {
if (features.watchAssetError) {
if (typeof features.watchAssetError === 'boolean')
throw new UserRejectedRequestError(new Error('Failed to switch chain.'));
throw features.watchAssetError;
}
return connected;
}
if (method === 'wallet_getCapabilities')
return {
'0x2105': {
paymasterService: {
supported: params[0] ===
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
},
sessionKeys: {
supported: true,
},
},
'0x14A34': {
paymasterService: {
supported: params[0] ===
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
},
},
};
if (method === 'wallet_sendCalls') {
const hashes = [];
const calls = params[0].calls;
for (const call of calls) {
const { result, error } = await rpc.http(url, {
body: {
method: 'eth_sendTransaction',
params: [call],
},
});
if (error)
throw new RpcRequestError({
body: { method, params },
error,
url,
});
hashes.push(result);
}
const id = keccak256(stringToHex(JSON.stringify(calls)));
transactionCache.set(id, hashes);
return id;
}
if (method === 'wallet_getCallsStatus') {
const hashes = transactionCache.get(params[0]);
if (!hashes)
return null;
const receipts = await Promise.all(hashes.map(async (hash) => {
const { result, error } = await rpc.http(url, {
body: {
method: 'eth_getTransactionReceipt',
params: [hash],
id: 0,
},
});
if (error)
throw new RpcRequestError({
body: { method, params },
error,
url,
});
if (!result)
return null;
return {
blockHash: result.blockHash,
blockNumber: result.blockNumber,
gasUsed: result.gasUsed,
logs: result.logs,
status: result.status,
transactionHash: result.transactionHash,
};
}));
if (receipts.some((x) => !x))
return { status: 'PENDING', receipts: [] };
return { status: 'CONFIRMED', receipts };
}
if (method === 'wallet_showCallsStatus')
return;
// other methods
if (method === 'personal_sign') {
if (features.signMessageError) {
if (typeof features.signMessageError === 'boolean')
throw new UserRejectedRequestError(new Error('Failed to sign message.'));
throw features.signMessageError;
}
// Change `personal_sign` to `eth_sign` and swap params
method = 'eth_sign';
params = [params[1], params[0]];
}
const body = { method, params };
const { error, result } = await rpc.http(url, { body });
if (error)
throw new RpcRequestError({ body, error, url });
return result;
};
return custom({ request })({ retryCount: 0 });
},
}));
}
//# sourceMappingURL=mock.js.map