Avantis Plugin
[!IMPORTANT] Complete the short Base MCP onboarding flow defined inAvantis is a perpetual futures DEX on Base mainnet (SKILL.mdbefore calling any Avantis endpoint. The user’s wallet address — used astraderin every tx-builder call — is fetched lazily when needed.
chainId 8453). The plugin returns unsigned call data; signing and broadcasting are the wallet’s job (Base MCP send_calls).
Surface routing
| Capability | Hosts | Where it runs |
|---|---|---|
| View-only reads — pair config, leverage envelopes, fees, open positions, limit orders, trade history, PnL, market-order settlement | data.avantisfi.com, core.avantisfi.com, api.avantisfi.com | Every surface. Use the harness HTTP tool when available; otherwise Base MCP web_request — these hosts are on the allowlist. |
| Transaction-builder — open/close trades, cancel orders, deposit/withdraw margin, set TP/SL, approve USDC, set/remove delegate | tx-builder.avantisfi.com | CLI harnesses (Claude Code, Codex, Cursor terminal). On chat-only surfaces (ChatGPT, Claude.ai), do not retry through web_request; link the user to the Avantis web UI — see Chat-only fallback. |
- Harness HTTP tool (
curl,fetch, shell) — works for every host, any method, no allowlist. - Base MCP
web_request— chat-only surfaces, view-only hosts only. - Avantis web UI — chat-only surfaces, tx-builder operations. See Chat-only fallback.
send_calls approval links is safe; the user approves any real transaction.
No API key or Authorization header is required for the documented public endpoints.
[!IMPORTANT] CORS caveat forweb_request. Most Avantis hosts returnAccess-Control-Allow-Origin: *, but two paths are not in the open-CORS prefix list:Base MCP
https://api.avantisfi.com/v2/history/referral/*https://api.avantisfi.com/v2/market-order-initiated/*web_requestis a server-side fetch and is not affected by browser CORS, so these still work fromweb_request. Only flag this if you ever proxy these requests from a browser context.
API Services
| Service | Base URL | Routing | Purpose |
|---|---|---|---|
| tx-builder | https://tx-builder.avantisfi.com | CLI; UI fallback on chat-only | GET-only ABI-encoder for Avantis Trading and USDC calls |
| data | https://data.avantisfi.com/v2/trading | CLI or web_request | Pair + group config, fees, leverage envelopes, open interest, Pyth feed metadata |
| core | https://core.avantisfi.com | CLI or web_request | Current open positions, open limit orders, per-pair open interest |
| history | https://api.avantisfi.com | CLI or web_request | Closed/all trade history, PnL aggregates, referral stats, market-order settlement status |
Base-Only Rules
- All tx-builder call data targets Base mainnet (
chainId8453). There is no chain selector. - Collateral is USDC only. ETH is used only for gas and the Avantis execution-fee
value. - Canonical Base USDC:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. - Default USDC spender is Avantis
TradingStorage:0x8a311D7048c35985aa31C131B9A13e03a5f7422d.
| Contract | Mainnet address |
|---|---|
Trading | 0x44914408af82bC9983bbb330e3578E1105e11d4e |
TradingStorage | 0x8a311D7048c35985aa31C131B9A13e03a5f7422d |
PairStorage | 0x5db3772136e5557EFE028Db05EE95C84D76faEC4 |
PairInfos | 0x81F22d0Cc22977c91bEfE648C9fddf1f2bd977e5 |
PriceAggregator | 0x64e2625621970F8cfA17B294670d61CB883dA511 |
USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
Multicall | 0xA7cFc43872F4D7B0E6141ee8c36f1F7FEe5d099e |
Referral | 0x1A110bBA13A1f16cCa4b79758BD39290f29De82D |
tx-builder Response Envelope
All calldata-producing tx-builder endpoints return:| Field | Notes |
|---|---|
to, data, value | Forwarded into Base MCP send_calls. value is 0x-prefixed wei; convert with BigInt(value) if you need a numeric. |
from | Who must sign. With &delegate=0x..., from is the delegate. |
chainId | Always 8453. |
nonce, gas | Never returned. The wallet manages them. |
meta | Endpoint-specific context. /trade/open includes a validation block; see Pre-Trade Validation. |
BAD_REQUEST, VALIDATION_ERROR (with details.fieldErrors), UPSTREAM_ERROR, NOT_FOUND, INTERNAL_ERROR.
send_calls payload:
Units And Scaling
| Surface | Unit behavior |
|---|---|
tx-builder request inputs (collateralUsdc, amountUsdc, openPrice, takeProfit, stopLoss, leverage, slippagePercent, executionFeeEth) | Human decimals, not raw scaled integers |
tx-builder response value | 0x-prefixed wei (ETH) |
data.avantisfi.com /v2/trading | Human decimals everywhere |
core /user-data positions[] and limitOrders[] | Raw stringified ints — USDC /1e6, prices / leverage / percent / slippage /1e10 |
api.avantisfi.com /v2/history/portfolio/* | Mixed; mostly human decimals — check each endpoint |
api.avantisfi.com /v2/history/referral/stats/* | USDC fields already divided by 1e6 |
1e6 USDC or 1e10 price units into tx-builder query parameters — they take human decimals.
Orchestration Pattern (open trade)
core /user-data first and use the real positions[i].index (positions) or limitOrders[i].index (resting orders) as tradeIndex. tx-builder will encode call data for an index that does not exist, and the call will then revert on chain.
Step 1 — Pair, Leverage, Liquidity (data API)
pairInfos["<pairIndex>"] to inspect a pair. Important fields:
| Field | Meaning |
|---|---|
index | Pair index used by tx-builder and on-chain calls |
from, to | Symbol components, e.g. BTC and USD |
isPairListed | Must be true to open new trades |
leverages.minLeverage, leverages.maxLeverage | Fixed-fee leverage envelope for market, limit, stop_limit |
leverages.pnlMinLeverage, leverages.pnlMaxLeverage | ZFP leverage envelope for market_zero_fee |
pairMinLevPosUSDC | Minimum notional: collateralUsdc × leverage |
pairOI, pairMaxOI | Pair open interest and cap (USDC, human decimals) |
groupIndex | Lookup key into groupInfo |
feed.attributes.is_open, feed.attributes.next_open, feed.attributes.next_close | Market open / schedule |
lazerFeed.state | stable → Lazer feed is available; use priceSourcing=1 (PYTH_LAZER) where needed |
1e6 / 1e10 math.
Market-open logic (for market orders):
- Open if
feed.attributes.is_open === true, ornow > feed.attributes.next_openandnow < feed.attributes.next_close. - Closed if
is_open === falseandnext_open > 0andnow < next_open.
positionSize must be <= available, otherwise tx-builder will reject with BAD_REQUEST (insufficient liquidity).
Minimum-position check (BELOW_MIN_POS):
~5 min TTL). If you call it directly from a hot path, cache locally too.
Step 2 — Positions and Limit Orders (core backend)
positions[] scaling:
| Field | Scaling | Use |
|---|---|---|
pairIndex | — | tx-builder pairIndex |
index | — | tx-builder tradeIndex |
buy | — | true = long, false = short |
collateral | / 1e6 | Use as collateralUsdc when closing full size |
leverage | / 1e10 | Display / validation |
openPrice, tp, sl, liquidationPrice | / 1e10 | Display / TP-SL decisions |
rolloverFee | / 1e6 | Accrued margin fee, USDC |
lossProtection | — | Tier integer (0 = none) |
openedAt | — | Unix seconds |
isPnl | — | true = ZFP trade, false = fixed-fee |
isOneCT | — | One-click-trading flag; ignore unless needed |
limitOrders[] adds:
| Field | Scaling | Meaning |
|---|---|---|
price | / 1e10 | Trigger price |
slippageP | / 1e10 | Slippage tolerance percent at execution |
block | — | Block number when the order was registered |
positionSize | / 1e6 | collateral × leverage (USDC) |
executionFee | / 1e6 | Currently 0 |
limitOrderType | — | 0 = LIMIT, others reserved |
{ positions: [], limitOrders: [] }, not an error. Treat empty as “no open state”, not “lookup failed”.
Other core endpoints (rarely needed by agents):
GET /open-interests— per-pairlongOI / shortOI / pendingLongOI / pendingShortOI(USDC, human decimals).GET /v2/open-interests— same plus per-market-makermmDatabreakdown.GET /user-data/config?wallet=0x...— one-click-trading feature flags. Not a trading-permission gate.GET /health— plain textOK.
HTTP 429 under load).
Step 3 — Approve USDC
Exact approval:spender defaults to TradingStorage. to is USDC; value is 0x0. Approval must be confirmed on chain before trade calls that require allowance can succeed, unless approval and action are submitted as a valid batch and the wallet/account contract supports the batch.
Step 4 — Open A Trade
Order types (and on-chain enum)
orderType (skill) | On-chain enum | Numeric | Notes |
|---|---|---|---|
market | MARKET | 0 | Fixed-fee path; openPrice auto-resolved if omitted |
stop_limit | REVERSAL | 1 | Requires openPrice |
limit | MOMENTUM | 2 | Requires openPrice |
market_zero_fee | MARKET_PNL | 3 | Zero-Fee Protocol (ZFP); uses pnlMinLeverage..pnlMaxLeverage |
stop_limit=1 and limit=2, not the other way round. Important when decoding logs.
value on the returned tx is the execution fee in wei.
ZFP example (small notional):
Pair separators
pair accepts /, -, or _. BTC/USD, btc-usd, eth_usd all resolve. Use &pairIndex= directly if you already have the integer.
Pre-Trade Validation
tx-builder enforces four checks on /trade/open before encoding. Each failure is 400 BAD_REQUEST with a human-readable message:
| Check | Source | Rejects when |
|---|---|---|
| Pair listed | data API | isPairListed === false |
| Minimum position | pair.pairMinLevPosUSDC | collateralUsdc × leverage < pairMinLevPosUSDC |
| Leverage envelope | leverages.* | leverage outside [minLeverage, maxLeverage] (fixed-fee) or [pnlMinLeverage, pnlMaxLeverage] (ZFP) |
| Liquidity | pairMaxOI − pairOI and groupMaxOI − groupOI | positionSize > min(pairAvail, groupAvail) |
meta.validation carries the computed envelope:
&skipValidation=true unless the user explicitly asks.
BELOW_MIN_POS recovery: compute minCollateral = ceil(pair.pairMinLevPosUSDC / leverage) and minLeverage = ceil(pair.pairMinLevPosUSDC / collateralUsdc), then present both options to the user (within the pair’s leverage envelope). Do not silently adjust parameters.
Step 5 — Close, Cancel, Margin, TP/SL
Always readcore /user-data first and use real indices from the returned arrays.
Close
value is the execution fee in wei.
Cancel resting limit / stop-limit
value is 0x0.
Deposit / withdraw margin
value is 0x1. When priceUpdateData is omitted, tx-builder fetches it from feed-v3.avantisfi.com and picks priceSourcing based on the pair’s Lazer status (lazerFeed.state === 'stable' → 1; otherwise 0). To run fully offline supply both priceUpdateData and priceSourcing.
Set / update TP and SL
value is 0x1. Same Pyth auto-fetch behavior as /margin/update.
To modify a resting limit order’s parameters, cancel it via /trade/cancel and create a new /trade/open with orderType=limit (or stop_limit).
Delegated Trading
from is the trader (only the trader can grant or revoke). value is 0x0. After a delegate is set, any trade-side endpoint accepts &delegate=<delegateAddress>; the response from becomes the delegate, the delegate signs, and the position still belongs to trader.
Only one delegate per trader; /delegate/set replaces any prior delegate.
Batching With send_calls
- Approval + open trade.
- Approval + margin deposit.
- Cancel resting order + create replacement limit order.
- Multiple independent generated calls, all on Base, that are logically safe together.
Settlement Polling (history API)
Market opens and closes settle after the submitted transaction emits aMarketOrderInitiated event. Only poll when you have a real tx hash from a submitted transaction.
status is one of:
| status | Blocks present |
|---|---|
executed | initiated + executed |
canceled | initiated + canceled (blockNumber, blockTimestamp, logIndex, txHash, orderId, txFrom, trader, pairIndex) |
pending | initiated only |
{ "success": false, "errorMessage": "Market order not found ..." }. Treat as still-pending or invalid hash — do not interpret as canceled.
Polling pattern (exponential backoff, ~60 s cap):
core /user-data and emit LimitExecuted events on chain.
History And PnL (history API)
Allapi.avantisfi.com endpoints return HTTP 200 even on logical failure and use the legacy envelope:
success before reading data. userAddress must be 0x-prefixed; the service short-circuits otherwise.
Endpoint reference
| Endpoint | Purpose |
|---|---|
GET /v2/history/portfolio/history/:userAddress/:page/:limit? | Closed trades, paginated. page is 1-indexed (first page = 1). limit capped at 20. |
GET /v2/history/portfolio/all/:userAddress/:page/:limit? | All trades (open + closed). Same page / limit rules as above. |
GET /v2/history/portfolio/top/:userAddress | Top 3 closed trades by net PnL. |
GET /v2/history/portfolio/top/:userAddress/:limit?/:timeStamp? | Top N by net PnL, optionally from a Unix-seconds floor. Includes still-open market entries (filter differs from /top/:userAddress). |
GET /v2/history/portfolio/profit-loss/:userAddress/:grouped?/:startDate? | Aggregate PnL. grouped must be the literal string grouped to bucket per pairIndex; anything else (including omitted) returns a single combined bucket. startDate is anything new Date(...) can parse; omit (or pass false) for no time filter — internally compared in Unix seconds. |
GET /v2/history/referral/stats/:userAddress | Referral activity (as referrer and as trader). USDC fields here are pre-divided by 1e6. |
GET /v2/market-order-initiated/status/:txHash | See Settlement Polling. |
/history response
event.args.tis the on-chainTradestruct at open time.event.args.positionSizeUSDCis the collateral closed in this event, not the position notional._grossPnlis per-close gross PnL in USDC (negative = loss).
/all response adds
open flips from true to false after the trade closes.
/profit-loss response
_mapped_netPnlwhenevent.args.isPnl === true(ZFP)._mapped_grossPnlotherwise (fixed-fee).
pairIndex is null.
/referral/stats response
1e6).
Rate limit
History API: ~10 req/s per IP. Batch and cache when possible.Chat-only fallback: Avantis UI
When the user wants a tx-builder action (open, close, cancel, margin update, TP/SL change, USDC approval, delegate set/remove) and there is no shell, terminal, or direct HTTP tool in the current surface (typical for ChatGPT, Claude.ai):-
Use
web_requestagainstdata.avantisfi.com,core.avantisfi.com, orapi.avantisfi.comto answer the read side of the question (pair info, the user’s open positions, recent PnL). - Tell the user plainly that signing and submitting Avantis trades from this surface requires the Avantis web UI (or a CLI harness like Claude Code, Codex, or Cursor terminal).
-
Build a deep link to the relevant market and surface it as a clickable link. URL pattern:
<SYMBOL>is the pair’sfromfrompairInfos["<pairIndex>"](e.g.BTC,ETH,SNDK). Examples:https://www.avantisfi.com/trade?asset=ETH-USDhttps://www.avantisfi.com/trade?asset=BTC-USDhttps://www.avantisfi.com/trade?asset=SNDK-USD
- If the user already supplied concrete trade parameters (side, leverage, collateral, TP, SL), summarize them in your message so they can reproduce the intent inside the UI. Do not claim the position was opened or modified — the UI flow is user-driven.
web_request on the same surfaces.
Error Handling
tx-builder error envelope:| Code | Meaning |
|---|---|
VALIDATION_ERROR | Query shape: bad address, missing required field, out-of-range numeric. details.fieldErrors is populated. |
BAD_REQUEST | Pre-trade check failed (delisted, min position, leverage envelope, liquidity) or domain rule violated (e.g. takeProfit=0). |
UPSTREAM_ERROR | data.avantisfi.com or feed-v3.avantisfi.com returned non-2xx. |
NOT_FOUND | Unknown route or pair index lookup miss. |
INTERNAL_ERROR | Unexpected service error. Surface and report. |
success:false is the normal failure mode. For a wallet with no portfolio, some endpoints return success:false with Unable to get the portfolio. while others return success:true with empty data. Treat as empty/unknown unless the user expected existing history.
Recommended handling:
- Surface validation messages verbatim — they describe the exact constraint that failed.
- For
/trade/open, inspectmeta.validationon success and show the user position size, min position, leverage envelope, and available liquidity when useful. - For management actions, do not rely on tx-builder to prove the position/order exists. Verify via
core /user-data. - Never silently retry a
BAD_REQUESTwith adjusted parameters; show options and ask the user.
Sanity Caps (tx-builder server-side)
These are looser than per-pair envelopes — the per-pair check still applies.| Field | Server cap | Notes |
|---|---|---|
leverage | <= 1000 | Per-pair max is stricter; see meta.validation.maxLeverage |
slippagePercent | <= 100 | |
executionFeeEth | <= 1 ETH | Default ~0.00035 |
priceUpdateData | <= 16 KB | URL length is the practical limit |
trader, delegate, spender accept both checksummed and all-lowercase; the service normalizes to checksum in the response.
Scaling Quick Reference (on-chain side)
| Domain | Scale | Example |
|---|---|---|
| Prices, leverage, percentages, slippage | × 10^10 | 10x leverage → 100_000_000_000 |
| USDC amounts (collateral, position size, fees) | × 10^6 | 100 USDC → 100_000_000 |
ETH amounts (value, execution fee) | × 10^18 | 0.00035 ETH → 350_000_000_000_000 |
core /user-data strings or decoding on-chain logs. tx-builder inputs and data.avantisfi.com are always human-decimal.
Current tx-builder Endpoint Inventory
| Endpoint | Calldata? | Purpose | value |
|---|---|---|---|
GET /trade/open | Yes | Open market, ZFP, limit, or stop-limit trade | execution fee wei |
GET /trade/close | Yes | Close (full or partial) | execution fee wei |
GET /trade/cancel | Yes | Cancel a resting limit / stop-limit | 0x0 |
GET /margin/update | Yes | Deposit / withdraw collateral | 0x1 |
GET /tpsl/update | Yes | Update TP and SL | 0x1 |
GET /delegate/set | Yes | Set delegate | 0x0 |
GET /delegate/remove | Yes | Remove delegate | 0x0 |
GET /token/approve | Yes | Approve USDC | 0x0 |
GET /pairs | No | Pair summaries (index + symbol + Lazer flag) | — |
GET /pairs/<index> | No | Single pair detail | — |
GET /addresses | No | Contract addresses on Base | — |
GET /health | No | { status:"ok", chainId:8453 } | — |
GET /docs | No | Swagger UI | — |
GET /openapi.json | No | OpenAPI 3.1 spec | — |
Portfolio Inspection Recipe
To answer “show me my Avantis activity” combine three sources:| Source | What it returns |
|---|---|
GET core.avantisfi.com/user-data?trader=<addr> | Current open positions + resting limit orders |
GET api.avantisfi.com/v2/history/portfolio/all/<addr>/1/20 | Page of all trades (open + closed), chronological with PnL |
GET api.avantisfi.com/v2/history/portfolio/profit-loss/<addr>/grouped | Aggregate PnL per pair |
/user-datareturns raw stringified ints (USDC/1e6, prices/leverage/1e10)./v2/history/portfolio/allmixes — most numeric fields are already human decimals./v2/history/portfolio/profit-lossreturns human decimals rounded to two places.
profit-loss for /v2/history/portfolio/top/<addr>/5 (top 5 by net PnL).
Cross-Recipe Pattern: Agent Loop
Pyth Feeds
Avantis prices flow through Pyth, never Chainlink directly for entry / settlement. Two paths:| Path | When to use | tx-builder behavior |
|---|---|---|
Pyth Core / Hermes (priceSourcing=0) | Every pair has a feed.feedId (bytes32). Default for pairs without a stable Lazer feed. | Auto-fetches update bytes from feed-v3.avantisfi.com /v2/pairs/<pairIndex>/price-update-data (core leg). |
Pyth Lazer / Pro (priceSourcing=1) | Pairs with lazerFeed.state === 'stable' get faster updates. | Auto-fetches from the same feed-v3 endpoint and uses the pro leg. |
/margin/update and /tpsl/update. Override via priceUpdateData + priceSourcing only when you have a fresh blob cached and want a fully offline call.
On-Chain Trade Tuple
Order matches the on-chain ITradingStorage.Trade struct. Useful when reading raw events or debugging:
| # | Field | Type | Scale |
|---|---|---|---|
| 0 | trader | address | — |
| 1 | pairIndex | uint256 | — |
| 2 | index | uint256 | — (0 on open; assigned at fill) |
| 3 | initialPosToken | uint256 | × 10^6 (USDC collateral) |
| 4 | positionSizeUSDC | uint256 | × 10^6 (often 0 on open; repurposed as timestamp on chain after open) |
| 5 | openPrice | uint256 | × 10^10 |
| 6 | buy | bool | — (true = long) |
| 7 | leverage | uint256 | × 10^10 |
| 8 | tp | uint256 | × 10^10 (0 = none) |
| 9 | sl | uint256 | × 10^10 (0 = none) |
| 10 | timestamp | uint256 | usually 0; contract overrides |
Trading.openTrade(t, type, slippageP) — type is the OpenLimitOrderType enum from Step 4 — Open A Trade; slippageP is × 10^10 percent.
Events Worth Indexing
If your agent indexes Avantis logs (rather than polling APIs):| Event | When |
|---|---|
MarketOrderInitiated(orderId, trader, pairIndex, open) | A market open / close has been queued |
MarketExecuted(orderId, t, open, price, positionSizeUSDC, percentProfit, usdcSentToTrader, isPnl, lossProtectionTier) | Market order settled |
LimitExecuted(orderId, t, ...) | Limit / stop-limit triggered |
OpenLimitPlaced / OpenLimitUpdated / OpenLimitCanceled | Resting-limit lifecycle |
MarginUpdated | Collateral deposit / withdrawal |
TpUpdated / SlUpdated | TP / SL change |
orderId from MarketOrderInitiated (or just the txHash) is what to feed into /v2/market-order-initiated/status/<txHash>.
Key Thresholds
| Constant | Value | Meaning |
|---|---|---|
| Liquidation threshold | ~85% | Position liquidates once loss exceeds ~85% of collateral |
| Max stop-loss distance | 80% | _MAX_SL_P |
| Max slippage | 80% | _MAX_SLIPPAGE |
| Max execution / keeper reward | 10 USDC | _MAX_EXEC_REWARD |
| Default execution fee | 0.00035 ETH | Used by tx-builder when executionFeeEth omitted |
| Margin-withdraw threshold | 80% | Withdrawals must keep position above ~20% effective collateral |