https://github.com/ethaccount/sendop
ERC-4337 utilities for sending user operations with ethers.js
https://github.com/ethaccount/sendop
account-abstraction eip-7702 erc-7579 erc4337 ethereum ethersjs smart-account
Last synced: 3 months ago
JSON representation
ERC-4337 utilities for sending user operations with ethers.js
- Host: GitHub
- URL: https://github.com/ethaccount/sendop
- Owner: ethaccount
- License: agpl-3.0
- Created: 2024-12-19T04:44:25.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-09-06T03:56:14.000Z (10 months ago)
- Last Synced: 2025-09-06T05:44:04.421Z (10 months ago)
- Topics: account-abstraction, eip-7702, erc-7579, erc4337, ethereum, ethersjs, smart-account
- Language: TypeScript
- Homepage:
- Size: 1.76 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# sendop
A TypeScript library for ERC-4337 account abstraction, making smart contract wallets simple and accessible.
For usage examples of this library, see [Smart Account Manager](https://github.com/ethaccount/SAManager), an open web wallet built on ERC-4337.
## Features
- **Multiple Account Types**: Support for Kernel, Nexus, Safe7579, and Simple7702 accounts
- **Modular Accounts**: Full support for ERC-7579 modular account standard
- **EIP-7702 Support**: Utilities for EIP-7702 and ERC-4337 integration
- **Flexible Validation**: ECDSA, WebAuthn, and custom validator support
- **Paymaster Integration**: Public Paymaster and USDC Paymaster support
- **Multi-Bundler Support**: Works with Alchemy, Pimlico, and other ERC-4337 bundlers
- **UserOpBuilder**: Intuitive chainable API for constructing user operations
## Installation
```sh
npm install ethers sendop
```
## Quick Start (v0.5.x)
### Deploy a Kernel account and increment the counter
```ts
import { getBytes, hexlify, JsonRpcProvider, randomBytes, Wallet } from 'ethers'
import { alchemy, pimlico } from 'evm-providers'
import {
ADDRESS,
ERC4337Bundler,
ERC4337Error,
fetchGasPricePimlico,
getECDSAValidator,
getPublicPaymaster,
INTERFACES,
KernelAccountAPI,
KernelAPI,
SingleEOAValidation,
UserOpBuilder,
} from 'sendop'
const { ALCHEMY_API_KEY = '', PIMLICO_API_KEY = '', PRIVATE_KEY = '' } = process.env
// Setup providers
const CHAIN_ID = 84532 // Base Sepolia
const client = new JsonRpcProvider(alchemy(CHAIN_ID, ALCHEMY_API_KEY))
const bundler = new ERC4337Bundler(pimlico(CHAIN_ID, PIMLICO_API_KEY))
// Setup signer and validator
const signer = new Wallet(PRIVATE_KEY)
const ecdsaValidator = getECDSAValidator({ ownerAddress: signer.address })
const { accountAddress, factory, factoryData } = await KernelAPI.getDeployment({
client,
validatorAddress: ecdsaValidator.address,
validatorData: ecdsaValidator.initData,
salt: hexlify(randomBytes(32)),
})
// Create account API
const kernelAPI = new KernelAccountAPI({
validation: new SingleEOAValidation(),
validatorAddress: ecdsaValidator.address,
})
// Define operations to execute
const executions = [
{
to: ADDRESS.Counter,
value: 0n,
data: INTERFACES.Counter.encodeFunctionData('increment'),
},
]
// Build operation
const op = new UserOpBuilder({
chainId: CHAIN_ID,
bundler,
entryPointAddress: kernelAPI.entryPointAddress,
})
.setSender(accountAddress)
.setFactory({
factory,
factoryData,
})
.setCallData(await kernelAPI.getCallData(executions))
.setNonce(await kernelAPI.getNonce(client, accountAddress))
.setSignature(await kernelAPI.getDummySignature())
// Set paymaster and gas price
op.setPaymaster(getPublicPaymaster())
op.setGasPrice(await fetchGasPricePimlico(pimlico(CHAIN_ID, PIMLICO_API_KEY)))
// Estimate gas
await op.estimateGas()
// Inspect userop
console.log('op.preview', op.preview())
console.log('op.toRequestParams', op.toRequestParams())
console.log('op.toSendRequest', op.toSendRequest())
console.log('op.encodeHandleOpsDataWithDefaultGas', op.encodeHandleOpsDataWithDefaultGas())
// Sign and send
const sig = await signer.signMessage(getBytes(op.hash()))
op.setSignature(await kernelAPI.formatSignature(sig))
try {
await op.send()
} catch (error) {
if (error instanceof ERC4337Error) {
console.error('error.message', error.message)
console.error('error.code', error.code)
console.error('error.method', error.method)
console.error('error.payload', error.payload)
console.error('error.userOp', error.userOp)
}
process.exit(1)
}
const receipt = await op.wait()
console.log('Operation success:', receipt.success)
```
### Run Tests
```sh
bun run test # vitest (recommended)
bun test # bun built-in test; some tests may fail
bun test -t 'test case'
```
### Dev Commands
```sh
bun install
bun typecheck
bun run build
bun run build:contract-types
```
## License
Copyright (C) 2024-2025 Johnson Chen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation.