Skip to content

An example automated filler bot for cross-chain swaps from CompactX

Notifications You must be signed in to change notification settings

Uniswap/Neutrofill

Folders and files

NameName
Last commit message
Last commit date

Latest commit

af48f30 · Mar 11, 2025
Feb 20, 2025
Feb 20, 2025
Mar 3, 2025
Mar 11, 2025
Feb 20, 2025
Feb 20, 2025
Feb 27, 2025
Feb 20, 2025
Feb 20, 2025
Feb 20, 2025
Mar 3, 2025
Feb 21, 2025
Feb 20, 2025
Feb 26, 2025
Mar 3, 2025
Feb 20, 2025
Feb 20, 2025

Repository files navigation

Neutrofill

Neutrofill is an automated filler bot that processes inbound transaction requests from CompactX and executes them if deemed profitable. It maintains a server-side wallet and automatically submits transactions that meet profitability criteria.

Features

  • Accepts inbound POST requests for transaction execution
  • Real-time ETH price monitoring across multiple chains via CoinGecko
  • Profitability analysis before transaction submission
  • Support for multiple chains (Ethereum, Optimism, Base, Unichain)
  • WebSocket support for real-time transaction status updates
  • Automated cross-chain token rebalancing via bridging using the Across Protocol
  • Automated single-chain token rebalancing via swaps using the Uniswap API

A hosted version can be accessed at neutrofill.com — note that this service is meant to serve as a reference for demonstration purposes and no guarantees about its uptime or reliability are provided.

This project is mostly meant to serve as a starting point until more mature standards like the Open Intents Framework have fully integrated with The Compact.

Getting Started

Prerequisites

  • Node.js 18+
  • npm 9+

Installation

  1. Clone the repository:

    git clone https://github.com/Uniswap/Neutrofill.git
    cd Neutrofill
  2. Install dependencies:

    npm install
  3. Set up Git hooks:

    npx husky init
  4. Copy .env.example to .env and configure your environment:

    cp .env.example .env

Development

  • Start the development server with hot reload:

    npm run dev
  • Run type checking:

    npm run typecheck
  • Run the test suite:

    npm test
  • Format and lint code:

    npm run fix

Building

Build both server and client:

npm run build

This will:

  • Compile TypeScript server code to dist/server/
  • Build React client to dist/client/

Production

Start the production server in production mode:

npm start

Or start the production server with debug logs enabled:

npm run start:debug

Cross-Chain Token Rebalancing

Neutrofill includes an automated cross-chain token rebalancing mechanism that helps maintain target token percentages across multiple blockchain networks. This maintatins sufficient funds on each supported chain to operate efficiently.

Rebalancing Features

  • Configurable target percentages for each chain
  • Token-specific rebalancing priorities
  • Automatic detection of chains that need funds
  • Cooldown periods between rebalancing operations
  • Minimum and maximum rebalance amounts
  • Transaction tracking and status monitoring
  • Event-based notifications for rebalance operations

How It Works

For cross-chain bridging:

  1. The system monitors token balances across all supported chains
  2. When a chain's balance falls below its configured threshold, a rebalance operation is triggered
  3. The system identifies a source chain with excess funds
  4. Funds are transferred from the source chain to the destination chain using the Across Protocol
  5. The operation is tracked until completion

For single-chain swapping, neutrofill uses the Uniswap API. Note that all swaps are performed on Unichain to allow for best execution with lowg gas costs.

Configuration

Rebalancing is configured in src/server/config/rebalance.ts. You can customize:

  • Target percentage for each chain / token
  • Trigger thresholds for rebalancing
  • Token priorities for rebalancing
  • Global settings like minimum/maximum amounts and cooldown periods

Configuration

The following environment variables are required:

  • PORT: Server port (default: 3000)
  • PRIVATE_KEY: Private key for the wallet that will execute transactions
  • RPC_URL_MAINNET: Ethereum mainnet RPC URL
  • RPC_URL_OPTIMISM: Optimism RPC URL
  • RPC_URL_BASE: Base RPC URL
  • RPC_URL_UNICHAIN: Unichain RPC URL
  • COINGECKO_API_KEY: CoinGecko API key
  • UNISWAP_API_KEY: Uniswap API key
  • COMPACT_INDEXER: Graphql indexing service (e.g. Ponder node) for The Compact

Supported Networks

  • Mainnet (Chain ID: 1)
  • Optimism (Chain ID: 10)
  • Base (Chain ID: 8453)
  • Unichain (Chain ID: 130)

API Endpoints

POST /broadcast

Submit a transaction for potential execution. Generally, this will be relayed from a service like disseminator — reach out to be added as a webhook.

Request body must conform to the BroadcastRequest schema which includes:

  • chainId: Chain ID where the resource lock resides (numeric string or hex)
  • compact: Compact message object
    • arbiter: Ethereum address
    • sponsor: Ethereum address
    • nonce: 32-byte hex string
    • expires: Numeric string or hex
    • id: Numeric string or hex
    • amount: Numeric string or hex
    • mandate: Mandate object
  • sponsorSignature: 64-byte hex string, '0x', or null (no signature indicates an onchain registration)
  • allocatorSignature: 64-byte hex string
  • context: Context object
    • dispensation: Numeric string or hex
    • dispensationUSD: String (can include $ prefix)
    • spotOutputAmount: Numeric string or hex
    • quoteOutputAmountDirect: Numeric string or hex
    • quoteOutputAmountNet: Numeric string or hex
    • deltaAmount: (optional) Numeric string or hex (can be negative)
    • slippageBips: (optional) Number between 0-10000
    • witnessTypeString: String
    • witnessHash: 32-byte hex string
    • claimHash: (optional) 32-byte hex string
  • claimHash: (optional) 32-byte hex string

The mandate object represents parameters related to the fill chain and contains:

  • chainId: Chain ID where the resource lock resides (positive number)
  • tribunal: Ethereum address
  • recipient: Ethereum address
  • expires: Numeric string or hex
  • token: Ethereum address
  • minimumAmount: Numeric string or hex
  • baselinePriorityFee: Numeric string or hex
  • scalingFactor: Numeric string or hex
  • salt: 32-byte hex string

Be sure that any ECDSA signatures are encoded in their "compact" representation using EIP-2098.

GET /health

Check server health status.

Response:

{
  status: "ok",
  timestamp: number  // Unix timestamp in seconds
}

WebSocket Connection

Connect to /ws for real-time transaction status updates. Note: WebSocket endpoint does not support CORS - connections must originate from the same origin.

Message Types:

  1. Connection Status (Server -> Client):
{
  type: "connection",
  status: "connected" | "disconnected",
  timestamp: string  // ISO timestamp
}
  1. Heartbeat (Bidirectional):
{
  type: "ping" | "pong",
  timestamp: string  // ISO timestamp
}
  1. Fill Request Status (Server -> Client):
{
  type: "fillRequest",
  success: boolean,
  request: {
    chainId: string,
    compact: {
      arbiter: string,
      sponsor: string,
      nonce: string,
      expires: string,
      id: string,
      amount: string,
      mandate: {
        chainId: number,
        tribunal: string,
        recipient: string,
        expires: string,
        token: string,
        minimumAmount: string,
        baselinePriorityFee: string,
        scalingFactor: string,
        salt: string
      }
    },
    sponsorSignature: string | null,
    allocatorSignature: string,
    context: {
      dispensation: string,
      dispensationUSD: string,
      spotOutputAmount: string,
      quoteOutputAmountDirect: string,
      quoteOutputAmountNet: string,
      deltaAmount?: string,
      slippageBips?: number,
      witnessTypeString: string,
      witnessHash: string,
      claimHash?: string
    }
  },
  error?: string,  // Present only if success is false
  transactionHash?: string,  // Present only if success is true
  details?: {  // Present only if success is true
    dispensationUSD: number,
    gasCostUSD: number,
    netProfitUSD: number,
    minProfitUSD: number
  }
}
  1. Price Update (Server -> Client):
{
  type: "price_update",
  chainId: number,
  price: string,  // Price in USD
  timestamp: string  // ISO timestamp
}
  1. Account Update (Server -> Client):
{
  type: "account_update",
  account: string,
  chainId: number,
  balances: Record<string, string>,  // Token balances in wei
  timestamp: string  // ISO timestamp
}
  1. Error (Server -> Client):
{
  type: "error",
  code: string,
  message: string,
  timestamp: string  // ISO timestamp
}

Code Quality

  • TypeScript for type safety
  • Biome for linting and formatting
  • Jest for testing
  • Husky for Git hooks
  • lint-staged for pre-commit checks

Deployment

The project includes a setup script for deploying to a cloud server with automatic HTTPS configuration using Let's Encrypt.

Prerequisites

  • A domain name pointing to your server (A record)
  • Ubuntu-based cloud server
  • SSH access to the server

Deployment Steps

  1. SSH into your server:
ssh user@your-server
  1. Clone the repository:
git clone https://github.com/Uniswap/neutrofill.git
cd neutrofill
  1. Run the setup script with your domain and IP:
./scripts/setup-server.sh your-domain.com your-server-ip

For example:

./scripts/setup-server.sh neutrofill.com 167.172.1.91

The script will:

  • Install required dependencies (Node.js, nginx, certbot)
  • Set up the project in /opt/neutrofill
  • Configure nginx with WebSocket support
  • Set up SSL certificates with Let's Encrypt
  • Create and enable a systemd service
  • Start the server

Monitoring

Monitor the server status:

sudo systemctl status neutrofill

View server logs:

sudo journalctl -u neutrofill -f

Testing Deployed Server

Test WebSocket connection:

wscat -c wss://your-domain.com/ws

Test broadcast endpoint:

curl -X POST https://your-domain.com/broadcast \
-H "Content-Type: application/json" \
-d '{ ... your payload ... }'

Test health endpoint:

curl https://your-domain.com/health

License

MIT

About

An example automated filler bot for cross-chain swaps from CompactX

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages