Skandha bundles ERC-4337 UserOperations, one EntryPoint version at a time
A modular typescript implementation of ERC4337 (Account Abstraction) bundler client.
At a glance
- What is it?
- Skandha is a TypeScript bundler for EIP-4337 account abstraction, and its versioning is driven by the EntryPoint contract rather than by feature releases: v1 targets EntryPoint 0.6.0, v2 targets 0.7.0 and v3 targets 0.8.0. Running it means a Bun based source build or a container image, plus a config file that names your entry points, relayer keys and the RPC endpoint.
- Who is it for?
- Skandha fits a team that needs its own bundler for a specific EntryPoint deployment, either on a chain with no public bundler or for control over relayer keys, fee collection and bundling policy. It does not fit a project that wants a hosted bundler with no operations burden, and it is the wrong choice for a chain whose EntryPoint version has no corresponding Skandha branch.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 49 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three branches, because EntryPoint versions do not talk to each other
Skandha is described as a modular, developer-friendly TypeScript bundler for Ethereum account abstraction under EIP-4337. What makes it unusual is how its versions are structured.
The README maps three major versions to three EntryPoint contract versions. Skandha v1 supports EntryPoint 0.6.0 and lives on a releases/v0.6 branch. Skandha v2 supports EntryPoint 0.7.0 and lives on a releases/v0.7 branch. Skandha v3 supports EntryPoint 0.8.0 and lives on master. Each version is a separate line of development rather than a tag on one branch, which is the practical answer to the fact that a bundler talks to a specific deployed EntryPoint contract and cannot serve two of them at once.
That structure has consequences for operators. The default branch of the repository is develop, while the newest release line sits on master, so a clone of the default branch is not the same thing as the newest supported release. Choosing a version is therefore a decision about which EntryPoint your chain runs, not a decision about which features you want.
The README also carries an explicit warning that the software is under active development, and it flags the shared mempool as a limitation: a warning note states that this version of the bundler only supports Entry Point v7, which does not have the p2p mempool yet. There is a P2P.md file at the top of the repository for that topic. In other words, the p2p mempool feature is a work in progress rather than a shipped capability, and the acknowledgement goes to eth-infinitism and lodestar for the surrounding work.
Running from source means Bun, a bcrypto patch and a config file
The README offers a one-liner first, then a step by step path for building from source. The one-liner pipes a remote script into a shell:
curl -fsSL https://skandha.run | bashThe manual route is more informative, and it tells you what the toolchain expects. Step one is installing dependencies with bun install. Step two is a patch for bcrypto, which means changing into./node_modules/bcrypto and running bun install there. Step three is the build, bun build. Then you copy config.json.default to config.json and edit it. Step six is optional: run a local geth node from test/geth-dev. Step seven runs./skandha standalone, and the bundler is then available on http://localhost:14337/rpc/.
The bcrypto step is the one that looks like trivia and is not. It appears in the source build instructions, in the Dockerfile, where the same fix is applied inside the image build, and the package.json declares node >=18.0.0 in its engines field for the workspace.
The repository is a monorepo. The root package.json holds the workspace scripts, and the build order is explicit: types first, then db, then utils, params, monitoring, executor, api, node and cli. The workspace uses Bun for installs and builds, lerna for running tests, and eslint for linting TypeScript sources. The package manager split is unusual enough to note: bun for dependency management and building, lerna for the test runner, and the packages themselves live under packages/ with names like @skandha/types and @skandha/executor.
The Docker path is three commands and a mounted config
The container route is documented separately and is the shortest way to get a running bundler. The README lists three steps: copy config.json.default to config.json, edit config.json, and build the image with docker build -t etherspot/skandha . Then you start a container with a bind mount for the config file and the port mapped:
4. `docker run --mount type=bind,source="$(pwd)"/config.json,target=/usr/app/config.json,readonly -dp 14337:14337 etherspot/skandha standalone`The mount is read only, which is a sensible default for a configuration file that carries relayer private keys, and the port mapping matches the RPC port used by the source build path.
The Dockerfile is a two stage build. The first stage uses oven/bun:1.2.23-alpine, installs a compiler toolchain with g++, make, python3 and git, copies the source, installs with a frozen lockfile, runs the full workspace build and then reinstalls production dependencies. The second stage starts again from the Bun alpine image and copies the built application out of the first stage. The entrypoint runs the compiled CLI directly with the Bun runtime rather than through node, and the bcrypto patch is applied inside the image build after the install step.
So the container and the source build are the same program assembled differently. The container pins the Bun version, which is the part that matters for reproducibility, since the source instructions leave the Bun version to whatever you have installed.
Relayer configuration is one JSON file with env var overrides
The relayer configuration is the operational core of the project. The README gives a simplest form and then a version annotated with every parameter and its default.
The minimal config names the entry points, the relayer keys and the RPC endpoint:
{
"entryPoints": [
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"
],
"relayers": [
"0x{RELAYER-PRIVATE-KEY}"
],
"rpcEndpoint": "https://polygon-mumbai.blockpi.network/v1/rpc/public"
}The annotated version shows how much more there is. entryPoints is a list of supported entry points. relayers holds private keys, and the comment notes they can come from there or from environment variables such as SKANDHA_MUMBAI_RELAYERS or SKANDHA_DEV_RELAYERS. beneficiary is the optional fee collector, with the same env var pattern, and if it is not set the relayer is used. rpcEndpoint can also come from an environment variable.
Then come the EIP-4337 parameters the README points at: minInclusionDenominator, throttlingSlack, banSlack, minStake and minUnstakeDelay, plus minSignerBalance for picking a fee collector when the relayer balance drops. There are also execution-level knobs: multicall pointing at a multicall3 address, estimationStaticBuffer for extra gas on the call gas limit, validationGasLimit for the simulate calls, receiptLookupRange for the block range of the two lookup RPCs, and useropsTTL in seconds.
The gas policy section is the part that decides whether you make money. gasPriceMarkup adds a percentage to the price reported by the skandha_getGasPrice RPC, with 10000 meaning 100 percent and 500 meaning 5 percent. enforceGasPrice and enforceGasPriceThreshold let you refuse to bundle user operations whose gas price is too far below the network price, with the threshold expressed in basis points. bundleGasLimitMarkup adds gas to a bundle transaction and pvgMarkup adds gas on top of the estimated priority fee.
The comment structure is itself informative: every parameter is annotated with its type and default, and several are marked optional.
CLI flags cover bundling mode, metrics and the two escape hatches
The command line options are a short list, and two of them deserve careful reading.
The first is --executor.bundlingMode manual|auto, which sets the bundling mode on start with auto as the default. The second is --unsafeMode, which enables a mode that bypasses opcode and stake validation. That is a development and testing convenience, not something to leave on in production: the validation you are disabling is what stops a user operation from doing something the EntryPoint contract is meant to forbid.
The third is --redirectRpc, which redirects ETH RPC calls to the underlying execution client, and the README states plainly that this is needed if you use UserOp.js. That is a dependency note rather than a preference: some client libraries call standard eth_ methods against the bundler endpoint, and a bundler is not an execution client, so the redirect exists to make those calls land somewhere that can answer them.
Metrics are configured by three flags: --metrics.enable false|true, --metrics.host defaulting to 127.0.0.1 and --metrics.port defaulting to 8008. Prometheus output is off by default, which is the right default for a service that exposes an RPC endpoint.
Beyond the flags, the config file has a relayingMode field with three values: classic, flashbots for the Flashbots Builder API and merkle for Merkle.io. The bundle policy is configured alongside it, with bundleInterval as the bundle creation interval in milliseconds, bundleSize defaulting to four user operations per bundle, and whitelistedEntities, an object with factory, paymaster and account arrays, for entities that bypass stake and opcode validation.
The whitelistedEntities option deserves the same caution as unsafeMode, because it is the same validation being waived, configured per address instead of globally.
What the README does not settle
Several operational questions are answered elsewhere or not at all, and an engineer evaluating this should know which are which.
The installation instructions live on a separate site, linked from the README as the install page, alongside pages listing the chains supported and a UserOp fee history. The README itself does not say how a chain is added to the supported list, or what the support commitment looks like.
The shared mempool remains the largest functional gap. The README warns that the current version supports only Entry Point v7, which does not have the p2p mempool yet, and the P2P.md file at the top of the repository is where that work is described. A bundler without a p2p mempool is a single-operator bundler: it sees the user operations sent to it and nothing else, so the set of operations it can bundle is smaller and it competes only for the transactions in its own memory.
Two other things are worth checking before you commit. The bundler holds relayer private keys in config.json or in environment variables, and the Docker example mounts that file read only but does not encrypt it. And the RPC port in the documentation is plain HTTP on localhost, so any exposure of the bundler to a network needs a reverse proxy and access control that the README does not describe.
On maintenance, the releases are named v2-2.4.6, v2-2.4.5 and v2-2.4.4, which shows the version scheme combining the major line with the patch number. The last push was on 2026-09-04, the same day as the v2-2.4.6 release, and the repository is not archived. The licence is MIT, which means you can run a modified bundler internally without any obligation beyond keeping the notice.
Editorial conclusion
Skandha fits a team that needs its own bundler for a specific EntryPoint deployment, either on a chain with no public bundler or for control over relayer keys, fee collection and bundling policy. It does not fit a project that wants a hosted bundler with no operations burden, and it is the wrong choice for a chain whose EntryPoint version has no corresponding Skandha branch. Before you run it, decide which branch matches your EntryPoint, since v1, v2 and v3 live in separate release branches, and keep unsafeMode off outside development, because it bypasses opcode and stake validation. The project is MIT licensed, the last release v2-2.4.6 was published on 2026-09-04, and the last push was on 2026-09-04.
Frequently asked questions
How do I run Skandha from source code?
The README lists the steps: install dependencies with bun install, patch bcrypto by running bun install inside ./node_modules/bcrypto, build with bun build, copy config.json.default to config.json and edit it, optionally run a local geth node from test/geth-dev, then run ./skandha standalone. The bundler is then served on http://localhost:14337/rpc/.
Which EntryPoint version does Skandha support?
The README maps three major versions to three EntryPoint versions: Skandha v1 supports EntryPoint 0.6.0 on the releases/v0.6 branch, Skandha v2 supports EntryPoint 0.7.0 on releases/v0.7, and Skandha v3 supports EntryPoint 0.8.0 on master.
How do I configure the relayer keys and RPC endpoint in Skandha?
In config.json you set entryPoints, relayers and rpcEndpoint, and the README notes that relayer keys, the beneficiary and the RPC endpoint can also be supplied through environment variables such as SKANDHA_MUMBAI_RELAYERS and SKANDHA_MUMBAI_RPC. The beneficiary is the fee collector and, if unset, the relayer is used.
What does unsafeMode do in Skandha?
The README lists unsafe mode as bypassing opcode and stake validation, enabled with the --unsafeMode flag. The same bypass is available per address through the whitelistedEntities configuration, which has factory, paymaster and account arrays.
Does Skandha support a p2p shared mempool?
Not yet. The README carries a warning that this version of the bundler only supports Entry Point v7, which does not have the p2p mempool yet, and the repository carries a P2P.md file describing that work.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/etherspot-skandha)Was this useful?