# Argus & LIFT Trading API > Trade ArgusPad and lift.fun tokens on Arc through one keyless API --- # Argus & LIFT Trading API: Trade ArgusPad and lift.fun Tokens on Arc > Build bots that trade Argus and LIFT tokens on Arc, Circle's USDC-native chain, through one free, keyless API. Buy and sell in the Uniswap pool every launch lives in, with the launchpad's fees and snipe tax already inside the quote. # Argus & LIFT Trading API The simplest way to trade tokens from Argus and LIFT, the two launchpads on Arc, through an API. Buy and sell any of their tokens in the Uniswap pool it launched into, in USDC, with one request. shrine.trade is an Arc API for executing buys and sells and reading a token's pool, fees and snipe tax before you trade. Start with [Buy & Sell](/local-trade), watch the [live feed](/live-launches), or look a token up on [Token Info](/token-info). ## One API, both launchpads Pass a token address and the API finds where it trades: an Argus pool (Uniswap v4 with the Argus hook), a LIFT pool (Uniswap v4 with the LIFT hook, or Uniswap v3 for earlier LIFT launches), or any other Uniswap pool on Arc priced in USDC. The request is the same in every case, and the response says which it was. ## Argus API [Argus](https://argus.world), also known as ArgusPad, is the busiest launchpad on Arc: well over a hundred thousand tokens launched since Arc mainnet opened, each into a Uniswap v4 pool with Argus's own fee hook that takes the creator tax and a decaying snipe tax. The API resolves any Argus token to its pool and hook, quotes through that hook, and builds the buy or sell. The live feed reports each Argus launch with its creator, tax rates and image. ## LIFT API [LIFT](https://lift.fun) launches tokens straight into a permanently locked Uniswap pool: v4 with the LIFT hook for current launches, v3 for its first generation. There is no bonding curve and no migration. The API trades both generations, reports LIFT's base fee and per-pool creator tax, and streams every LIFT launch with the creator's first buy and the pinned metadata. ## Everything is USDC Arc's native token is USDC. Gas is paid in USDC, every launchpad pool is priced in USDC, and the `amount` you pass on a buy is USDC to spend. A wallet with USDC on Arc can trade; there is nothing else to hold. ## Why shrine.trade - **Nothing to sign up for.** No account, no API key, no rate plan. Send the request. - **Non-custodial by construction.** The API hands back an unsigned transaction; your wallet signs it and hands the signed bytes back to be broadcast. We never see a key and never touch funds, and you never need an RPC. - **Fees inside the quote.** The launchpad's pool fee, the creator's tax and the snipe tax on early buys are all applied by the hook, and the quote runs through that same hook. `expectedOut` is what arrives. - **Fast.** One round trip to the chain for everything a trade needs, quote and gas estimate included, then the transaction is built in memory. Tens of milliseconds on our side. - **Checked before you sign.** Funds, balances, approvals and slippage are verified up front, so what you get back lands. - **Scripts, not SDKs.** Each page carries a complete Node.js and Python script. Edit the constants at the top and run it; there is nothing to install beyond ethers or web3. - **FREE live feed.** Every Argus and LIFT launch over WebSocket, within a second of the block, and a keyed stream of every trade on the tokens you follow. - **Built for bots.** Stable error codes, consistent nonces, USDC-denominated amounts. ``` Base URL: https://api.shrine.trade/arc ``` ## Endpoints | Endpoint | What it does | |---|---| | `POST /api/local-trade` | Buy or sell any Argus or LIFT token → [Buy & Sell](/local-trade) | | `POST /api/send` | Broadcast a transaction you signed, no RPC of your own needed → [Sending](/local-trade#sending) | | `WS /api/launches/ws` | Live feed of every Argus and LIFT launch → [Live Launches](/live-launches) | | `WS /api/stream/ws` | Every buy, sell and liquidity change for the tokens you name → [Advanced Data Stream](/advanced-stream) | | `GET /api/token/{address}` | Which launchpad, which pool, fees, snipe tax, price, your balance → [Token Info](/token-info) | Token creation is next; it is not in this release. ## Questions? Read the [FAQ](/faq) for supported venues, fees, amounts, slippage, approvals and private-key safety. ## Stay updated Releases are announced on [X, @shrinetrade](https://x.com/shrinetrade). For help, the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) is where the team answers. If you build with an AI coding tool, point it at [llms-full.txt](/llms-full.txt): the whole of this site as one plain-text file. *shrine.trade is an Argus and LIFT API for Arc developers and traders. It is an independent integration, not affiliated with Argus, LIFT, Circle or Uniswap; the launchpads' contracts and their fees belong to them. Built by traders, for traders.* --- # Argus & LIFT Trade Stream: Every Buy, Sell and Liquidity Change on Arc > WebSocket stream of every buy, sell, liquidity add and liquidity removal for the Argus and LIFT tokens you choose, in their Uniswap v4 or v3 pool on Arc. Free key, live. # Advanced Data Stream Every trade and every liquidity change for the tokens you name, pushed as they land. One socket covers the whole life of a token, because on Arc a token has one venue for good: - **Argus and LIFT v2 tokens**: each swap in the token's Uniswap v4 pool, and each liquidity position opened or closed. - **LIFT v1 tokens and plain listings**: the same for their Uniswap v3 pool. Use it for a strategy, a wallet tracker, volume and holder analytics, or a bot that reacts to what others do instead of only to launches. The [launch feed](/live-launches) tells you a token exists; this tells you what happens to it. > **Free key, by request.** This stream needs a free api key. Join the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk) to request one. ``` WS wss://api.shrine.trade/arc/api/stream/ws?key=YOUR_KEY&tokens=0x…,0x… WS wss://api.shrine.trade/arc/api/stream/ws?key=YOUR_KEY&wallets=0x…,0x… ``` Follow **tokens** to get everything that happens to them, **wallets** to get everything they do on any token, or both on one socket. ## Copy this **JavaScript** ```js const KEY = "sk_…"; // your free api key const TOKENS = ["0xb242508C29959aA165379BCfdb576070B24eE5A8"]; // any Argus or LIFT tokens const ws = new WebSocket(`wss://api.shrine.trade/arc/api/stream/ws?key=${KEY}&tokens=${TOKENS.join(",")}`); ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.type === "websocket_active") { console.log("following", m.tokens.length, "tokens from block", m.block); return; } if (m.type === "trade") console.log(`${m.side.toUpperCase()} ${m.tokenAmountFormatted} ${m.symbol} for ${m.quoteAmountFormatted} USDC on ${m.venue} by ${m.origin}`); if (m.type === "liquidity") console.log(`liquidity ${m.action} ${m.symbol} on ${m.venue}: ${m.liquidityDelta}`); if (m.type === "lagged") console.warn("read too slowly, missed", m.dropped, "events"); }; // Change what you follow without reconnecting. // ws.send(JSON.stringify({ subscribe: ["0x…"], unsubscribe: ["0x…"] })); ``` Node 22+ has `WebSocket` built in; older Node needs `npm install ws`. **Python** ```python import json, websocket # pip install websocket-client KEY = "sk_…" # your free api key TOKENS = ["0xb242508C29959aA165379BCfdb576070B24eE5A8"] # any Argus or LIFT tokens def on_message(ws, raw): m = json.loads(raw) if m["type"] == "websocket_active": print("following", len(m["tokens"]), "tokens from block", m["block"]) elif m["type"] == "trade": print(f'{m["side"].upper()} {m["tokenAmountFormatted"]} {m["symbol"]} for {m["quoteAmountFormatted"]} USDC on {m["venue"]} by {m.get("origin")}') elif m["type"] == "liquidity": print(f'liquidity {m["action"]} {m["symbol"]} on {m["venue"]}: {m["liquidityDelta"]}') elif m["type"] == "lagged": print("read too slowly, missed", m["dropped"], "events") url = f"wss://api.shrine.trade/arc/api/stream/ws?key={KEY}&tokens={','.join(TOKENS)}" websocket.WebSocketApp(url, on_message=on_message).run_forever() ``` ## What you get On connect, one `websocket_active` frame confirming what is being followed: ```json { "type": "websocket_active", "stream": "advanced", "tokens": ["0xb242508c29959Aa165379BCfdB576070b24ee5a8"], "wallets": [], "unknown": [], "maxTokens": 50, "maxWallets": 50, "block": 22255848, "upstream": "open" } ``` `unknown` lists anything you asked for that the stream can't follow, with a reason: an address that is not an Argus or LIFT token and has no Uniswap pool against USDC, or not an address at all. The rest of the connection is events. `upstream` is `open` when logs are flowing from the node and `connecting` while the subscription is being set up, which the first client to connect triggers; it takes a second or two. If a token you follow stays silent, check this before assuming the token is quiet. ### Trades ```json { "type": "trade", "venue": "uniswap_v4", "side": "buy", "token": "0xb242508c29959Aa165379BCfdB576070b24ee5a8", "symbol": "XEUS", "protocol": "ARGUS", "quoteToken": "0x3600000000000000000000000000000000000000", "quoteSymbol": "USDC", "origin": "0x702468CB17E0b46184281dfc8185BA19a43dF75C", "caller": "0x4fcA4a51Ab4F23A7447b3284fBd7D73289A89Fb1", "tokenAmount": "191577422625225790682028", "tokenAmountFormatted": "191577.422625225790682028", "quoteAmount": "495000", "quoteAmountFormatted": "0.495", "price": "0.000002583812", "sqrtPriceX96": "127416838254128394112", "tick": -406012, "tokenReserve": "980245211035624581738236", "tokenReserveFormatted": "980245.211035624581738236", "quoteReserve": "2533261", "quoteReserveFormatted": "2.533261", "reservesSource": "pool_virtual", "priceAfter": "0.000002584311", "marketCap": "2584.311", "block": 22255841, "timestamp": 1790120094, "tx": "0xafd16e429bd6cb0cbb401359740491a98c4acff6bb46c7a815ebf9d8fcecffd4", "logIndex": 5 } ``` | Field | Meaning | |---|---| | `venue` | `uniswap_v4` (Argus, LIFT v2, plain v4 pools) or `uniswap_v3` (LIFT v1, plain v3 pools). | | `side` | `buy`: USDC went in and tokens came out. `sell`: the reverse. | | `protocol` | `ARGUS`, `LIFT` or `UNISWAP`. | | `quoteToken` / `quoteSymbol` | Always USDC on Arc, 6 decimals. Amounts are in it. | | `origin` | The wallet that sent the transaction. Most trades go through a router, ours or Uniswap's, so `caller` is a contract and `origin` is the person. Watch wallets by this. Missing on the rare event where the node didn't return the transaction in time. | | `caller` | Whoever called the pool: a router, or the wallet itself on a direct trade. | | `tokenAmount` / `quoteAmount` | Base units, plus a `…Formatted` decimal for each. The quote amount is what the pool saw, after the hook's fees. | | `price` | USDC per token, from this trade's own two amounts. | | `priceAfter` | USDC per token once this trade is done, from the pool's price. | | `marketCap` | `priceAfter` times the token's total supply, in USDC. | | `tokenReserve` / `quoteReserve` | The active range's virtual reserves after the trade, derived from the swap's liquidity and price, what the next trade is priced against. `reservesSource` is always `pool_virtual` on Arc. | | `sqrtPriceX96` / `tick` | The pool's price after the swap, in Uniswap's own units. | | `block` / `timestamp` / `tx` / `logIndex` | Where it happened. `block` and `logIndex` together order events exactly. | ### Liquidity ```json { "type": "liquidity", "venue": "uniswap_v4", "action": "add", "token": "0x…", "symbol": "XEUS", "protocol": "ARGUS", "quoteToken": "0x3600000000000000000000000000000000000000", "quoteSymbol": "USDC", "origin": "0x…", "caller": "0x…", "liquidityDelta": "184467440737095516", "tickLower": -887200, "tickUpper": 405400, "block": 22255000, "timestamp": 1790119000, "tx": "0x…", "logIndex": 3 } ``` | Field | Meaning | |---|---| | `action` | `add` or `remove`. | | `liquidityDelta` | Liquidity units added (positive) or removed (negative), in Uniswap's own measure. | | `tokenAmount` / `quoteAmount` | V3 pools report how much of each asset moved; v4 pools report only the liquidity delta, so these are absent there. | | `tickLower` / `tickUpper` | The position's price range. On a launchpad pool the one position is the launch itself, locked for good. | ### Following wallets Pass `wallets=` in the URL, or send `subscribeWallets`, and every trade or liquidity change those wallets **send** comes through, whatever token it's on, with the token attributed. The match is on `origin` (the transaction sender) or `caller`, so a wallet trading through a router is still caught. Use it for copy-trading, whale watching, or following a creator's own wallet. One limit: a wallet's swap in a pool nobody follows can only be attributed if the stream has seen that pool open since it started. If you care about one token, follow the token as well. ### Control messages Send JSON text frames to change what you follow; the reply is a `subscribed` message with the full current lists: ```json { "subscribe": ["0x…", "0x…"], "unsubscribe": ["0x…"] } { "subscribeWallets": ["0x…"], "unsubscribeWallets": ["0x…"] } { "ping": true } ``` Add `&pretty=1` to the URL to have every frame indented and newline-terminated, handy for watching a tape with curl. `{"type":"lagged","dropped":n}` means your client read too slowly and `n` events were skipped rather than queued without bound. Process faster or follow fewer tokens. ## Notes - **Filtering is required.** You name up to 50 tokens and 50 wallets per connection, and only their events come through. There is no "everything" mode. - **Live.** Events are pushed from a node subscription and arrive within about a second of the block, same as the [launch feed](/live-launches). - **Argus, LIFT and plain Uniswap tokens.** Anything the [trade endpoint](/local-trade) can trade, the stream can follow. A token priced in something other than USDC is reported in `unknown`. - **Limits.** 50 tokens and 50 wallets per connection, one connection per key and per IP. Follow more on the one socket rather than opening a second; ask if you need more. --- # Argus & LIFT Trading API Errors > Every error code the Argus & LIFT Trading API returns, grouped by endpoint, with what to do about each. # Errors Every failure is a JSON object carrying a machine-readable `error` code, which never changes meaning, and a `message` written for people. Branch on the code; log or display the message. ```json { "error": "insufficient_balance", "message": "wallet holds 0 tokens" } ``` ## Your request | Code | HTTP | Meaning | |---|---|---| | `invalid_request` | 400 | Something required is absent, or a field has the wrong shape. | | `invalid_address` | 400 | An address field (`token`, `from`, `wallet`) does not parse as one. | | `invalid_amount` | 400 | `amount` is zero, negative or not numeric, or a percentage was given on a buy. | | `invalid_action` | 400 | Only `"buy"` and `"sell"` are accepted. | | `token_not_found` | 404 | The address is not an Argus or LIFT token, and no Uniswap pool on Arc holds it against USDC. Check you copied the token, not a pool. | ## Trading | Code | HTTP | Meaning | |---|---|---| | `insufficient_funds` | 400 | Not enough USDC in `from` for the trade plus gas. The message states the balance and the requirement side by side. It has to be **USDC on Arc**: USDC on Ethereum, Base or Solana does not count until it is bridged. | | `insufficient_balance` | 400 | Selling more tokens than the wallet holds. `"100%"` sells everything. | | `slippage_exceeded` | 400 | Between quoting and building, the price moved past what `slippage` allows. Re-request; on a volatile token, allow more. | | `amount_too_small` | 400 | So little that the swap would return nothing. Increase `amount`. | | `deadline_passed` | 400 | The transaction's deadline expired before it was sent. Request again and send promptly. | ## Sending | Code | HTTP | Meaning | |---|---|---| | `not_ours` | 400 | `/api/send` only broadcasts transactions this API built: Universal Router swaps, and the one-time ERC-20 and Permit2 approvals in front of them. Anything else is refused before it reaches the chain. | | `wrong_chain` | 400 | The signed transaction is for another chain id; this API relays Arc (5042). | ## Limits | Code | HTTP | Meaning | |---|---|---| | `rate_limited` | 429 | Over 3 calls a second to a single endpoint from one IP. `Retry-After: 1` says when to try again. | | `too_many_connections` | 429 | One launch-feed socket and one advanced-stream socket per IP (and one per stream key) is the cap; this would have been the second. | | `unauthorized` | 401 | The advanced stream was opened without a key, or with an unknown or revoked one. Keys are free, ask in the Telegram group. | | `too_many_tokens` / `too_many_wallets` | 400 | A stream connection may follow at most 50 tokens and 50 wallets. | ## Upstream | Code | HTTP | Meaning | |---|---|---| | `rpc_error` | 502 | The chain could not be read or the transaction could not be sent. The message carries the node's own text. Retry; if it persists, ask in the Telegram group. | --- # Argus & LIFT Trading API FAQ > Answers about supported launchpads, USDC amounts, fees, snipe tax, approvals, private-key safety, slippage and rate limits for the Argus & LIFT Trading API on Arc. # Frequently asked questions ### Which platforms are supported? Every token launched on Argus and on LIFT, the two launchpads on Arc, in the Uniswap pool it opened in. Argus tokens trade in Uniswap v4 pools with the Argus hook; LIFT tokens in Uniswap v4 pools with the LIFT hook, or in Uniswap v3 pools for LIFT's earlier launches. Any other Uniswap pool on Arc priced in USDC works too. You never say which; the API reads the token and picks the venue. ### What is Arc? Circle's blockchain, live since September 2026. Its native token is USDC, so gas is paid in USDC and prices are in USDC. Blocks are about half a second apart, and transactions are ordered by priority fee, like Ethereum. ### Do I need an API key or an account? No. Every endpoint is open. Limits per IP address: 3 requests a second on any one endpoint, one open launch-feed socket, and one advanced-stream socket. The advanced stream needs a free key, so every firehose connection has a name attached. ### Is my private key safe? It never leaves your machine. Every response is an unsigned transaction. Signing happens in your script, with your key; only the signed bytes are posted to `/api/send`, which broadcasts them and can do nothing else with them. Nothing about the key ever reaches shrine.trade, and there is no custody of any kind. ### What does it cost? A 0.75% fee on buys and on sells, taken in USDC inside the swap. Token info costs nothing. The launchpad's pool fee, the token's creator tax and, on early buys, the snipe tax apply on top and are all inside the quote. Gas is a fraction of a cent. See [Fees](/fees). ### Which currency is `amount` in? On a buy, USDC: `"5"` spends 5 USDC. On a sell, tokens, or a percentage of your balance like `"50%"`; proceeds come back as USDC. ### Why does my first buy come back as three transactions? The swap pulls your USDC through Permit2, Uniswap's shared approval contract. The first time a wallet trades, two one-time approvals go in front of the swap: USDC to Permit2, then Permit2 to the router. After that a buy is one transaction. The first sell of a token needs the same pair for that token. The scripts send whatever comes back, in order, so nothing changes for you. ### What is the snipe tax? Both launchpads charge a steep extra fee on buys in the first moments after a launch, up to 99%, falling to zero within seconds on Argus and within 6 blocks on LIFT. It is inside the quote, so a buy during that window shows a much smaller `expectedOut`. [Token Info](/token-info) reports the current `snipeTaxBps`; a sniper polls it and buys when it reads 0. ### What is the creator tax? A percentage of every buy and sell that the launchpad's hook pays to the token's creator, set at launch. `creatorTaxBps` in the quote is the rate on your side of the trade; `buyTaxBps` and `sellTaxBps` on Token Info show both. ### Are trades front-run protected? The on-chain minimum (`minOut`, from your slippage) is enforced by the Universal Router, so a trade that would fill worse than quoted fails instead of filling badly. Arc has a mempool, but public nodes do not gossip transactions to outsiders; signed transactions go through `/api/send`, which sends them over our own endpoint rather than a public node. ### How does slippage work here? `slippage` is the percentage the fill may fall short of the quote before the transaction refuses to execute; the API turns it into an on-chain `minOut`. The default of 5 suits most tokens. A busy launch may need more, a quiet token can take less. `slippage_exceeded` means the floor was hit. ### The wallet holds enough for the trade, so why `insufficient_funds`? A node sets aside `gasLimit x maxFeePerGas` before it executes anything, and only refunds the unused part afterwards. On Arc that reservation is in USDC, the same balance the trade spends from. The API applies the same check up front and states the exact figure required. Keep a dollar of USDC extra and this never comes up. ### Can I be faster than other bots? Arc orders transactions by priority fee. The API builds each transaction with a modest tip; a bot that wants the front of the block raises `maxPriorityFeePerGas` before signing. The rest is latency: the closer your sender is to Arc's validators, the earlier your transaction arrives. ### Is there a live feed of launches? Yes. `wss://api.shrine.trade/arc/api/launches/ws` streams every Argus and LIFT launch and every new Uniswap v4 pool against USDC, free, no key, one connection per IP. It is a stream, not a database: no history endpoint exists. See [Live Launches](/live-launches). ### Can I follow trades on a token? Yes, on the [Advanced Data Stream](/advanced-stream): every buy, sell and liquidity change for up to 50 tokens or 50 wallets per connection, pushed within a second of the block. It needs a free key, handed out in the Telegram group. ### Can AI coding tools read these docs? Yes. Two files are kept for that: [llms.txt](/llms.txt), an index with one line per page, and [llms-full.txt](/llms-full.txt), the entire site as plain text. Both are rebuilt with every release. ### Where do I get help? Ask in the [Telegram group](https://t.me/+nEqAowTK8BZhZjFk). New versions are posted on [X](https://x.com/shrinetrade). --- # Argus & LIFT Trading API Fees > shrine.trade charges 0.75% per trade on Argus and LIFT tokens on Arc, taken in USDC inside the swap; reads are free. The launchpad's pool fee, creator tax and snipe tax apply on top. # Fees ## shrine.trade | Action | Fee | |---|---| | Buy | **0.75%** | | Sell | **0.75%** | | Token info | Free | ## Lower fees **The 0.75% is negotiable.** Trading real size, building a bot or app on top of this, or need an endpoint that isn't here yet? Message me on [Telegram](https://t.me/+nEqAowTK8BZhZjFk) and we'll sort out a rate that works. ## The launchpads' own fees Both launchpads charge inside the pool, through a Uniswap v4 hook. The quote runs through the hook, so these are inside `expectedOut` too. | | Argus | LIFT | |---|---|---| | Pool fee | 1% (Uniswap LP tier) | 1% base fee (v2 pools); 1% LP tier (v3 pools) | | Creator tax | 1% to 10% per side, set at launch, fixed forever | 0% to 10% per side, set at launch | | Snipe tax | Up to 99% on buys in the first seconds after launch, decaying to 0 | Up to 99% on buys in the first 6 blocks, decaying to 0 | | Who gets it | 90% creator, 10% Argus | 70% creator, 30% LIFT | `poolFeeBps`, `creatorTaxBps` and `snipeTaxBps` in the quote show the rates that applied to your side of the trade. [Token Info](/token-info) shows them before you trade, so a sniper can wait for `snipeTaxBps` to reach 0. ## Gas Gas on Arc is paid in USDC, the chain's native token, and is cheap: the base fee floor is 20 gwei, which makes a swap of about 250,000 gas cost around half a cent. The transactions the API builds carry a small priority fee so they are not last in the block; a bot that must be first can raise `maxPriorityFeePerGas` before signing, since Arc orders transactions by priority fee. A wallet needs its USDC to cover the trade plus the gas reservation, `gasLimit` times `maxFeePerGas`. The unused part of the reservation comes back after the transaction; the API's `insufficient_funds` message states the exact figure required. --- # Live Argus and LIFT Launches on Arc: New Token Feed > WebSocket feed of every new Argus and LIFT launch on Arc, plus every plain Uniswap v4 pool opened against USDC, the moment it happens. Free and keyless. # Live Launches Every new token on Arc, pushed the moment it lands. Three things arrive on this one socket: - **Argus launches**, with the creator tax and the image the creator pinned. - **LIFT launches**, with the metadata URI and the creator's first buy. - **New Uniswap v4 pools** against USDC opened by anyone, without a launchpad. Free, no key, no account. ``` wss://api.shrine.trade/arc/api/launches/ws ``` ## Copy this Click Argus or LIFT to choose what the script receives; the URL updates. **JavaScript** (Node 22+, no packages): ```js // Add ?protocols=ARGUS or ?protocols=LIFT to narrow it. No query = everything, // new_pool events for plain Uniswap pools included. const ws = new WebSocket("wss://api.shrine.trade/arc/api/launches/ws"); ws.onmessage = (e) => { const t = JSON.parse(e.data); // t.type: new_launch or new_pool; t.protocol: ARGUS, LIFT or UNISWAP console.log(JSON.stringify(t, null, 2)); }; ``` **Python** (`pip install websockets`): ```python import json from websockets.sync.client import connect with connect("wss://api.shrine.trade/arc/api/launches/ws") as ws: for raw in ws: t = json.loads(raw) print(json.dumps(t, indent=2)) ``` ## What you get On connect you're sent the **single most recent event** so the screen isn't blank, then everything new as it happens. Each message has a `type` and a `protocol`: | `type` | `protocol` | Means | |---|---|---| | `new_launch` | `ARGUS` | A token launched on Argus. Its Uniswap v4 pool exists and trades from this block on. | | `new_launch` | `LIFT` | A token launched on LIFT, into a Uniswap v4 pool (v2 launches) or a Uniswap v3 pool (v1). | | `new_pool` | `UNISWAP` | A hookless Uniswap v4 pool against USDC was opened. | Filter with `?protocols=ARGUS`, `?protocols=LIFT` or `?protocols=UNISWAP`, comma-separated for several, or omit it for everything. Fields that don't apply to an event are simply absent, so check `type` and `protocol` first. A real Argus launch: ```json { "type": "new_launch", "protocol": "ARGUS", "token": "0x78cb16BCec098946CF25190c11DFB511550b9215", "name": "Niu LAI", "symbol": "牛来", "blockNumber": 22255452, "txHash": "0x9cc33d95f2c338c1c17189d83914923faee5bc518d550cbe44331564581c29e6", "timestamp": 1790119898, "creator": "0x1c4ABb26936c050A864E23017881e588DDB4E9F4", "quoteToken": "0x3600000000000000000000000000000000000000", "venue": "uniswap_v4", "pool": "0xa96421b2d30902ad9e1ac60230e2a7545c29e4329675e8a51687510f6f96a3ac", "hook": "0x478d6B947398Cc74Ef3B7c4D6BCd2d12f471e044", "buyTaxBps": 300, "sellTaxBps": 1000, "imageUri": "ipfs://bafkrei…", "fee": 10000, "tickSpacing": 200 } ``` | Field | Meaning | |---|---| | `token` / `name` / `symbol` / `creator` | The token and who launched it. Name and symbol ride on the launch event itself, so there is nothing extra to fetch. | | `venue` / `pool` | Where it trades from now on: `uniswap_v4` with the pool id, or `uniswap_v3` with the pool address. This never changes for a token. | | `hook` | The launchpad's fee hook on the pool. | | `buyTaxBps` / `sellTaxBps` | Argus: the creator tax per side, fixed at launch. | | `imageUri` / `website` / `twitter` / `telegram` | Argus: what the creator filled in, when they did. | | `metaUri` / `initialBuy` | LIFT: the metadata JSON the creator pinned, and their own buy in the launch transaction, USDC base units. | | `fee` / `tickSpacing` | The pool's tier. | > **Don't buy the instant you see a launch.** Both launchpads charge a snipe tax on buys in the first moments after a launch, up to **99%**, decaying to zero within seconds on Argus and within 6 blocks on LIFT. Buying the moment a launch appears in this feed hands most of your order to the tax. Poll `snipeTaxBps` on [Token Info](/token-info) and buy when it reads 0. ## Notes - **Reconnect on drop.** Long-lived sockets die; reconnect and you're re-seeded with the latest event. - **One subscription per IP.** A second launch-feed connection from the same address is refused with `too_many_connections`. One socket with no filter carries everything; the [data stream](/advanced-stream) is a separate kind and can be open alongside it. - **Nothing is stored.** This is a live feed, not an archive: no history endpoint and no backfill. LIFT's own API lists past launches; Argus tokens can be read from the Argus factory on chain. --- # Buy & Sell Argus and LIFT Tokens on Arc (ArgusPad, lift.fun) > Buy or sell any Argus or LIFT token on Arc with one script, in the Uniswap pool it launched into. Pay in USDC; the launchpad's fees, creator tax and snipe tax are already inside the quote. # Buy & Sell One request, unsigned transactions back, signed and sent by your own script. The snippet below is complete: set the key, the token and the amount, and run it. One endpoint for every Argus and LIFT token on Arc, wherever it lives: - **Argus** tokens, in their Uniswap v4 pool with the Argus fee hook. - **LIFT** tokens, in their Uniswap v4 pool with the LIFT hook. LIFT's earlier v1 launches sit in plain Uniswap v3 pools and report as `UNISWAP`. - **Any other Uniswap pool** on Arc priced in USDC. You send the token address; the API finds the pool and routes accordingly, reporting `protocol` as `ARGUS`, `LIFT` or `UNISWAP` and `venue` as `uniswap_v4` or `uniswap_v3`. There is no bonding curve and no graduation on Arc: a token trades in the same pool from its first block on. ## What you need - **Node.js** or **Python**. - A wallet private key with some **USDC on Arc**. USDC is the chain's native token: it pays for the trade and for gas. - A token address. Take one from [argus.world](https://argus.world) or [lift.fun](https://lift.fun). ## Example **JavaScript** ```js const { Wallet } = require("ethers"); // ─── change these three ─────────────────────────────── const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"; const TOKEN = "0xTHE_TOKEN_ADDRESS"; const AMOUNT = "5"; // to buy: USDC to spend. To sell: "1000" tokens, or "100%" const ACTION = "buy"; // "buy" or "sell" const SLIPPAGE = 5; // %, how far the price may move against you // ────────────────────────────────────────────────────── async function main() { const wallet = new Wallet(PRIVATE_KEY); // signs only; no node needed const res = await fetch("https://api.shrine.trade/arc/api/local-trade", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ action: ACTION, token: TOKEN, amount: AMOUNT, slippage: SLIPPAGE, from: wallet.address, }), }); const data = await res.json(); if (data.error) throw new Error(`${data.error}: ${data.message}`); console.log("you should get about", data.quote.expectedOutFormatted, ACTION === "buy" ? "tokens" : "USDC"); // Sign locally and hand the signed bytes to the API to broadcast - the key // never leaves this script, and you need no RPC of your own. The first // trade of a token carries one-time approvals in front of the swap. for (const tx of data.txs) { const signed = await wallet.signTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas), maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas), nonce: tx.nonce, chainId: tx.chainId, type: 2, }); const sent = await (await fetch("https://api.shrine.trade/arc/api/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ signedTx: signed }), })).json(); if (sent.error) throw new Error(`${sent.error}: ${sent.message}`); if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`); console.log(tx.description + ": " + sent.explorer); } if (ACTION === "buy") { console.log(`bought ${data.name} (${data.symbol}) ${data.token} for ${data.amountInFormatted} USDC on ${data.protocol}`); } else { console.log(`sold ${data.amountInFormatted} ${data.name} (${data.symbol}) ${data.token} for ${data.quote.expectedOutFormatted} USDC`); } } main(); ``` Save it as `trade.js`, then: ```bash npm install ethers node trade.js ``` **Python** ```python import requests from eth_account import Account # ─── change these three ─────────────────────────────── PRIVATE_KEY = "0xYOUR_PRIVATE_KEY" TOKEN = "0xTHE_TOKEN_ADDRESS" AMOUNT = "5" # to buy: USDC to spend. To sell: "1000" tokens, or "100%" ACTION = "buy" # "buy" or "sell" SLIPPAGE = 5 # %, how far the price may move against you # ────────────────────────────────────────────────────── account = Account.from_key(PRIVATE_KEY) # signs only; no node needed res = requests.post( "https://api.shrine.trade/arc/api/local-trade", json={"action": ACTION, "token": TOKEN, "amount": AMOUNT, "slippage": SLIPPAGE, "from": account.address}, ) data = res.json() if "error" in data: raise SystemExit(f"{data['error']}: {data['message']}") unit = "tokens" if ACTION == "buy" else "USDC" print("you should get about", data["quote"]["expectedOutFormatted"], unit) # The first trade of a token carries one-time approvals in front of the swap; # send whatever comes back, in order. for tx in data["txs"]: signed = Account.sign_transaction( { "to": tx["to"], "data": tx["data"], "value": int(tx["value"]), "gas": tx["gas"], "maxFeePerGas": int(tx["maxFeePerGas"]), "maxPriorityFeePerGas": int(tx["maxPriorityFeePerGas"]), "nonce": tx["nonce"], "chainId": tx["chainId"], }, account.key, ) sent = requests.post("https://api.shrine.trade/arc/api/send", json={"signedTx": "0x" + signed.raw_transaction.hex().removeprefix("0x")}).json() if "error" in sent: raise SystemExit(f"{sent['error']}: {sent['message']}") if sent["status"] != "landed": raise SystemExit(f"{tx['description']} {sent['status']}: {sent['explorer']}") print(tx["description"] + ":", sent["explorer"]) if ACTION == "buy": print(f"bought {data['name']} ({data['symbol']}) {data['token']} for {data['amountInFormatted']} USDC on {data['protocol']}") else: print(f"sold {data['amountInFormatted']} {data['name']} ({data['symbol']}) {data['token']} for {data['quote']['expectedOutFormatted']} USDC") ``` Save it as `trade.py`, then: ```bash pip install eth-account requests python trade.py ``` Whatever the API returns, the script sends in sequence. After the first trade, a buy or a sell is a single transaction. Trades are front-run protected: the on-chain `minOut` guarantees the fill is never worse than your slippage allows, and the signed transaction is broadcast through our own endpoint via `/api/send`, not a public node, so you need no RPC of your own. > **One-time approvals.** Every pool on Arc is priced in USDC, and the swap pulls your USDC through Permit2, Uniswap's shared approval contract. The first time a wallet buys, two approvals come back ahead of the swap: `approve_quote_permit2` (USDC to Permit2, once ever) and `approve_quote_router` (Permit2 to the router, once a year). Every later buy is one transaction. The first sell of a token needs the same pair for that token, `approve_permit2` and `approve_router`. The scripts above handle all of it. > **Snipe tax on fresh launches.** Both launchpads charge a steep extra fee on buys in the first moments after a launch, up to 99%, decaying to zero within seconds on Argus and within 6 blocks on LIFT. It is inside the quote, so a buy during that window shows a much smaller `expectedOut`. `snipeTaxBps` in the response is the rate that applied; [Token Info](/token-info) shows it before you trade, so a bot can wait for 0. ## Selling Switch `ACTION` to `"sell"`. For the amount, give either a token count or a percentage of the wallet's balance: | `AMOUNT` | Sells | |---|---| | `"1000"` | 1000 tokens | | `"50%"` | half your balance | | `"100%"` | everything | Proceeds arrive in USDC. ## Creator tax Most launches carry a creator tax: the launchpad's hook pays a percentage of every buy and sell to the token's creator, fixed at launch. The tax is applied inside the quote, so `expectedOut` is what you actually receive. `creatorTaxBps` in the response is the rate on your side of the trade. Nothing changes in the script. ## If it doesn't work | Error | What to do | |---|---| | `insufficient_funds` | Add USDC to the wallet, on Arc: the amount plus a little for gas. | | `insufficient_balance` | The sell is bigger than the balance. `"100%"` always fits. | | `token_not_found` | Not an Argus or LIFT token, and no Uniswap pool holds it against USDC. Make sure it is the token contract, not the pool. | | `slippage_exceeded` | The price ran away between quote and send. Retry, with a higher `SLIPPAGE` on a busy token. | | `amount_too_small` | The swap would return nothing. Increase `AMOUNT`. | Full list on [Errors](/errors). ## Sending `POST https://api.shrine.trade/arc/api/send` takes one signed transaction and broadcasts it through our Arc endpoint, so the scripts need no RPC at all. It only forwards transactions this API built, swaps and their approvals, and refuses anything else with `not_ours`. | Field | | | |---|---|---| | `signedTx` | 0x-hex | The signed transaction, as `wallet.signTransaction` returns it. | | `wait` | boolean, optional | Default `true`: wait for the receipt. `false` returns the hash as soon as the node accepts it. | ```json { "hash": "0x…", "status": "landed", "blockNumber": 22171031, "gasUsed": 214058, "effectiveGasPrice": "20300000000", "explorer": "https://explorer.arc.io/tx/0x…" } ``` `status` is `landed`, `reverted`, or `pending` (not waited for, or not mined within 45 seconds, stuck on nonce or gas). ### Request and response `POST https://api.shrine.trade/arc/api/local-trade` **Request** | Field | | | |---|---|---| | `action` | `"buy"` or `"sell"` | | | `token` | address | The token. | | `amount` | decimal string | On a buy, the USDC to spend, such as `"5"`. On a sell, a token count such as `"1000"`, or a percentage of the balance such as `"50%"` or `"100%"`. | | `from` | address | Your wallet. | | `slippage` | number, optional | In percent. `5` when omitted. | **Response**, a real one: a buy of an Argus token for 5 USDC from a wallet that has never traded, so the two one-time approvals ride in front of the swap: ```json { "action": "buy", "protocol": "ARGUS", "token": "0xb242508c29959Aa165379BCfdB576070b24ee5a8", "name": "Xeusthegreat", "symbol": "XEUS", "venue": "uniswap_v4", "pool": "0x6299828f0bbf42c06d571279bf83d06d930bb5bc4a99b98793c6ff47141030b1", "amountIn": "5000000", "amountInFormatted": "5", "quoteAsset": { "address": "0x3600000000000000000000000000000000000000", "symbol": "USDC", "decimals": 6, "isNative": false }, "quote": { "expectedOut": "1917103258230312676855547", "expectedOutFormatted": "1917103.258230312676855547", "minOut": "1821248095318797043012770", "poolFeeBps": 100, "creatorTaxBps": 300, "snipeTaxBps": 0, "shrineFeeBps": 75, "slippagePct": 5.0 }, "txs": [ { "to": "0x3600000000000000000000000000000000000000", "data": "0x095ea7b3…", "value": "0", "gas": 80000, "maxFeePerGas": "27842282458", "maxPriorityFeePerGas": "2842282458", "nonce": 50, "chainId": 5042, "type": 2, "description": "approve_quote_permit2" }, { "to": "0x000000000022D473030F116dDEE9F6B43aC78BA3", "data": "0x87517c45…", "value": "0", "gas": 80000, "maxFeePerGas": "27842282458", "maxPriorityFeePerGas": "2842282458", "nonce": 51, "chainId": 5042, "type": 2, "description": "approve_quote_router" }, { "to": "0x4fcA4a51Ab4F23A7447b3284fBd7D73289A89Fb1", "data": "0x3593564c…", "value": "0", "gas": 600000, "maxFeePerGas": "27842282458", "maxPriorityFeePerGas": "2842282458", "nonce": 52, "chainId": 5042, "type": 2, "description": "buy" } ] } ``` - `txs`: the transactions to sign and broadcast, in this order. A swap that follows an approval in the same batch carries a fixed gas limit, since it cannot be estimated before the allowance exists; a swap on its own carries a real estimate plus 20%. The three wei-denominated fields (`value`, `maxFeePerGas`, `maxPriorityFeePerGas`) are decimal strings so they survive JSON; everything else is a plain number. `description` labels each: `buy`, `sell`, or the one-time approvals `approve_quote_permit2`, `approve_quote_router`, `approve_permit2`, `approve_router`. - `name` / `symbol`: read from the token contract, for logging what was traded. - `protocol` / `venue` / `pool`: which launchpad the token came from, and where it trades: `uniswap_v4` with the pool id, or `uniswap_v3` with the pool address. - `quoteAsset`: always USDC on Arc, 6 decimals. `amountIn` is in base units of it on a buy (5 USDC = `5000000`), tokens on a sell. - `quote.expectedOut`: the amount you should end up with once the pool fee, the creator tax, any snipe tax and our fee are all taken. `minOut` is that figure less your slippage, and it is the floor the router enforces. Both are base units; use the `*Formatted` twins for display. - `quote` fees: `poolFeeBps` is the pool's own fee, `creatorTaxBps` the token's tax on this side, `snipeTaxBps` the early-buy surcharge that applied (0 outside the launch window, `null` for plain Uniswap pools), `shrineFeeBps` ours (75 = 0.75%). --- # Argus & LIFT Token Info API: Price, Snipe Tax and Pool on Arc > Read any Argus or LIFT token on Arc: which launchpad and pool, pool fee, creator tax, the current snipe tax, price and market cap in USDC, and a wallet's balance. # Token Info Free, keyless reads. Use them to decide what to trade and when: a sniper polls `snipeTaxBps` and buys when it reads 0. Amounts are raw base-unit strings (6 decimals for USDC, the token's own decimals for tokens); fields ending in `Formatted` are the human-readable decimal. ## Token ``` GET https://api.shrine.trade/arc/api/token/{address} GET https://api.shrine.trade/arc/api/token/{address}?wallet=0x… ``` **JavaScript** ```js const TOKEN = "0xb242508C29959aA165379BCfdb576070B24eE5A8"; async function main() { const res = await fetch(`https://api.shrine.trade/arc/api/token/${TOKEN}`); const t = await res.json(); if (t.error) throw new Error(`${t.error}: ${t.message}`); console.log(t.symbol, "on", t.protocol, "| venue:", t.venue, "| price:", t.priceUsdc, "USDC"); console.log("market cap:", t.marketCapUsdc.toFixed(2), "USDC | tax:", t.buyTaxBps, "/", t.sellTaxBps, "bps | snipe tax now:", t.snipeTaxBps ?? "n/a", "bps"); } main(); ``` Save it as `token.js` and run `node token.js`. No packages needed. **Python** ```python import requests TOKEN = "0xb242508C29959aA165379BCfdb576070B24eE5A8" t = requests.get(f"https://api.shrine.trade/arc/api/token/{TOKEN}").json() if "error" in t: raise SystemExit(f"{t['error']}: {t['message']}") print(t["symbol"], "on", t["protocol"], "| venue:", t["venue"], "| price:", t["priceUsdc"], "USDC") print("market cap:", f"{t['marketCapUsdc']:.2f}", "USDC | tax:", t["buyTaxBps"], "/", t["sellTaxBps"], "bps | snipe tax now:", t.get("snipeTaxBps", "n/a"), "bps") ``` Save it as `token.py`, `pip install requests`, then `python token.py`. Add `?wallet=0x…` to get that wallet's token balance. A response for an Argus token: ```json { "protocol": "ARGUS", "token": "0xb242508c29959Aa165379BCfdB576070b24ee5a8", "name": "Xeusthegreat", "symbol": "XEUS", "decimals": 18, "venue": "uniswap_v4", "pool": "0x6299828f0bbf42c06d571279bf83d06d930bb5bc4a99b98793c6ff47141030b1", "hook": "0x7B734D70eFCD84ed8C36dF5Aa170e3ebF3322044", "creator": "0x04D704070Bcf21977dDd6457A365b8051699250f", "portal": "0xB021Be536808f551b31789422Fd28a6c9c6e97Da", "tickBond": 376400, "quoteAsset": { "address": "0x3600000000000000000000000000000000000000", "symbol": "USDC", "decimals": 6, "isNative": false }, "poolFeeBps": 100, "buyTaxBps": 300, "sellTaxBps": 300, "snipeTaxBps": 0, "totalFeeBuyBps": 400, "totalFeeSellBps": 400, "bonded": false, "launchedAt": 1790076636, "priceUsdc": 2.480877603500105e-06, "marketCapUsdc": 2480.877603500105, "totalSupply": "1000000000000000000000000000", "totalSupplyFormatted": "1000000000", "liquidity": "1575037795897358225", "walletBalance": "0", "walletBalanceFormatted": "0" } ``` | Field | Meaning | |---|---| | `protocol` | `ARGUS`, `LIFT` (v2 launches, Uniswap v4 with the LIFT hook), or `UNISWAP` for any other pool against USDC, which is also where LIFT's earlier v1 launches land: they are plain Uniswap v3 pools with nothing of LIFT's on them. | | `venue` / `pool` | `uniswap_v4` with the pool id, or `uniswap_v3` with the pool address. This is where a trade goes; it never changes for a token. | | `hook` | The launchpad's fee hook on the pool. Absent on v3 pools and plain Uniswap pools. | | `creator` / `portal` / `tickBond` | Argus only: the wallet that launched the token, the Argus factory it came from, and the tick at which the launch counts as bonded. | | `quoteAsset` | Always USDC on Arc, 6 decimals. | | `poolFeeBps` | The pool's own fee: 100 (1%) on Argus and on LIFT's v3 pools; LIFT's v4 pools carry 0 here and charge their 1% base fee through the hook instead, which `totalFee*Bps` includes. | | `buyTaxBps` / `sellTaxBps` | The creator tax on each side, fixed at launch. | | `totalFeeBuyBps` / `totalFeeSellBps` | Everything the hook takes on each side, pool fee and creator tax together, as the hook reports it. Absent where the hook does not expose it. | | `snipeTaxBps` | The extra tax a buy would pay **right now**. Up to 9900 in the first seconds after launch, 0 once the window has passed. Poll this before sniping. Absent on `UNISWAP` pools. | | `priceUsdc` / `marketCapUsdc` | Numbers, from the pool's current price: USDC per token, and that times the total supply. | | `liquidity` | The pool's current in-range liquidity, Uniswap's own unit. Larger is deeper. | | `bonded` / `launchedAt` | Argus only: whether the price has crossed the launch's bond tick (cosmetic, trading does not change), and the launch time as a Unix timestamp. | | `walletBalance` | The wallet's token balance, when `?wallet=` is given. | Responses are cached for one second per token and wallet.