@fastlane-labs/shbundler-sdk
Version:
SDK for interacting with Fastlane's 4337 ShBundler
188 lines (185 loc) • 5.38 kB
JavaScript
// src/client.ts
import {
createPublicClient,
createWalletClient,
http
} from "viem";
import {
createBundlerClient,
createPaymasterClient,
entryPoint07Address,
entryPoint08Address,
sendUserOperation as sendUserOperationViem
} from "viem/account-abstraction";
import {
toSimpleSmartAccount,
toSafeSmartAccount
} from "permissionless/accounts";
import { toAccount } from "viem/accounts";
import { createSmartAccountClient } from "permissionless";
// src/utils/gas.ts
import { hexToBigInt } from "viem";
async function getUserOperationGasPrice(bundlerClient) {
const resultEncoded = await bundlerClient.request({
method: "gas_getUserOperationGasPrice",
params: []
});
return {
maxFeePerGas: hexToBigInt(resultEncoded.standard.maxFeePerGas),
maxPriorityFeePerGas: hexToBigInt(resultEncoded.standard.maxPriorityFeePerGas)
};
}
// src/utils/networks.ts
async function fetchNetworkDefaults(chainId) {
const url = "https://raw.githubusercontent.com/FastLane-Labs/shbundler-sdk/main/configs/networks.json";
const res = await fetch(url);
const data = await res.json();
return data[chainId] || null;
}
// src/client.ts
async function buildShBundlerSDK({
smartAccount,
rpcUrl,
chain,
bundlerUrl,
paymasterUrl,
paymasterAddress
}) {
const publicClient = createPublicClient({
transport: http(rpcUrl),
chain
});
const walletClient = createWalletClient({
transport: http(rpcUrl),
account: smartAccount
});
const paymasterClient = createPaymasterClient({
transport: http(paymasterUrl)
});
let bundlerClient;
bundlerClient = createBundlerClient({
transport: http(bundlerUrl),
name: "shBundler",
account: smartAccount,
client: publicClient,
paymaster: paymasterClient,
userOperation: {
estimateFeesPerGas: async () => getUserOperationGasPrice(bundlerClient)
}
});
const sendUserOperation = async ({
to,
data,
chain: chain2,
paymasterContext
}) => {
const dynamicSmartAccountClient = createSmartAccountClient({
client: publicClient,
chain: chain2,
bundlerTransport: http(bundlerUrl),
account: smartAccount,
userOperation: {
estimateFeesPerGas: async () => getUserOperationGasPrice(bundlerClient)
},
paymaster: paymasterClient,
paymasterContext
});
return sendUserOperationViem(dynamicSmartAccountClient, {
account: smartAccount,
calls: [{ to, data }]
});
};
return {
publicClient,
walletClient,
smartAccount,
paymasterClient,
bundlerClient,
sendUserOperation
};
}
async function createShBundlerClient(opts) {
const {
signer,
rpcUrl,
chain,
bundlerUrl: inputBundlerUrl,
paymasterUrl: inputPaymasterUrl,
paymasterAddress: inputPaymasterAddress,
entryPointVersion = "0.8"
} = opts;
if (!signer || !rpcUrl || !chain) {
throw new Error("signer, rpcUrl, and chain are required");
}
const publicClient = createPublicClient({
transport: http(rpcUrl),
chain
});
const chainId = await publicClient.getChainId();
const defaults = await fetchNetworkDefaults(chainId) || {};
const bundlerUrl = inputBundlerUrl || defaults.bundlerUrl;
const paymasterUrl = inputPaymasterUrl || defaults.paymasterUrl;
const paymasterAddress = inputPaymasterAddress || defaults.paymasterAddress;
if (!bundlerUrl || !paymasterUrl || !paymasterAddress) {
throw new Error("Missing bundlerUrl, paymasterUrl, or paymasterAddress and no defaults found");
}
const entryPointAddress = entryPointVersion === "0.7" ? entryPoint07Address : entryPoint08Address;
const smartAccountSimple = await toSimpleSmartAccount({
client: publicClient,
entryPoint: {
address: entryPointAddress,
version: entryPointVersion
},
owner: toAccount(signer)
});
const smartAccountV08MonadTestnet = await toSafeSmartAccount({
client: publicClient,
entryPoint: {
address: entryPoint08Address,
version: "0.7"
},
owners: [signer],
version: "1.4.1",
safe4337ModuleAddress: "0x02b336F533F2de3F221540eF56583e9cb8E65203",
safeProxyFactoryAddress: "0xd9d2Ba03a7754250FDD71333F444636471CACBC4",
safeSingletonAddress: "0x639245e8476E03e789a244f279b5843b9633b2E7",
safeModuleSetupAddress: "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47",
multiSendAddress: "0x7B21BBDBdE8D01Df591fdc2dc0bE9956Dde1e16C",
multiSendCallOnlyAddress: "0x32228dDEA8b9A2bd7f2d71A958fF241D79ca5eEC"
});
const smartAccount = chainId !== 10143 ? smartAccountSimple : entryPointVersion === "0.7" ? smartAccountSimple : smartAccountV08MonadTestnet;
return buildShBundlerSDK({
smartAccount,
rpcUrl,
chain,
bundlerUrl,
paymasterUrl,
paymasterAddress
});
}
async function createShBundlerClientFromSmartAccount(opts) {
const {
smartAccount,
rpcUrl,
chain,
bundlerUrl,
paymasterUrl,
paymasterAddress
} = opts;
if (!rpcUrl || !chain || !bundlerUrl || !paymasterUrl || !paymasterAddress) {
throw new Error("All fields are required to use a precomputed smartAccount");
}
return buildShBundlerSDK({
smartAccount,
rpcUrl,
chain,
bundlerUrl,
paymasterUrl,
paymasterAddress
});
}
export {
createShBundlerClient,
createShBundlerClientFromSmartAccount
};
//# sourceMappingURL=index.mjs.map