@abstract-foundation/agw-client
Version:
Abstract Global Wallet Client SDK
233 lines • 10.1 kB
JavaScript
import { assertCurrentChain, createPublicClient, createWalletClient, custom, fromHex, hexToBigInt, hexToNumber, isHex, toHex, } from 'viem';
import { parseAccount, toAccount } from 'viem/accounts';
import { createAbstractClient } from './abstractClient.js';
import { agwCapabilitiesV2, getReceiptStatus, } from './eip5792.js';
import { validChains } from './exports/index.js';
import { getSmartAccountAddressFromInitialSigner } from './utils.js';
async function getAgwAddressFromInitialSigner(chain, transport, signer) {
const publicClient = createPublicClient({
chain,
transport,
});
return await getSmartAccountAddressFromInitialSigner(signer, publicClient);
}
async function getAgwSigner(provider, method = 'eth_accounts') {
const accounts = await provider.request({ method });
return accounts?.[0];
}
async function getAgwClient(account, chain, transport, isPrivyCrossApp, overrideTransport, customPaymasterHandler) {
const wallet = createWalletClient({
account,
transport,
});
const signer = toAccount({
address: account,
signMessage: wallet.signMessage,
signTransaction: wallet.signTransaction,
signTypedData: wallet.signTypedData,
});
const abstractClient = await createAbstractClient({
chain,
signer,
transport,
isPrivyCrossApp,
publicTransport: overrideTransport,
customPaymasterHandler,
});
return abstractClient;
}
export function transformEIP1193Provider(options) {
const { provider, chain, transport: overrideTransport, isPrivyCrossApp = false, customPaymasterHandler, } = options;
const transport = custom(provider);
const handler = async (e) => {
const { method, params } = e;
switch (method) {
case 'eth_requestAccounts': {
const signer = await getAgwSigner(provider, method);
if (!signer) {
return [];
}
const smartAccount = await getAgwAddressFromInitialSigner(chain, transport, signer);
return [smartAccount, signer];
}
case 'eth_accounts': {
const signer = await getAgwSigner(provider);
if (!signer) {
return [];
}
const smartAccount = await getAgwAddressFromInitialSigner(chain, transport, signer);
return [smartAccount, signer];
}
case 'eth_signTypedData_v4': {
const account = await getAgwSigner(provider);
if (!account) {
throw new Error('Account not found');
}
if (params[0] === account) {
return provider.request(e);
}
const abstractClient = await getAgwClient(account, chain, transport, isPrivyCrossApp, overrideTransport, customPaymasterHandler);
return abstractClient.signTypedData(JSON.parse(params[1]));
}
case 'personal_sign': {
const account = await getAgwSigner(provider);
if (!account) {
throw new Error('Account not found');
}
if (params[1] === account) {
return provider.request(e);
}
const abstractClient = await getAgwClient(account, chain, transport, isPrivyCrossApp, overrideTransport, customPaymasterHandler);
return await abstractClient.signMessage({
message: {
raw: params[0],
},
});
}
case 'eth_signTransaction':
case 'eth_sendTransaction': {
const account = await getAgwSigner(provider);
if (!account) {
throw new Error('Account not found');
}
const transaction = params[0];
if (transaction.from === account) {
return await provider.request(e);
}
const abstractClient = await getAgwClient(account, chain, transport, isPrivyCrossApp, overrideTransport, customPaymasterHandler);
// Undo the automatic formatting applied by Wagmi's eth_signTransaction
// Formatter: https://github.com/wevm/viem/blob/main/src/zksync/formatters.ts#L114
if (transaction.eip712Meta && transaction.eip712Meta.paymasterParams) {
transaction.paymaster =
transaction.eip712Meta.paymasterParams.paymaster;
transaction.paymasterInput = toHex(transaction.eip712Meta.paymasterParams.paymasterInput);
}
if (method === 'eth_signTransaction') {
return (await abstractClient.signTransaction(transaction));
}
else if (method === 'eth_sendTransaction') {
return await abstractClient.sendTransaction(transaction);
}
throw new Error('Should not have reached this point');
}
case 'wallet_sendCalls': {
const account = await getAgwSigner(provider);
if (!account) {
throw new Error('Account not found');
}
const sendCallsParams = params[0];
if (sendCallsParams.from === account) {
return await provider.request(e);
}
if (sendCallsParams.version === '1.0' ||
sendCallsParams.version === undefined) {
sendCallsParams.calls.forEach((call) => {
if (call.chainId) {
assertCurrentChain({
chain,
currentChainId: fromHex(call.chainId, 'number'),
});
}
});
}
if (sendCallsParams.version === '2.0.0') {
if (fromHex(sendCallsParams.chainId, 'number') !== chain.id) {
return {
code: 5710,
message: 'Chain not supported',
};
}
}
const abstractClient = await getAgwClient(account, chain, transport, isPrivyCrossApp, overrideTransport, customPaymasterHandler);
if (sendCallsParams.from !== parseAccount(abstractClient.account).address) {
return {
code: 4001,
message: 'Unauthorized',
};
}
const txHash = await abstractClient.sendTransactionBatch({
calls: sendCallsParams.calls.map((call) => ({
to: call.to,
value: call.value ? hexToBigInt(call.value) : undefined,
data: call.data,
})),
});
if (sendCallsParams.version === undefined ||
sendCallsParams.version === '1.0') {
return txHash;
}
return {
id: txHash,
};
}
case 'wallet_getCallsStatus': {
const receipt = await provider.request({
method: 'eth_getTransactionReceipt',
params,
});
return {
version: '2.0.0',
id: params[0],
chainId: toHex(chain.id),
status: getReceiptStatus(receipt ?? undefined),
atomic: true, // AGW will always process multiple calls as an atomic batch
receipts: receipt != null ? [receipt] : undefined,
};
}
case 'wallet_addEthereumChain':
case 'wallet_switchEthereumChain': {
const request = params[0];
const chainIdHex = request.chainId;
if (!chainIdHex) {
throw new Error('Chain ID is required');
}
// chainId is hex most likely, convert to number
const chainId = isHex(chainIdHex)
? hexToNumber(chainIdHex)
: chainIdHex;
const chain = Object.values(validChains).find((c) => c.id === chainId);
if (!chain) {
throw new Error(`Chain ${chainId} not supported`);
}
return await provider.request(e);
}
case 'wallet_showCallsStatus': {
// not implemented
return undefined;
}
case 'wallet_getCapabilities': {
const account = await getAgwSigner(provider);
if (!account) {
throw new Error('Account not found');
}
if (params[0] === account) {
return await provider.request(e);
}
const chainIds = params[1];
const capabilities = agwCapabilitiesV2;
if (chainIds) {
const filteredCapabilities = {};
for (const chainId of chainIds) {
if (capabilities[chainId]) {
filteredCapabilities[chainId] = capabilities[chainId];
}
}
return filteredCapabilities;
}
else {
return capabilities;
}
}
default: {
return await provider.request(e);
}
}
};
return {
...provider,
on: provider.on,
removeListener: provider.removeListener,
request: handler,
};
}
//# sourceMappingURL=transformEIP1193Provider.js.map