Skip to content

feat(sdk): swap() end-to-end for usdc to usdh#10

Merged
sumfxn merged 1 commit into
mainfrom
feat/sdk-swap-impl
Apr 28, 2026
Merged

feat(sdk): swap() end-to-end for usdc to usdh#10
sumfxn merged 1 commit into
mainfrom
feat/sdk-swap-impl

Conversation

@sumfxn
Copy link
Copy Markdown
Owner

@sumfxn sumfxn commented Apr 28, 2026

What

swap() end-to-end for USDC -> USDH. The SDK is now feature-complete for V1.

Files

  • packages/sdk/src/kit.ts — full swap impl (~150 LOC)
  • packages/sdk/src/pricing.tsformatDecimal accepts optional maxFracDigits
  • packages/sdk/src/pair-resolver.tsResolvedPair adds baseSzDecimals + quoteWeiDecimals
  • packages/sdk/src/types/swap.tsSwapResult.txHash removed (HL has no tx hash for L1 actions)

Behaviour

  • Reads L2 book, computes mid + slippage-tolerant aggressive limit mid * (1 + slippageBps/10000)
  • Builds Market IOC order action at the limit, with size in USDH base units
  • Signs via signL1Action, posts via /exchange
  • Returns SwapResult with orderId, received, spent, price, realised slippageBps
  • Realised slippage is reported, not thrown (the limit already enforces pre-fill)
  • USDT throws NotImplementedError until the double-hop lands
  • Nonces are monotonic across concurrent swap() calls

Tests (121 total)

  • pnpm lint clean
  • pnpm typecheck clean
  • 7 swap tests cover validation, happy path with limit price, per-call slippage, monotonic nonce, /exchange err, order err, realised slippage on result
  • pnpm build ESM 24KB + CJS + dts

Follow-ups (post-V1)

Wire createUsdhKit to InfoClient + signing + ExchangeClient. Reads the
L2 orderbook, computes a slippage-tolerant aggressive limit price
(mid * (1 + slippageBps/10000)), builds a Market IOC order action,
signs via the configured signer, submits to /exchange, parses the
fill, and returns a SwapResult with realised slippage in bps.

The slippage check is pre-fill (HL rejects bad prices itself) and the
realised value is reported on the result without throwing after
settlement. NetworkError wraps transport and per-order errors.
NotImplementedError for USDT until the double-hop lands.

SwapResult.txHash removed (HL L1 actions have no Ethereum tx hash);
orderId is the canonical identifier. ResolvedPair exposes assetIndex,
baseSzDecimals, and quoteWeiDecimals so price and size strings respect
actual pair precision. Nonces are emitted monotonically across
concurrent calls.

Tests: 7 swap + others, 121 total in the package.
@sumfxn sumfxn merged commit b7676de into main Apr 28, 2026
2 checks passed
@sumfxn sumfxn deleted the feat/sdk-swap-impl branch April 28, 2026 07:07
sumfxn pushed a commit that referenced this pull request Apr 28, 2026
Bump usdh-kit to 0.1.0 with the consolidated changelog from #1 through #10. First publish on npm with provenance.
sumfxn added a commit that referenced this pull request Apr 28, 2026
)

The post-fill slippage check was removed in PR #10 because Hyperliquid's
matcher enforces the limit price pre-fill, but the SlippageExceededError
class kept being exported and the README example told users to instanceof
it. Removing both means the public API stops promising a code path that
cannot run; consumers read result.slippageBps instead.

useUsdhKit was rebuilding the kit on every render, throwing away the
monotonic-nonce closure, the pair-resolver cache, and any in-flight
spotMeta request. Wrapping in useMemo keyed on the wagmi inputs gives
consumers a stable kit identity across renders and stops needless
network chatter on quote-heavy screens.

assertBookHasSides was unreachable: midPrice18 already throws
NetworkError with the same message format on the same book reference.
Added a defensive non-zero pair-index test in pair-resolver.test.ts
to lock the contract that pair.index is the SpotPair API field, not
the array position (mainnet has USDH/USDC at index 230).
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.

1 participant