Skip to content

I forked Pimlico Alto Bundler and set it up for 1click deploy

License

Notifications You must be signed in to change notification settings

voidfab/bundler-alto-rail

Β 
Β 

Repository files navigation


ALTO on Railway with Arbitrum

This project is a fork of the original pimlicolabs/alto repository. We would like to acknowledge and thank the original creators for their work and contribution. This fork has been modified to enable hosting on Railway with default settings configured for Arbitrum.

Table of Contents

Introduction

In ERC-4337, a Bundler is the core infrastructure component that allows account abstraction to work on any EVM network. On the highest level, its purpose is to work with a mempool of User Operations to get the transaction to be included on-chain.

This project allows you to deploy the ALTO bundler application on Railway with support for Arbitrum. ALTO is a platform designed to provide decentralized orchestration for blockchain-based tasks, specifically functioning as a Bundler for ERC-4337.

Getting Started

Follow these steps to get the project up and running:

  1. Fork the Repository: Fork this repository to your own GitHub account.
  2. Clone the Repository: Clone the forked repository to your local machine.
    git clone https://github.com/syphrpunk/alto.git
  3. Install Dependencies: Navigate to the project directory and install the required dependencies.
    cd alto
    pnpm install
  4. Deploy to Railway: Follow the Railway Documentation to deploy the project. Or 1-click deploy

Deploy on Railway

Features

  • Decentralized task orchestration
  • Default settings configured for Arbitrum
  • Easy deployment on Railway

Configuration

By default, this project is configured to work with Arbitrum. To modify the configuration, edit the necessary environment variables and settings in the railway.toml (and environment variables directly on railway).

Alto Help

You can edit the commands in railway.toml to include any of the below additional parameters. Below are the commands available in the ALTO CLI, obtained by running ./alto help:

πŸ” Alto: TypeScript ERC-4337 Bundler.
  * by Pimlico, 2024

  ./alto help

  πŸ” Alto: TypeScript ERC-4337 Bundler.
    * by Pimlico, 2024

  Options:
    -e, --entrypoints                        EntryPoint contract addresses split by commas  [string] [required]
    -c, --entrypoint-simulation-contract     Address of the EntryPoint simulations contract  [string]
    -x, --executor-private-keys              Private keys of the executor accounts split by commas  [string] [required]
    -u, --utility-private-key                Private key of the utility account  [string]
        --max-executors                      Maximum number of executor accounts to use from the list of executor private keys  [number]
        --min-executor-balance               Minimum balance required for each executor account (below which the utility account will refill)  [string]
        --executor-refill-interval           Interval to refill the signer balance (seconds)  [number] [required] [default: 1200]
        --min-entity-stake                   Minimum stake required for a relay (in 10e18)  [number] [required] [default: 1]
        --min-entity-unstake-delay           Minimum unstake delay (seconds)  [number] [required] [default: 1]
        --max-bundle-wait                    Maximum time to wait for a bundle to be submitted (ms)  [number] [required] [default: 1000]
        --max-bundle-size                    Maximum number of operations allowed in the mempool before a bundle is submitted  [number] [required] [default: 10]
        --safe-mode                          Enable safe mode (enforcing all ERC-4337 rules)  [boolean] [required] [default: true]
        --gas-price-bump                     Amount to multiply the gas prices fetched from the node  [string] [default: "100"]
        --gas-price-floor-percent            The minimum percentage of incoming user operation gas prices compared to the gas price used by the bundler to submit bundles  [number] [required] [default: 101]
        --gas-price-expiry                   Maximum that the gas prices fetched using pimlico_getUserOperationGasPrice will be accepted for (seconds)  [number] [default: 10]
        --gas-price-multipliers              Amount to multiply the gas prices fetched using pimlico_getUserOperationGasPrice (format: slow,standard,fast)  [string] [default: "105,110,115"]
        --mempool-max-parallel-ops           Maximum amount of parallel user ops to keep in the meempool (same sender, different nonce keys)  [number] [default: 10]
        --mempool-max-queued-ops             Maximum amount of sequential user ops to keep in the mempool (same sender and nonce key, different nonce values)  [number] [default: 0]
        --enforce-unique-senders-per-bundle  Include user ops with the same sender in the single bundle  [boolean] [default: true]
        --max-gas-per-bundle                 Maximum amount of gas per bundle  [string] [default: "5000000"]
        --config                             Path to JSON config file
    -h, --help                               Show help  [boolean]
    -v, --version                            Show version number  [boolean]

  Compatibility Options:
        --chain-type                               Indicates weather the chain is a OP stack chain, arbitrum chain, or default EVM chain  [string] [choices: "default", "op-stack", "arbitrum"] [default: "default"]
        --legacy-transactions                      Send a legacy transactions instead of an EIP-1559 transactions  [boolean] [required] [default: false]
        --balance-override                         Override the sender native token balance during estimation  [boolean] [required] [default: true]
        --local-gas-limit-calculation              Calculate the bundle transaction gas limits locally instead of using the RPC gas limit estimation  [boolean] [required] [default: false]
        --flush-stuck-transactions-during-startup  Flush stuck transactions with old nonces during bundler startup  [boolean] [required] [default: false]
        --fixed-gas-limit-for-estimation           Use a fixed value for gas limits during bundle transaction gas limit estimations  [string]
        --api-version                              API version (used for internal Pimlico versioning compatibility)  [string] [required] [default: "v1,v2"]
        --default-api-version                      Default API version  [string] [default: "v1"]
        --paymaster-gas-limit-multiplier           Amount to multiply the paymaster gas limits fetched from simulations  [string] [required] [default: "110"]

  Server Options:
        --port                        Port to listen on  [number] [required] [default: 3000]
        --timeout                     Timeout for incoming requests (in ms)  [number]
        --websocket-max-payload-size  Maximum payload size for websocket messages in bytes (default to 1MB)  [number]
        --websocket                   Enable websocket server  [boolean]

  RPC Options:
    -r, --rpc-url                     RPC url to connect to  [string] [required]
        --send-transaction-rpc-url    RPC url to send transactions to (e.g. flashbots relay)  [string]
        --polling-interval            Polling interval for querying for new blocks (ms)  [number] [required] [default: 1000]
        --max-block-range             Max block range for getLogs calls  [number]
        --block-tag-support-disabled  Disable sending block tag when sending eth_estimateGas call  [boolean] [default: false]

  Bundle Compression Options:
        --bundle-bulker-address    Address of the BundleBulker contract  [string]
        --per-op-inflator-address  Address of the PerOpInflator contract  [string]

  Logging Options:
        --json                          Log in JSON format  [boolean] [required] [default: false]
        --network-name                  Name of the network (used for metrics)  [string] [required] [default: "localhost"]
        --log-level                     Default log level  [string] [required] [choices: "trace", "debug", "info", "warn", "error", "fatal"] [default: "info"]
        --public-client-log-level       Log level for the publicClient module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --wallet-client-log-level       Log level for the walletClient module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --rpc-log-level                 Log level for the rpc module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --mempool-log-level             Log level for the mempool module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --executor-log-level            Log level for the executor module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --reputation-manager-log-level  Log level for the executor module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]
        --nonce-queuer-log-level        Log level for the executor module  [string] [choices: "trace", "debug", "info", "warn", "error", "fatal"]

  Debug Options:
        --bundle-mode                               Set if the bundler bundle user operations automatically or only when calling debug_bundler_sendBundleNow.  [string] [required] [choices: "auto", "manual"] [default: "auto"]
        --enable-debug-endpoints                    Enable debug endpoints  [boolean] [required] [default: false]
        --expiration-check                          Should the node make expiration checks  [boolean] [required] [default: true]
        --dangerous-skip-user-operation-validation  Skip user operation validation, use with caution  [boolean] [required] [default: false]
        --deploy-simulations-contract               Should the bundler deploy the simulations contract on startup  [boolean] [required] [default: false]
        --tenderly                                  RPC url follows the tenderly format  [boolean] [required] [default: false]

  πŸ“– For more information, check the our docs:
    * https://docs.pimlico.io/

Security Considerations

When reading the EIP specs, you'll notice that there are many rules a bundler must follow. Although the list of rules may seem long and complex, each one has been extensively debated and discussed by security researchers and builders within the Ethereum ecosystem.

One of the bundler's main jobs is to comply with these rules to prevent all possible DoS attack vectors. These include everything from basic sanity checks that make sure a User Operation is structurally sound to more in-depth tracing for banned opcodes and storage access to make sure bundles cannot be censored once submitted to the network.

Similar to Ethereum clients, all bundler implementations are expected to pass a test suite to ensure compliance and that it won't fragment the mempool.

Spec Tests: https://github.com/eth-infinitism/bundler-spec-tests

πŸ’‘ Stackup's bundler currently maintains 100% coverage of the test suite.

Although the spec is still a work in progress, all future iterations will strive to maintain full compliance coverage.

UserOperation Mempool

The canonical mempool for EIP-4337 is decentralized and is made up of a permissionless P2P network of independent bundlers. To maintain this requirement, it doesn't make any assumptions about which contracts are okay and which are not. All contracts must follow the same rules during validation.

However, there will be cases where some contracts are audited and proven to be safe even though they break some of the rules set by the canonical mempool. In this case, a group of bundlers can create alternative mempools for such exceptions. A common example of when this might be needed is in the case of a Deposit Paymaster that can abstract gas fees with any ERC-20 token.

Another role of the bundler is to maintain a connection to the canonical mempool and also any other alternative mempools it opts into. To read more about this topic, we highly recommend checking out this article.

When a bundler receives a UserOperation, it must first run some basic sanity checks, namely that:

  • Either the sender is an existing contract, or the initCode is not empty (but not both).
  • If initCode is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked.
  • The verificationGasLimit is sufficiently low (<= MAX_VERIFICATION_GAS) and the preVerificationGas is sufficiently high (enough to pay for the calldata gas cost of serializing the UserOperation plus PRE_VERIFICATION_OVERHEAD_GAS).
  • The paymasterAndData is either empty or starts with the paymaster address. The paymaster must (i) currently have nonempty code on chain, (ii) have a sufficient deposit to pay for the UserOperation, and (iii) not be currently banned.
  • The callGas is at least the cost of a CALL with non-zero value.
  • The maxFeePerGas and maxPriorityFeePerGas are above a configurable minimum value that the bundler is willing to accept. At the minimum, they are sufficiently high to be included with the current block basefee.
  • The sender doesn't have another UserOperation already present in the pool (or it replaces an existing entry with the same sender and nonce, with higher maxPriorityFeePerGas and an equally increased maxFeePerGas).

If the UserOperation object passes these sanity checks, the bundler must next run the first op simulation, and if the simulation succeeds, the bundler must add the op to the pool. A second simulation must also happen during bundling to make sure the UserOperation is still valid.

Simulation

In order to add a UserOperation into the UserOp mempool (and later to add it into a bundle), we need to "simulate" its validation to make sure it is valid, and that it is capable of paying for its own execution. In addition, we need to verify that the same will hold true when executed on-chain. For this purpose, a UserOperation is not allowed to access any information that might change between simulation and execution, such as current block time, number, hash, etc.

A UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so that it is impossible to invalidate a large number of UserOperations with a single state change.

There are three special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and the signature aggregator. Each of these contracts is also restricted in its storage access, to make sure UserOperation validations are isolated.

Acknowledgements

Special thanks to the original creators of the pimlico/alto project. This fork wouldn't be possible without their foundational work.

The ERC-4337 Team and the Ethereum community for their continued support and guidance in the development of the EIP-4337 specification.

License

This project is licensed under the GNU GENERAL PUBLIC LICENSE. See the LICENSE file for details.


About

I forked Pimlico Alto Bundler and set it up for 1click deploy

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 98.7%
  • Other 1.3%