@owlprotocol/contracts-account-abstraction
Version:
ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.
149 lines (148 loc) • 5.85 kB
JavaScript
import {
numberToHex,
RpcRequestError,
ContractFunctionExecutionError
} from "viem";
import { getAction } from "viem/utils";
import {
concatRequests,
isPublicRpcMethod,
requestPublicEIP1193,
requestWithMemoizedChainId
} from "@owlprotocol/backend-public/eip1193";
import { createRequestGetUserOperationByHash } from "./bundler/requestGetUserOperationByHash.js";
import { createRequestGetUserOperationReceipt } from "./bundler/requestGetUserOperationReceipt.js";
import {
estimateUserOperationGas,
getSupportedEntryPoints,
getUserOperationGasPrice,
sendUserOperation
} from "../actions/index.js";
import { decodeUserOp } from "../models/UserOperation.js";
import { ExecutionError } from "../models/Errors.js";
const bundlerRpcMethods = [
"eth_chainId",
"eth_supportedEntryPoints",
"eth_getUserOperationByHash",
"eth_getUserOperationReceipt",
"eth_sendUserOperation",
"eth_estimateUserOperationGas"
];
function isBundlerRpcMethod(method) {
return bundlerRpcMethods.includes(method);
}
function createBackendBundlerEIP1193(client, config) {
const request = client.request ?? config?.requestOverride;
const requestMemoizedChainId = requestWithMemoizedChainId(request);
const requestGetUserOperationByHash = config?.createRequestGetUserOperationByHash ? config?.createRequestGetUserOperationByHash(requestMemoizedChainId) : createRequestGetUserOperationByHash(requestMemoizedChainId);
const requestGetUserOperationReceipt = config?.createRequestGetUserOperationReceipt ? config.createRequestGetUserOperationReceipt(requestMemoizedChainId) : createRequestGetUserOperationReceipt(requestMemoizedChainId);
const requestPublic = async function(args) {
return requestPublicEIP1193(requestMemoizedChainId, args);
};
const requestBundler = async function(args) {
try {
if (args.method === "eth_supportedEntryPoints") {
return getAction(client, getSupportedEntryPoints, "getSupportedEntryPoints")({});
} else if (args.method === "eth_getUserOperationByHash") {
return requestGetUserOperationByHash(args);
} else if (args.method === "eth_getUserOperationReceipt") {
return requestGetUserOperationReceipt(args);
} else if (args.method === "eth_estimateUserOperationGas") {
const [userOperationHex, entryPoint] = args.params;
const supportedEntryPoints = await getAction(
client,
getSupportedEntryPoints,
"getSupportedEntryPoints"
)({});
if (entryPoint && !supportedEntryPoints.includes(entryPoint)) {
throw new Error(`Unsupported entrypoint ${entryPoint}, expected one of ${supportedEntryPoints}`);
}
let maxFeePerGas;
let maxPriorityFeePerGas;
if (userOperationHex.maxFeePerGas) {
maxFeePerGas = userOperationHex.maxFeePerGas;
maxPriorityFeePerGas = userOperationHex.maxPriorityFeePerGas ?? userOperationHex.maxFeePerGas;
} else {
const gasPrice = await getAction(client, getUserOperationGasPrice, "getUserOperationGasPrice")({});
maxFeePerGas = numberToHex(gasPrice.standard.maxFeePerGas);
maxPriorityFeePerGas = numberToHex(gasPrice.standard.maxPriorityFeePerGas);
}
const userOperation = decodeUserOp({ ...userOperationHex, maxFeePerGas, maxPriorityFeePerGas });
const {
preVerificationGas,
verificationGasLimit,
callGasLimit,
paymasterVerificationGasLimit,
paymasterPostOpGasLimit
} = await getAction(client, estimateUserOperationGas, "estimateUserOperationGas")(userOperation);
const result = {
preVerificationGas: numberToHex(preVerificationGas),
verificationGasLimit: numberToHex(verificationGasLimit),
callGasLimit: numberToHex(callGasLimit)
};
if (paymasterVerificationGasLimit != void 0) {
result.paymasterVerificationGasLimit = numberToHex(paymasterVerificationGasLimit);
}
if (paymasterPostOpGasLimit != void 0) {
result.paymasterPostOpGasLimit = numberToHex(paymasterPostOpGasLimit);
}
return result;
} else if (args.method === "eth_sendUserOperation") {
const [userOperationHex, entryPoint] = args.params;
const supportedEntryPoints = await getAction(
client,
getSupportedEntryPoints,
"getSupportedEntryPoints"
)({});
if (entryPoint && !supportedEntryPoints.includes(entryPoint)) {
throw new Error(`Unsupported entrypoint ${entryPoint}, expected one of ${supportedEntryPoints}`);
}
const userOperation = decodeUserOp(userOperationHex);
const userOperationHash = await getAction(
client,
sendUserOperation,
"sendUserOperation"
)(userOperation);
console.debug({ userOperationHash });
return userOperationHash;
} else {
throw new RpcRequestError({
body: args,
url: "",
error: {
code: -32601,
message: "Method not found"
}
});
}
} catch (error) {
if (error instanceof RpcRequestError) {
throw error;
}
if (error instanceof ExecutionError || error instanceof ContractFunctionExecutionError) {
throw new RpcRequestError({
body: { method: args.method },
url: "",
error: {
code: -32603,
message: error.message
}
});
}
console.error(error);
return null;
}
};
return concatRequests([
{ request: requestBundler, isRpcMethod: isBundlerRpcMethod },
{
request: requestPublic,
isRpcMethod: isPublicRpcMethod
}
]);
}
export {
bundlerRpcMethods,
createBackendBundlerEIP1193,
isBundlerRpcMethod
};