feat: Circle Developer Wallets integration (foundation PR, 1 of 4)#3
Merged
Conversation
Maps to Circle's published Web SDK ErrorCode enum and Developer-Controlled Wallets numeric error codes: - code:1-11 (auth, service, retry, customer state) - code:155104, 155203, 155258, 155264 (session, balance, policy) - cctp-attestation-pending (for CCTP integration)
serialize_and_backoff, burst_then_pace, rotate_authorization, wait_attestation
- serialize_and_backoff: Wallets API concurrency lock recovery - burst_then_pace: Gateway throughput window pacing - rotate_authorization: EIP-3009 nonce regeneration - wait_attestation: CCTP iris-api polling
Q-values derived from Helix experiments (Apr 2026, Arc Testnet): - wallets-api-rate-limit + serialize_and_backoff (q=0.95, Exp B) - gateway-rate-limit + burst_then_pace (q=0.70) - gateway-nonce-used + rotate_authorization (q=0.92) - cctp-attestation-pending + wait_attestation (q=0.98) - circle-param-invalid + hold_and_notify (q=0.85) apiLayer set explicitly to ensure correct Gene Map lookup matching post v14 migration.
- perceive.ts: dual-SDK error shape support (v7 raw AxiosError + v10 wrapped), numeric Circle code routing via response.data.code, apiLayer gating to prevent over-claiming on plain noise - strategies.ts: 15 construct() branches covering 4 telemetry-validated codes plus 11 Circle Web SDK numeric codes - Reordered defaultAdapters with circle first (most-specific URL fingerprint precedes Privy's generic /429/ regex)
Three Circle Wallets API failure scenarios + Helix repair: 1. Rate limit (10 concurrent → IMMUNE → serialize_and_backoff) 2. Param invalid (idempotencyKey trigger → IMMUNE → hold_and_notify) 3. Insufficient funds (over-draft → IMMUNE → reduce_request) Runs against Circle Sandbox + Arc Testnet, generates real Arc tx hashes verifiable on testnet.arcscan.app.
perceive-trace.ts: verify which platform adapter claims a given error probe-error-shapes.ts: dump raw Circle SDK error shapes for analysis Used during PR development to diagnose the dual-SDK-shape and adapter-chain ordering issues. Kept as permanent debug harnesses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Circle Integration — Foundation PR (1 of 4)
This PR adds Helix support for Circle Developer-Controlled Wallets, the first integration with Circle's payments infrastructure. Submitted as part of Helix's Circle Developer Grant application.
What it ships
Platform adapter (
packages/core/src/platforms/circle/)perceive.ts— identifies 15 Circle-specific failure types from SDK errors. Dual-version support (v7 raw AxiosError + v10 wrapped). Readserror.response.data.codefor Circle numeric codes (noterror.code, which is axios's own).strategies.ts— 15construct()branches mapping each failure type to a repair candidate.Engine extensions
types.ts— 12 newErrorCodeunion entries (Circle Web SDK numeric codes +cctp-attestation-pending)provider.ts— 4 new strategy implementations:serialize_and_backoff— Wallets API concurrency lock recoveryburst_then_pace— Gateway throughput window pacingrotate_authorization— EIP-3009 nonce regeneration (usesnode:crypto.randomBytes)wait_attestation— CCTP iris-api pollingseed-genes.ts— 5 new seed capsules with experimentally-validated q-values from Helix's April 2026 Arc Testnet experiments:wallets-api-rate-limitserialize_and_backoffgateway-rate-limitburst_then_pacegateway-nonce-usedrotate_authorizationcctp-attestation-pendingwait_attestationcircle-param-invalidhold_and_notifyllm.ts— whitelist new strategy names inVALID_STRATEGIESAdapter chain reorder (
platforms/index.ts)Circle adapter moved to front of
defaultAdapters. Required because Privy's generic/429/regex previously claimed Circle rate-limit errors before the Circle adapter could see them. Mitigated reverse-coupling withapiLayergating in Circle'sperceive.ts.End-to-end demo (
examples/circle-e2e.ts)3 scenarios on Circle Sandbox + Arc Testnet:
serialize_and_backoffidempotencyKeytrigger → IMMUNE →hold_and_notify(attempt 1, no LLM fallback)reduce_request→ real Arc Testnet txDiagnostic harnesses (
scripts/circle-bench/)perceive-trace.ts— verify which platform adapter claims a given errorprobe-error-shapes.ts— dump raw Circle SDK error shapes for analysisValidation
tscclean acrosspackages/coretestnet.arcscan.appGene Map deltas across two demo runs
Notable bugs surfaced and resolved during this PR
idempotencyKeybody rejection — Circle Sandbox rejects the field even though SDK TypeScript declares it valid. Solution: strip fromsendUsdc, keep insendUsdcWithBadFieldas Scenario 2's deliberate trigger.amountvsamounts— SDK type uses singularamount: string[]; sibling type uses plural. Latent bug inscripts/circle-bench/exp-b-rate-limit.ts(Circle server is lenient).detectApiLayerreading wrong field — axios errors don't put the URL inerror.message. URL is aterror.config.url/error.response.config.url.error.response.data.code, noterror.code—error.codeis axios's own ("ERR_BAD_REQUEST"), not Circle's.155201for insufficient funds; Developer-Controlled Wallets SDK actually returns155258. Different namespaces. PR reads both for forward-compat.[tempo, privy, coinbase, circle, ...]let Privy claim Circle 429s viamsg.includes('429'). Reordered with Circle first.@circle-fin/developer-controlled-wallets@7.3.0; demo deps use@10.3.1. Error wrapping differs. Perceive supports both shapes.What's NOT in this PR (planned follow-up)
PR #2 — Experimentally-validated capsules from April experiments:
decimals-metadata-mismatch+override_api_decimals(Exp A, real Arc tx0x113addf13baa75d60cd402360d2ecb67512c31b4214750f56ace81c749781d07)stale_quote+late_discover(Exp D, 96% E2E vs 0% bare baseline across 932 confirmed Arc tx)PR #3 — Real e2e demos for CCTP V2 and Circle Gateway (capsules are ready and seeded; no real run yet)
PR #4 — Mainnet adapter and production tier configuration (pending Circle KYC approval)
Related Circle products
Used today
Capsules ready (not yet exercised in demo)
Not used (out of scope for our agent-side use case)
Verification by reviewers
https://helix-telemetry.haimobai-adrian.workers.dev/v1/stats