Skip to content

feat: Circle Developer Wallets integration (foundation PR, 1 of 4)#3

Merged
adrianhihi merged 7 commits into
mainfrom
feat/circle-integration
May 26, 2026
Merged

feat: Circle Developer Wallets integration (foundation PR, 1 of 4)#3
adrianhihi merged 7 commits into
mainfrom
feat/circle-integration

Conversation

@adrianhihi

Copy link
Copy Markdown
Collaborator

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). Reads error.response.data.code for Circle numeric codes (not error.code, which is axios's own).
  • strategies.ts — 15 construct() branches mapping each failure type to a repair candidate.

Engine extensions

  • types.ts — 12 new ErrorCode union entries (Circle Web SDK numeric codes + cctp-attestation-pending)
  • provider.ts — 4 new strategy implementations:
    • serialize_and_backoff — Wallets API concurrency lock recovery
    • burst_then_pace — Gateway throughput window pacing
    • rotate_authorization — EIP-3009 nonce regeneration (uses node:crypto.randomBytes)
    • wait_attestation — CCTP iris-api polling
  • seed-genes.ts — 5 new seed capsules with experimentally-validated q-values from Helix's April 2026 Arc Testnet experiments:
    Capsule Strategy q-value Source
    wallets-api-rate-limit serialize_and_backoff 0.95 Exp B (Apr 2026)
    gateway-rate-limit burst_then_pace 0.70
    gateway-nonce-used rotate_authorization 0.92
    cctp-attestation-pending wait_attestation 0.98
    circle-param-invalid hold_and_notify 0.85 added in this PR
  • llm.ts — whitelist new strategy names in VALID_STRATEGIES

Adapter 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 with apiLayer gating in Circle's perceive.ts.

End-to-end demo (examples/circle-e2e.ts)

3 scenarios on Circle Sandbox + Arc Testnet:

  1. Rate limit — 10 parallel transfers → IMMUNE → serialize_and_backoff
  2. Param invalididempotencyKey trigger → IMMUNE → hold_and_notify (attempt 1, no LLM fallback)
  3. Insufficient funds — overdraft → IMMUNE → reduce_request → real Arc Testnet tx

Diagnostic harnesses (scripts/circle-bench/)

  • perceive-trace.ts — verify which platform adapter claims a given error
  • probe-error-shapes.ts — dump raw Circle SDK error shapes for analysis

Validation

  • tsc clean across packages/core
  • ✅ 570/570 existing tests pass (no regressions)
  • ✅ Demo Run 1 (LEARN): all 3 scenarios complete with correct capsule attribution
  • ✅ Demo Run 2 (IMMUNE rerun): per-scenario elapsed slightly faster, all entries still tagged correctly, q-values continue rising
  • ✅ Real Arc Testnet transactions verified on testnet.arcscan.app

Gene Map deltas across two demo runs

Capsule uses (run 1) uses (run 2) Δ q-value (run 2)
wallets-api-rate-limit 21 33 +12 0.984
circle-param-invalid 7 11 +4 0.909
circle-insufficient-funds 6 10 +4 0.758
gateway-rate-limit 7 7 0 (not exercised) 0.700
gateway-nonce-used 8 8 0 (not exercised) 0.920
cctp-attestation-pending 6 6 0 (not exercised) 0.980

Notable bugs surfaced and resolved during this PR

  1. idempotencyKey body rejection — Circle Sandbox rejects the field even though SDK TypeScript declares it valid. Solution: strip from sendUsdc, keep in sendUsdcWithBadField as Scenario 2's deliberate trigger.
  2. amount vs amounts — SDK type uses singular amount: string[]; sibling type uses plural. Latent bug in scripts/circle-bench/exp-b-rate-limit.ts (Circle server is lenient).
  3. detectApiLayer reading wrong field — axios errors don't put the URL in error.message. URL is at error.config.url / error.response.config.url.
  4. Circle numeric code at error.response.data.code, not error.codeerror.code is axios's own ("ERR_BAD_REQUEST"), not Circle's.
  5. Code value drift — Circle Web SDK enum documents 155201 for insufficient funds; Developer-Controlled Wallets SDK actually returns 155258. Different namespaces. PR reads both for forward-compat.
  6. Adapter chain order[tempo, privy, coinbase, circle, ...] let Privy claim Circle 429s via msg.includes('429'). Reordered with Circle first.
  7. Dual SDK versions in repo — bench uses @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 tx 0x113addf13baa75d60cd402360d2ecb67512c31b4214750f56ace81c749781d07)
  • 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

  • Circle Developer-Controlled Wallets (Sandbox tier)
  • USDC ERC20 on Arc Testnet

Capsules ready (not yet exercised in demo)

  • CCTP V2 (attestation polling strategy implemented)
  • Circle Gateway (EIP-3009 nonce rotation + burst-then-pace pacing)

Not used (out of scope for our agent-side use case)

  • Web SDK / User-Controlled Wallets
  • Circle Mint API

Verification by reviewers

  • All transactions verifiable on testnet.arcscan.app
  • Helix telemetry registry: https://helix-telemetry.haimobai-adrian.workers.dev/v1/stats
  • To reproduce demo locally:
  git checkout feat/circle-integration
  npm install
  cp scripts/circle-bench/.env.example .env  # add your Circle Sandbox creds
  set -a; source .env; set +a
  npx tsx examples/circle-e2e.ts

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.
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