stacc-arb
v0.4.4
Published
Triangular NFT arbitrage: Number Go Up curves, Staccpad NFT CLMMs (Uniswap v4) and OpenSea on every LayerZero chain. Atomic where a contract can guard it, journaled and resumable where it cannot.
Maintainers
Readme
stacc-arb
Triangular NFT arbitrage on Robinhood Chain (4663) and every LayerZero chain its collections reach:
Number Go Up curve ──── mint / redeem ────┐
│ │
companions + locked LP (v6) │
▼ ▼
Staccpad NFT CLMM (Uniswap v4) ◄── LayerZero ──► OpenSea on 25 chains (Seaport 1.6)One command indexes every venue and prices one NFT each way on each; scan lists every route with a
positive edge; run --execute fires the atomic ones through one contract call and walks the non-atomic
ones leg by leg with a journal you can resume.
npx stacc-arb markets # every NFT CLMM (both factories), both quotes
npx stacc-arb curves # every NGU curve: mint, redeem, v6 LP terms
npx stacc-arb scan --opensea-key $OPENSEA_API_KEY # all routes, dry-run, sorted by edge
npx stacc-arb run --opensea-key $OPENSEA_API_KEY --private-key $PK --execute --min-profit 0.0005
npx stacc-arb pawn # every Staccpad Pawn pool + open ticket, health, bounties
npx stacc-arb pawn --watch --execute --private-key $PK # keeper: liquidate tickets past their line, keep the bounty
npx stacc-arb status # what you hold mid-route
npx stacc-arb resume --private-key $PK --execute # continue bridges that were in flightEnvironment works too: OPENSEA_API_KEY, PRIVATE_KEY, RPC_URL.
Venues
| venue | discovered from | priced by |
| --- | --- | --- |
| Staccpad NFT CLMMs | both StaccpadFactory stacks on Robinhood (0x9f6A…1E14, 0xF0BE…9Fe9) | exact eth_call round trip through router + hook + PoolManager (main stack), tick math (other) |
| NGU curves | seven NguFactory generations, v6 first | quoteBuy(1) / quoteSell(1) on the curve |
| OpenSea | Robinhood, plus every chain a collection has a LayerZero mirror on (mirrorOf on portals v4/v5) | best listing / best collection bid, in that chain's ETH |
| betfi.fun | the live board (staccpad.fun/api/swap/bets?board=1): zero-fee FULL_OPEN Seaport orders | asks (NFT for ETH/WETH) and bids (WETH/USDG for an id or any id) |
Routes
| route | legs | atomic |
| --- | --- | --- |
| stacc->stacc | buy the floor in one quote's pool, sell into the same collection's other pool | ✅ one tx, reverts unless profit |
| os->stacc | buy the OpenSea listing on Robinhood, sell into the pool | ✅ |
| stacc->os | buy the pool floor, accept the OpenSea collection bid on Robinhood | ⚠️ OpenSea signs restricted bids only once you hold the NFT → 2 tx |
| ngu->stacc | mint one on the curve, sell the buyer NFT into its CLMM — you keep the LP position NFT and its fees | 2 tx |
| stacc->ngu | buy a buyer id out of the CLMM (FIFO floor, or snipe past companions), redeem on the curve at floor | 2 tx |
| stacc->stacc* | cross-quote hop on the OMNIGODS stack or between stacks | 2 tx |
| os:<chain>->rh | buy the mirror on OpenSea <chain>, portal.send it home, sell into the CLMM or redeem on the curve | 3–4 tx over LayerZero |
| rh->os:<chain> | CLMM floor or curve mint, bridge out, accept the foreign collection bid | 3–4 tx over LayerZero |
| os:<A>->os:<B> | buy the mirror's listing on chain A, bridge to B, accept B's collection bid | 3–4 tx over LayerZero |
| ngu->os / os->ngu | curve mint → OpenSea bid on Robinhood; OpenSea listing → curve redeem | 2 tx |
| betfi->stacc / stacc->betfi | betfi.fun ask → pool; pool floor → betfi bid | ✅ atomic (plain Seaport orders, no zone) |
| betfi->ngu / ngu->betfi | betfi ask → curve redeem; curve mint → betfi collection bid | 2 tx |
v6 curves mint 1–10 companions into the collection's CLMM on every buy and lock a creator-set slice of the
payment as the minter's liquidity. Companions never redeem on the curve, so stacc->ngu only ever targets buyer
ids (isCompanion is checked), and ngu->stacc counts the LP position you receive as a bonus, not as edge.
Pawn keeper
staccpad.fun/pawn is a perpetual pawn desk: holders take ltv of the CLMM floor now
and the debt ticks up at a utilisation rate; once it reaches liq of what the CLMM (or NGU curve) pays today,
anyone may liquidate and keep bounty of the sale. stacc-arb pawn prices every open ticket exactly as the desk
does (floorOf = a simulated sell) and --watch --execute fires the ones past the line, simulated first so a
raced ticket costs nothing. Desk 0x38C1E08eBF7959Fd5ffA824025437a0cB76b0026 (v3).
Non-atomic execution
Every leg is simulated (eth_call) before it is sent. Each hash is written to ~/.stacc-arb/journal.json
(STACC_ARB_HOME to move it) the moment it lands, with the ids you now hold. bridge.wait polls the
destination for ownership; if LayerZero takes longer than the timeout the run is left running and
resume --execute continues at that leg. Nothing is ever deleted from the journal.
Foreign-chain legs are priced in ETH only: chains whose gas token is not ETH (Polygon, Avalanche, BNB, …) are read for mirrors but skipped for pricing until a rate source is wired.
The atomic contract — StaccpadSeaportArb
0x61D4D59E39E292503894564aD26F0E753A47903D (Robinhood Chain, bound to router 0xa3f7…E740)Stateless, permissionless, sweeps every asset back to the caller and reverts with NotEnoughProfit
unless the caller's balance in profitToken grew by minProfit. Only the main stack's pools are reachable
through it; everything else runs stepwise.
Options
--opensea-key <key> OpenSea API key (or OPENSEA_API_KEY). Without it only on-chain routes are searched.
--private-key <hex> wallet that funds and receives every leg (or PRIVATE_KEY). Needed for --execute.
--rpc <url> Robinhood JSON-RPC (or RPC_URL). Other chains come from data/chains.json.
--min-profit <eth> minimum net edge in ETH to execute. Default 0.0005
--slippage <bps> headroom on quotes. Default 200
--eth-usd <price> override ETH/USD used for USDG legs. Default: Coinbase spot.
--interval <sec> run loop period. Default 20
--routes <a,b> restrict routes, e.g. --routes ngu->stacc,os:base->rh
--no-chains skip the LayerZero / foreign OpenSea legs (fast local scan)
--account-for-creator-royalties
credit royalties you receive yourself back to the edge
--creator <a,b> who receives them (default: the --private-key wallet)
--execute in run/resume actually send transactions (default dry-run)
--json machine-readable outputstacc-arb atomic … keeps the 0.1.x single-chain CLI; stacc-arb --config arb.json … is the explicit-config
multichain engine for collections that are not Staccpad-native.
If you are the creator
A creator royalty is a cost to an arber and income to the creator. If the collection pays you, an edge that counts your own royalty as a cost is understating itself, and you will skip trades that actually pay.
stacc-arb scan --account-for-creator-royalties --creator 0x26E8…5158It credits back only what you actually receive: the ERC-2981 receiver on a staccpad pool leg, and the individual Seaport fee recipients on a listing you fill or a bid you accept, each matched against your own addresses. On OpenSea that is whatever the order really pays you, which is the creator earnings the collection is configured for, not the rate the contract's ERC-2981 happens to return. A collection set to 10% on OpenSea and 2.5% on chain credits 10% on the OpenSea leg and 2.5% on the pool leg, because that is what each one actually hands over. Nothing is assumed from the fact that you are the one running it, and the number is never folded into the price. A candidate keeps the market edge and gains a line beside it:
stacc->stacc* Strategic Portraits est +0.000002215842140879 Ξ · 2 tx
[staccpad] buy floor #7 @ 0.00049 ETH → [peg] sell into ETH pool for 0.00048 ETH
market edge -0.000009623367267801 Ξ + 0.00001183920940868 Ξ your own creator royaltyThat one is a loser at the market and a winner to its creator. The ranking sorts on the adjusted number;
--json carries edgeBeforeRoyaltyEth, royaltyBackEth and royaltyNote so you can always take it back out.
