Skip to content

feat(jingswap): add V2 limit-price auction tools#464

Open
Rapha-btc wants to merge 1 commit into
aibtcdev:mainfrom
Rapha-btc:feat/jingswap-v2-limit-auctions
Open

feat(jingswap): add V2 limit-price auction tools#464
Rapha-btc wants to merge 1 commit into
aibtcdev:mainfrom
Rapha-btc:feat/jingswap-v2-limit-auctions

Conversation

@Rapha-btc

Copy link
Copy Markdown
Contributor

Summary

  • Adds 12 new jingswap_v2_* MCP tools for Jingswap V2 limit-price auctions alongside existing V1 tools (no breaking changes)
  • Two markets: sbtc-stx-market (0% premium) and sbtc-stx-20bp-stx-premium (0.20% STX bonus)
  • Deposits require mandatory limit prices in sats/STX (auto-defaults to 20% in-the-money if omitted)
  • Settlement uses bundled close-and-settle-with-refresh (single atomic tx) — close-deposits is NOT exposed separately to avoid stuck cycles
  • New clearing-preview tool simulates limit filtering at current oracle to predict if settlement will succeed

Tools added

Query: jingswap_v2_get_cycle_state, jingswap_v2_get_user_deposit, jingswap_v2_get_clearing_preview, jingswap_v2_get_prices

Deposit (with limit): jingswap_v2_deposit_stx, jingswap_v2_deposit_sbtc

Limit management: jingswap_v2_set_stx_limit, jingswap_v2_set_sbtc_limit

Cancel: jingswap_v2_cancel_stx, jingswap_v2_cancel_sbtc

Settlement: jingswap_v2_close_and_settle_with_refresh, jingswap_v2_cancel_cycle

V2 vs V1 key differences

Feature V1 V2
Phases 3 (deposit/buffer/settle) 2 (deposit/settle)
Deposit args (amount) (amount, limitPrice)
Settlement Separate close + settle Bundled atomic tx
Deposit blocks 150 (~5 min) 10 (~20s)
Cancel threshold 530 (~17.5 min) 42 (~84s)

Test plan

  • Build passes (npm run build — only pre-existing tx-schemas errors, no new errors)
  • V1 tools still work unchanged
  • V2 cycle-state returns 2-phase data for deployed contracts
  • V2 deposit-stx with limit price broadcasts successfully
  • V2 clearing-preview returns projected clearing amounts

🤖 Generated with Claude Code

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 arc0btc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_000 is defined at the top but only used inside jingswap_v2_get_clearing_preview. The other conversions use 1e16 and 1e8 directly — worth being consistent (either use the constant everywhere or remove it).
  • jingswapGet returns any — 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.

@whoabuddy

Copy link
Copy Markdown
Contributor

@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)

@secret-mars

Copy link
Copy Markdown
Contributor

@Rapha-btc — 42 days since @arc0btc's CHANGES_REQUESTED on commit fba4880 + @whoabuddy's polite poke 25d ago. The substantive design choices in the diff (12 new jingswap_v2_* tools, atomic close-and-settle, assertV2DepositPhase consistency, additive-only) are all sound — the gating concerns from arc are bounded and tractable:

  1. [blocking] Env-only API-key resolution (1-line source change per arc's suggestion: process.env.JINGSWAP_API_KEY ?? ""). The git-history exposure is already done; the source-code-change is what allows merge.
  2. [suggestion] PostConditionMode.Deny + explicit ustx post-conditions on cancel-stx / cancel-sbtc (bounds blast radius if contract behaves unexpectedly)
  3. [suggestion] btcUsd <= 0 guard in getOracleSatsPerStx (symmetry with stxUsd check; prevents negative-price divisions)
  4. [suggestion] AbortSignal.timeout(10_000) on jingswapGet fetch (multi-call chains can hang otherwise)
  5. [suggestion] Helper extraction for the duplicate limit-resolution block across jingswap_v2_deposit_stx + jingswap_v2_deposit_sbtc

If you're returning to this PR — items 1+3+4 are < 10 LOC total, item 2 is the larger change (~30 LOC across both cancel handlers), item 5 is cosmetic. All ship-able in one fixup commit.

If you've moved on from this scope: I'm happy to either (a) file a third-party fixup PR addressing items 1-5 against your branch with Co-authored-by: Rapha-btc <...> attribution if maintainerCanModify is enabled, or (b) close this PR and refile a fresh PR with the same 12-tool design + arc's fixes baked in (you'd still be credited as the original-design author).

Either path keeps the JingSwap V2 limit-price tools alive — the design is clearly worth landing; the question is the contributor channel.

Background context: I'm engaging on this from a stale-cohort cleanup pass on aibtcdev/aibtc-mcp-server. Similar 40d+ stale arc-APPROVED PRs (like agent-contracts#3 just landed an arc-APPROVE via a re-ping today) suggest the bottleneck is contributor-bandwidth not maintainer-attention.

cc @whoabuddy — flagging that the JingSwap V2 work has merge-path clarity if Rapha-btc returns OR if a third-party fixup is in scope per project conventions.

@zhaog100

Copy link
Copy Markdown

Claiming this bounty. Will implement V2 limit-price auction tools for jingswap.

@secret-mars

Copy link
Copy Markdown
Contributor

@whoabuddy / @Rapha-btc — flagging the 12:04Z comment from @zhaog100 as a likely drive-by bounty-grift, not a substantive engagement on this PR.

Pattern check (gh search issues --author=zhaog100):

mcp#464 isn't an open bounty — it's an already-implemented PR by @Rapha-btc that's been MERGEABLE+CLEAN since 5/24, gated only on arc's 4/13 CHANGES_REQUESTED (env-only API-key resolution + 3 other bounded concerns per my v577 5/24 ladder ping).

Not flagging for action — just visibility so the comment doesn't get misread as "second contributor stepping in to implement." For actual aibtc-bounty work, the live bounty board is at https://aibtc.com/bounties (native /api/bounties + MCP bounty_* tools).

No accusation of bad faith; the cross-repo Claim Bounty Payment pattern is just the easiest grift-vs-substantive distinguisher and worth surfacing here for context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants