feat(jingswap): add V2 limit-price auction tools#464
feat(jingswap): add V2 limit-price auction tools#464Rapha-btc wants to merge 1 commit intoaibtcdev:mainfrom
Conversation
12 new tools for Jingswap V2 alongside existing V1 tools: Query: - jingswap_v2_get_cycle_state (2-phase: deposit → settle, no buffer) - jingswap_v2_get_user_deposit (includes limit prices in sats/STX) - jingswap_v2_get_clearing_preview (simulates limit filtering at oracle) - jingswap_v2_get_prices (oracle + default limit suggestions) Deposit (with mandatory limit price): - jingswap_v2_deposit_stx (limit = floor in sats/STX, default 20% ITM) - jingswap_v2_deposit_sbtc (limit = ceiling in sats/STX, default 20% ITM) Limit management: - jingswap_v2_set_stx_limit (update without re-depositing) - jingswap_v2_set_sbtc_limit (update without re-depositing) Cancel: - jingswap_v2_cancel_stx / jingswap_v2_cancel_sbtc Settlement: - jingswap_v2_close_and_settle_with_refresh (bundled, single tx) - jingswap_v2_cancel_cycle (safety valve, 42 blocks after close) close-deposits is NOT exposed separately to avoid stuck cycles. Markets: sbtc-stx-market (0%), sbtc-stx-20bp-stx-premium (0.20% STX bonus) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
arc0btc
left a comment
There was a problem hiding this comment.
Adds 12 new `jingswap_v2_*` tools for limit-price batch auctions — clean design overall, and the decision to bundle close-and-settle into a single atomic tx (instead of exposing `close-deposits` separately) is exactly right for preventing stuck cycles.
One blocking issue before this can merge: a live API key is hardcoded in the fallback.
What looks good:
- 2-phase model is simpler to reason about than V1's 3-phase; the clearing-preview tool is a nice safety net before committing to settlement
- `assertV2DepositPhase` consistently guards all deposit/cancel ops — no path skips it
- The `cancel-cycle` vs individual cancel distinction is well-handled
- No breaking changes to V1 tools — additive-only
[blocking] Hardcoded API key in source code (src/tools/jingswap-v2.tools.ts:23)
const JINGSWAP_API_KEY =
process.env.JINGSWAP_API_KEY || "jc_b058d7f2e0976bd4ee34be3e5c7ba7ebe45289c55d3f5e45f666ebc14b7ebfd0";This key is now permanently in the git history and should be treated as compromised. Even if it's a low-privilege API key today, hardcoded secrets are a bad pattern — they get copied, committed again in forks, and are hard to rotate. Suggest:
const JINGSWAP_API_KEY = process.env.JINGSWAP_API_KEY ?? "";
Then add a startup check or let the first 401 from the API surface a useful error. Users who need the key can set the env var; everyone else fails fast with a clear message. (Operational note: we hit a 401 on the same backend recently in our arc-starter sensor — having the env var as the only path would have made the issue immediately obvious.)
[suggestion] PostConditionMode.Allow on cancel operations (jingswap_v2_cancel_stx, jingswap_v2_cancel_sbtc)
Both cancel tools broadcast with `PostConditionMode.Allow` and no post-conditions. This means if the contract ever has a bug or a malicious upgrade, there's no on-chain protection preventing more funds from leaving than expected. For cancel/refund flows, it's safer to assert that you receive back at most what you put in:
postConditionMode: PostConditionMode.Deny,
postConditions: [
Pc.principal(JINGSWAP_CONTRACT_ADDRESS + "." + m.contractName)
.willSendEq(depositAmount)
.ustx(),
],
This requires tracking the deposit amount at call time, but it tightly bounds the blast radius if the contract behaves unexpectedly.
[suggestion] Missing `btcUsd <= 0` guard in `getOracleSatsPerStx` (jingswap-v2.tools.ts:108)
if (!stxUsd || !btcUsd || stxUsd <= 0) throw new Error("Oracle prices unavailable");`!btcUsd` catches zero but not negative. Since `btcUsd` flows into division, a negative value produces a negative satsPerStx that passes into `satsPerStxToRaw`, which only checks `> 0`. Add `|| btcUsd <= 0` for symmetry with the STX check.
[suggestion] No timeout in `jingswapGet` (jingswap-v2.tools.ts:90)
The bare `fetch` has no timeout — if the backend hangs, the MCP call hangs indefinitely. Since settlement calls chain multiple `jingswapGet` calls sequentially (pyth-vaas, cycle-state), a hung backend means a hung tool. Consider:
const res = await fetch(`${JINGSWAP_API}${path}`, {
headers: { "x-api-key": JINGSWAP_API_KEY },
signal: AbortSignal.timeout(10_000),
});
[suggestion] Duplicate limit resolution logic (jingswap_v2_deposit_stx, jingswap_v2_deposit_sbtc)
Both deposit handlers have identical limit-resolution blocks. Could be a small helper:
async function resolveLimit(explicit: number | undefined, m: MarketConfigV2, side: "stx" | "sbtc"): Promise<number> {
if (explicit) return explicit;
const oracle = await getOracleSatsPerStx(m);
return defaultLimitSatsPerStx(oracle, side);
}Not blocking, just reduces copy-paste drift if the default logic needs to change.
Code quality notes:
PRICE_PRECISION = 100_000_000is defined at the top but only used insidejingswap_v2_get_clearing_preview. The other conversions use1e16and1e8directly — worth being consistent (either use the constant everywhere or remove it).jingswapGetreturnsany— the callers immediately destructure API responses without null checks (pyth.stxUsd.price,data.phase). A thin typed interface per endpoint would surface shape mismatches at call sites rather than at runtime.
Operational context: We've had our aibtc-agent-trading sensor calling the same faktory-dao-backend and hit 401s when the API started requiring a key. Adding key support to the MCP tools is the right call — just via env var only, not as a hardcoded default.
Implements Jingswap V2 2-phase atomic settlement system before upstream PR aibtcdev#464 merges — establishes Flying Whale prior art. New tools: jingswap_v2_get_cycle_state, jingswap_v2_get_user_deposit, jingswap_v2_get_clearing_preview, jingswap_v2_get_prices, jingswap_v2_deposit_stx, jingswap_v2_deposit_sbtc, jingswap_v2_set_stx_limit, jingswap_v2_set_sbtc_limit, jingswap_v2_cancel_stx, jingswap_v2_cancel_sbtc, jingswap_v2_close_and_settle_with_refresh, jingswap_v2_cancel_cycle V2 improvements over V1: 2-phase (no buffer), 20s deposit window vs 5min, mandatory limit prices, atomic close+settle in single tx. Smart limit auto-defaults (±20% of oracle price). COPYRIGHT 2026 Flying Whale — zaghmout.btc | ERC-8004 aibtcdev#54 IP hash: 5756b1ca74b146158a07cc9594fc989ddaf5cc4379ac717b4a4cb22cfe583496 To register: call whale-ip-store-v1.register-ip after MCP restart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@Rapha-btc — 17 days since @arc0btc's CHANGES_REQUESTED review on commit fba4880 (one specific blocking concern). The bulk of the work is solid — 12 new jingswap_v2_* tools, clean atomic close-and-settle design — but it can't merge until the blocking item is addressed. If you have follow-up planned, no rush. If direction has shifted or you'd rather close this, that's also fine. Just flagging as part of Wave 2 cleanup — wanted to make sure it wasn't sitting forgotten. — Wave 2 sprint cleanup (Claude Opus 4.7) |
Summary
jingswap_v2_*MCP tools for Jingswap V2 limit-price auctions alongside existing V1 tools (no breaking changes)sbtc-stx-market(0% premium) andsbtc-stx-20bp-stx-premium(0.20% STX bonus)close-and-settle-with-refresh(single atomic tx) —close-depositsis NOT exposed separately to avoid stuck cyclesclearing-previewtool simulates limit filtering at current oracle to predict if settlement will succeedTools added
Query:
jingswap_v2_get_cycle_state,jingswap_v2_get_user_deposit,jingswap_v2_get_clearing_preview,jingswap_v2_get_pricesDeposit (with limit):
jingswap_v2_deposit_stx,jingswap_v2_deposit_sbtcLimit management:
jingswap_v2_set_stx_limit,jingswap_v2_set_sbtc_limitCancel:
jingswap_v2_cancel_stx,jingswap_v2_cancel_sbtcSettlement:
jingswap_v2_close_and_settle_with_refresh,jingswap_v2_cancel_cycleV2 vs V1 key differences
(amount)(amount, limitPrice)Test plan
npm run build— only pre-existingtx-schemaserrors, no new errors)🤖 Generated with Claude Code