DEV Community

Cover image for Building a Pons v2 Sniper Bot on Robinhood Chain with TypeScript
hamssog
hamssog

Posted on Originally published at hamssog.substack.com

Building a Pons v2 Sniper Bot on Robinhood Chain with TypeScript

A Pons sniper bot should not be reduced to:

new launch → buy
Enter fullscreen mode Exit fullscreen mode

The difficult part is everything around the transaction: identifying the right launch, checking Pons v2 state, accounting for snipe tax, calculating the curve quote, applying risk controls, simulating or executing the trade, monitoring the transaction, and reconciling the final position.

The reference implementation for this article is:

GitHub:
https://github.com/0xhamssog/pons-sniper-bot

The repository is a Pons v2 implementation for Robinhood Chain. Pons v2 starts on a bonding curve and later graduates into a Uniswap v4 pool, so its sniper logic should be treated separately from the current pool-based Pons launch architecture.

The Implementation Flow

The sniper can be thought of as one pipeline:

TokenLaunched
    ↓
Launch filters
    ↓
Snipe-tax check
    ↓
Curve quote
    ↓
Risk checks
    ↓
Dry run / live execution
    ↓
Transaction monitoring
    ↓
Reconciliation
Enter fullscreen mode Exit fullscreen mode

The rest of the codebase exists to make those transitions reliable.


Pons v2 Is a Different Trading Model

Pons v2 starts a launch on a bonding curve:

CREATE
  ↓
BONDING CURVE
  ↓
CURVE TRADING
  ↓
CURVE COMPLETION
  ↓
UNISWAP V4
Enter fullscreen mode Exit fullscreen mode

For a sniper, this means the entry logic operates against the curve rather than assuming a normal DEX pool already exists.

That affects quote calculation, tax handling, execution, and graduation logic.


1. Project Structure

A practical TypeScript implementation can be organized like this:

src/
├── chain.ts
├── clients.ts
├── protocol.ts
├── quote.ts
├── tx.ts
├── wallets.ts
├── env.ts
│
└── sniper/
    ├── cli.ts
    ├── config.ts
    ├── wallet.ts
    ├── quote/
    │   └── PonsQuoteEngine.ts
    └── execution/
Enter fullscreen mode Exit fullscreen mode

The repository separates protocol integration, quote logic, transaction handling, wallet access, configuration, and sniper-specific behavior.

That makes it easier to change the strategy without rewriting the blockchain layer.


2. Detect New Pons v2 Launches

The first important event is:

TokenLaunched
Enter fullscreen mode Exit fullscreen mode

The detector should turn that event into an internal opportunity:

type LaunchOpportunity = {
  token: `0x${string}`;
  curve: `0x${string}`;
  deployer: `0x${string}`;
  pairToken: `0x${string}`;

  blockNumber: bigint;
  txHash: `0x${string}`;
};
Enter fullscreen mode Exit fullscreen mode

The detector's job is simply:

A new launch exists.

It should not immediately decide that the launch should be traded.

That keeps detection separate from strategy.


3. Filter the Opportunity

Not every launch should reach the quote engine.

The reference implementation supports configurable filters around areas such as:

Creator address
Token symbol
Quote asset
Reserve size
Creator tax
Snipe tax
Maximum positions
Maximum exposure
Entry frequency
Cooldown
Enter fullscreen mode Exit fullscreen mode

The practical result is:

TokenLaunched
     ↓
Basic validation
     ↓
Launch filters
     ↓
PASS / REJECT
Enter fullscreen mode Exit fullscreen mode

For one deployment, the filters might favor specific creators.

For another, they might focus on reserve size or exposure.

The protocol integration remains unchanged.


4. Snipe Tax Is Part of the Trading Decision

One of the key Pons v2 mechanics is the opening snipe tax.

The protocol exposes:

currentSnipeTaxBps(recipient)
Enter fullscreen mode Exit fullscreen mode

The tax applies to buys during the opening period, so a sniper cannot calculate a curve quote and assume that the quoted token output is the final amount received.

The bot needs to ask:

Who receives the tokens?
        ↓
What is the current snipe tax?
        ↓
Is it within the strategy limit?
Enter fullscreen mode Exit fullscreen mode

For example:

const snipeTaxBps =
  await client.readContract({
    address: curveAddress,
    abi: curveAbi,
    functionName: "currentSnipeTaxBps",
    args: [recipient],
  });

if (snipeTaxBps > maxAllowedSnipeTaxBps) {
  return {
    approved: false,
    reason: "SNIPE_TAX_TOO_HIGH",
  };
}
Enter fullscreen mode Exit fullscreen mode

The reference implementation can wait for a configured acceptable tax condition instead of blindly entering the first possible block.

That is a strategy decision, not just a protocol read.


5. Build the Curve Quote From Current Protocol State

Pons v2 quoting needs the actual curve state, not a generic DEX quote.

The quote calculation should use the protocol's pricing reserves together with the relevant fee and tax inputs:

Input Amount
    ↓
Curve Reserves
    ↓
Trade Fee
    ↓
Creator Tax
    ↓
Snipe Tax
    ↓
Expected Output
    ↓
Slippage Protection
Enter fullscreen mode Exit fullscreen mode

For Pons v2, getReserves() provides the curve's pricing state. The raw token balance of the contract should not simply be substituted for the pricing reserve.

Conceptually:

const [quoteReserve, tokenReserve] =
  await client.readContract({
    address: curveAddress,
    abi: curveAbi,
    functionName: "getReserves",
  });
Enter fullscreen mode Exit fullscreen mode

A useful quote object is:

type CurveQuote = {
  amountIn: bigint;
  expectedTokensOut: bigint;
  minimumTokensOut: bigint;

  priceImpactBps: bigint;
  snipeTaxBps: bigint;
};
Enter fullscreen mode Exit fullscreen mode

This gives the risk engine concrete values to inspect.

A quote is still not a guaranteed execution result.

Suppose:

Expected output:
1,000,000 TOKEN

Maximum slippage:
8%
Enter fullscreen mode Exit fullscreen mode

The transaction should therefore carry a minimum acceptable output:

Curve quote
    ↓
Slippage limit
    ↓
minimumTokensOut
Enter fullscreen mode Exit fullscreen mode

For example:

type ExecutionRequest = {
  token: `0x${string}`;
  buyAmount: bigint;

  expectedTokensOut: bigint;
  minimumTokensOut: bigint;

  maxSlippageBps: bigint;
};
Enter fullscreen mode Exit fullscreen mode

This keeps reserve state, taxes, quote calculation, and slippage protection in one coherent quote stage rather than scattering the logic across multiple layers.


6. Keep Trading Amounts Type-Safe

Use bigint for blockchain quantities:

type TokenAmount = bigint;
type QuoteAmount = bigint;
type BasisPoints = bigint;
Enter fullscreen mode Exit fullscreen mode

For example:

```ts id="0ioy63"
type RiskRequest = {
buyAmount: TokenAmount;
expectedTokensOut: TokenAmount;
maxSlippageBps: BasisPoints;
};




Avoid converting token amounts to JavaScript `number` merely for convenience.

Token atomic units, quote currency, and basis points are different units and should remain explicit in the code.

---

## 7. Risk Comes Before Execution

A valid launch is not automatically a valid trade.

The reference implementation supports controls such as:



```plaintext
Maximum buy
Maximum exposure
Maximum positions
Gas reserve
Maximum snipe tax
Maximum slippage
Entry frequency
Cooldown
Enter fullscreen mode Exit fullscreen mode

A risk decision can remain simple:

type RiskDecision =
  | {
      approved: true;
      buyAmount: bigint;
    }
  | {
      approved: false;
      reason: string;
    };
Enter fullscreen mode Exit fullscreen mode

Rejected opportunities should be recorded explicitly.

That makes it possible to answer:

Why did the bot skip this launch?

without inspecting logs manually.


8. Dry Run Should Exercise the Real Path

A useful dry-run mode should perform the actual detection, quote, and risk logic rather than merely printing a message.

The repository supports:

npm run pons -- sniper run --dry-run
Enter fullscreen mode Exit fullscreen mode

The flow can be:

Detect
  ↓
Filter
  ↓
Read snipe tax
  ↓
Quote
  ↓
Risk
  ↓
Simulate
Enter fullscreen mode Exit fullscreen mode

without signing or broadcasting a live transaction.

That gives you a way to test the trading path with real protocol reads before enabling live execution.


9. Live Execution Should Be Explicit

Live mode is deliberately separate:

npm run pons -- sniper run --live
Enter fullscreen mode Exit fullscreen mode

Combined with the repository's live-trading configuration, this creates a clear boundary between:

Development
   ↓
Dry run
Enter fullscreen mode Exit fullscreen mode

and:

Explicit configuration
   +
Explicit live command
   ↓
Transaction broadcast
Enter fullscreen mode Exit fullscreen mode

For unattended trading, that separation is useful.


10. Use a Transaction State Machine

Once a trade is approved, the execution layer should track where it is in the lifecycle.

DETECTED
   ↓
VALIDATED
   ↓
QUOTED
   ↓
RISK_APPROVED
   ↓
TX_PREPARED
   ↓
SIGNED
   ↓
SUBMITTED
   ↓
PENDING
   ↓
CONFIRMED
Enter fullscreen mode Exit fullscreen mode

Failure states should also exist:

REJECTED
REVERTED
UNKNOWN
Enter fullscreen mode Exit fullscreen mode

This gives the system enough state to recover from interruptions instead of treating every transaction as a single function call.


11. RPC Timeout Does Not Automatically Mean Failure

A particularly dangerous assumption is:

RPC timeout
=
transaction failed
Enter fullscreen mode Exit fullscreen mode

That is not necessarily true.

A timeout may happen after the transaction has already been submitted.

A safer sequence is:

RPC timeout
    ↓
UNKNOWN
    ↓
Inspect original transaction
    ↓
Confirmed?
 /       \
YES       NO
 ↓         ↓
Track     Recover
Enter fullscreen mode Exit fullscreen mode

The reference implementation preserves an UNKNOWN state and reconciles the original transaction before deciding what happened.

This helps avoid duplicate execution after ambiguous RPC failures.


12. Handle Partial Fills Near Graduation

Pons v2 can partially fill a final curve buy when the remaining curve inventory is smaller than the requested trade.

For example:

Requested:
1 ETH

Remaining capacity:
0.6 ETH

Actual:
partial fill
+
refund
Enter fullscreen mode Exit fullscreen mode

So:

requested amount
≠
guaranteed executed amount
Enter fullscreen mode Exit fullscreen mode

The position must ultimately be updated from actual execution results.

This is especially important as a launch approaches curve completion.


13. Graduation Is a State Change

A Pons v2 launch eventually moves from the curve into Uniswap v4.

That means the bot should understand the difference between:

CURVE
Enter fullscreen mode Exit fullscreen mode

and:

UNISWAP V4
Enter fullscreen mode Exit fullscreen mode

A focused sniper implementation can intentionally limit its entry scope to the curve phase.

That is preferable to silently mixing curve execution and post-graduation routing in the same strategy.


14. Persist Opportunities and Execution State

A live sniper needs to remember what it has already processed.

Useful persistent entities include:

opportunity
execution
transaction
position
Enter fullscreen mode Exit fullscreen mode

Conceptually:

Opportunity
    ↓
Execution
    ↓
Transaction
    ↓
Position
Enter fullscreen mode Exit fullscreen mode

After a restart, the bot should still know whether an opportunity is pending, confirmed, failed, or already reconciled.

This prevents a process restart from turning into an accidental duplicate trade.


15. Make Event Processing Recoverable

A launch detector should keep a persistent block cursor:

type IndexerCursor = {
  lastProcessedBlock: bigint;
};
Enter fullscreen mode Exit fullscreen mode

Processing then becomes:

Saved block
    ↓
Next block range
    ↓
Read events
    ↓
Persist
    ↓
Advance cursor
Enter fullscreen mode Exit fullscreen mode

For RPC backfills, bounded block ranges are preferable to one huge getLogs request.

That gives the worker a clear recovery point after a crash or timeout.


16. Add Event Idempotency

Retries should not create duplicate opportunities.

A useful event identity is:

transaction hash + log index
Enter fullscreen mode Exit fullscreen mode

A database constraint can enforce that:

UNIQUE (tx_hash, log_index)
Enter fullscreen mode Exit fullscreen mode

Then a retry becomes safe:

Process range
     ↓
Worker fails
     ↓
Retry range
     ↓
Same event
     ↓
Ignored
Enter fullscreen mode Exit fullscreen mode

This is a small implementation detail that makes the indexer much more reliable.


17. Example End-to-End Run

Imagine a new Pons v2 launch appears.

Detection

TokenLaunched
      ↓
Launch detected
Enter fullscreen mode Exit fullscreen mode

Filtering

Creator accepted
Symbol accepted
Reserve acceptable
Enter fullscreen mode Exit fullscreen mode

Snipe tax

Current tax
      ↓
Within configured limit
Enter fullscreen mode Exit fullscreen mode

Quote

Buy amount
      ↓
Curve state
      ↓
Fees / taxes
      ↓
Expected output
Enter fullscreen mode Exit fullscreen mode

Risk

Position limit
Exposure limit
Slippage
Gas reserve
      ↓
APPROVED
Enter fullscreen mode Exit fullscreen mode

Execution

Dry run / live
      ↓
Transaction
      ↓
Monitor
Enter fullscreen mode Exit fullscreen mode

Reconciliation

Receipt
   ↓
Actual token result
   ↓
Position
   ↓
Reconciled
Enter fullscreen mode Exit fullscreen mode

18. Reference CLI

The repository provides a useful progression:

npm install

cp .env.example .env

npm run pons -- status

npm run pons -- sniper test

npm run pons -- sniper run --dry-run

npm run pons -- sniper status

npm run pons -- sniper config

npm run pons -- sniper reconcile
Enter fullscreen mode Exit fullscreen mode

Live execution is explicit:

npm run pons -- sniper run --live
Enter fullscreen mode Exit fullscreen mode

This makes the repository usable as both a development reference and a starting point for a larger trading application.


19. What the Repository Demonstrates

The value of the GitHub project is that it gives the implementation a concrete reference.

A potential client can inspect:

Protocol integration
Curve quote engine
Launch filters
Risk controls
Dry-run execution
Live execution
Transaction states
Recovery
Reconciliation
Enter fullscreen mode Exit fullscreen mode

That is stronger proof than an article describing the same components conceptually.

GitHub:
https://github.com/0xhamssog/pons-sniper-bot

The repository also contains Pons v2 bundler functionality, but the sniper implementation is the focus here.


20. Extending the Reference Implementation

A client-specific build could add:

Creator scoring
Token scoring
Advanced launch filters
Multiple strategies
Automated take-profit
Stop-loss
Trailing exits
Telegram alerts
Discord alerts
Dashboard
REST API
WebSocket API
Multi-wallet support
Performance analytics
Enter fullscreen mode Exit fullscreen mode

The existing execution foundation can remain reusable while the strategy layer changes.

For example:

Pons v2 Launch
      ↓
Creator score
      ↓
Token score
      ↓
Snipe tax
      ↓
 Curve state
      ↓
  Strategy
      ↓
     Risk
      ↓
Execution
      ↓
     Exit
Enter fullscreen mode Exit fullscreen mode

Final Takeaway

The important part of a Pons v2 sniper bot is not simply detecting a TokenLaunched event.

The engineering challenge is handling everything that follows:

Launch
→ Filter
→ Snipe Tax
→ Curve Quote
→ Risk
→ Execution
→ Monitoring
→ Reconciliation
Enter fullscreen mode Exit fullscreen mode

The implementation becomes much more robust when each stage owns a specific responsibility and persists enough state to recover after failures.

That is the difference between a launch listener and a trading system.

GitHub

Pons v2 Sniper Bot:
https://github.com/0xhamssog/pons-sniper-bot

Building a Custom Pons Sniper Bot?

I build custom Pons and Robinhood Chain trading systems, including Pons sniper bots, copy-trading systems, wallet trackers, launch monitors, trading terminals, token scanners, risk engines, and automated execution infrastructure.

Projects can start from an existing repository, prototype, technical specification, or a completely new system.

Top comments (0)