Skip to content

feat: superbridge integration#21

Open
OWK50GA wants to merge 3 commits into
masterfrom
feat/integrate-superbridge
Open

feat: superbridge integration#21
OWK50GA wants to merge 3 commits into
masterfrom
feat/integrate-superbridge

Conversation

@OWK50GA
Copy link
Copy Markdown
Collaborator

@OWK50GA OWK50GA commented Apr 14, 2026

feat: cross-chain bridging via Superbridge — IBridgeClient, SuperbridgeClient, BridgeSettler

What this does

Adds full cross-chain settlement to Griffin. When a user submits an intent where fromChain !== toChain, the new BridgeSettler routes it through Superbridge, executing each step of the bridge transaction on-chain and waiting for destination-chain confirmation before marking the intent complete.


New files

src/blockchain/IBridgeClient.ts
The bridge provider interface — same design discipline as IChainClient and IDexClient. Defines three types and one interface:

  • BridgeRoute — one provider's offer: amounts, fees, estimated time, ordered steps
  • BridgeStep — a single on-chain action within a route (approval, bridge tx)
  • BridgeStepTransaction — raw calldata to submit for one step
  • IBridgeClientgetRoutes(), getStepTransaction(), waitForCompletion()

BridgeSettler depends only on this interface. Adding Across, Hop, or any other bridge means implementing IBridgeClient and registering it — nothing else changes.

src/blockchain/superbridge/SuperbridgeClient.ts
Implements IBridgeClient against the Superbridge HTTP API. The @superbridge/sdk npm package is not yet publicly available, so this uses fetch directly — mirroring the official SDK examples exactly. When the SDK ships, the post()/get() calls can be swapped in with no interface changes.

Key behaviours:

  • Converts Griffin's "eip155:133" chain ID format to the numeric IDs Superbridge expects
  • Caches fetched route quotes by routeId so getStepTransaction() can look them up without a second API call
  • Builds the correct step list (revoke approval → token approval → bridge) from the quote shape
  • For approval steps, returns the pre-built calldata directly without hitting the API
  • For bridge steps, calls /v1/get_step_transaction with the correct recipient at execution time
  • waitForCompletion() polls /v1/activity, respects nextCheckTimestamp from the API, and times out after 60 polls

src/settlement/BridgeSettler.ts
Implements ISettler with type = SettlerType.BRIDGE.

canSettle() checks three things in order:

  1. fromChain !== toChain — bridge is cross-chain only; same-chain intents are declined immediately
  2. A chain client exists for the source chain (needed to submit transactions)
  3. At least one bridge provider returns a non-empty route list

settle() finds the best route across all registered providers (highest amountOut), then executes each step sequentially: fetches calldata via getStepTransaction(), submits via the chain client's signer, waits for on-chain confirmation, then calls waitForCompletion() for destination-chain finality.

src/tests/settlement/BridgeSettler.test.ts
16 unit tests covering all canSettle and settle paths — same-chain decline, missing chain client, no routes, multi-provider fallback, best route selection, approval step ordering, error propagation.

src/tests/blockchain/SuperbridgeClient.test.ts
18 unit tests covering route mapping, step list construction, approval calldata (no API call), bridge step transaction fetching, chain ID conversion, cache miss handling, and API error propagation. All tests mock fetch — no real API key required.


Modified files

src/settlement/ISettler.ts
Added BRIDGE = "bridge" to SettlerType.

src/settlement/SwapSettler.ts
Added an early return in canSettle() for cross-chain intents:

if (intent.fromChain !== intent.toChain) {
  return { capable: false, reason: `Cross-chain intent detected, defaulting to bridge` };
}

Previously SwapSettler would attempt to swap cross-chain, which is semantically wrong. Now it declines cleanly and the engine falls through to BridgeSettler.

src/blockchain/index.ts
Exports IBridgeClient, BridgeRoute, BridgeStep, BridgeStepTransaction, SuperbridgeClient, and SuperbridgeClientConfig.

src/config/index.ts
Added external.superbridge.apiKey reading from SUPERBRIDGE_API_KEY.

src/app.ts
Composition root now instantiates SuperbridgeClient and registers BridgeSettler as the third settler. Missing SUPERBRIDGE_API_KEY logs a warning and BridgeSettler gracefully declines all cross-chain intents rather than crashing.

.env.example / packages/orchestrator/.env.example
Added SUPERBRIDGE_API_KEY.


Settlement priority after this PR

SettlementEngine.settle(intent)
  → InventorySettler.canSettle()   // Griffin holds the output token?
  → SwapSettler.canSettle()        // Same-chain? DEX has liquidity?
      ↳ declines cross-chain with "defaulting to bridge"
  → BridgeSettler.canSettle()      // Cross-chain? Bridge route available?
      → SuperbridgeClient.getRoutes()
  → BridgeSettler.settle()
      → SuperbridgeClient.getRoutes()        // live route refresh
      → pick best route (highest amountOut)
      → for each step:
          → SuperbridgeClient.getStepTransaction()
          → EvmClient.signer.sendTransaction()
          → EvmClient.waitForConfirmation()
      → SuperbridgeClient.waitForCompletion()  // destination chain finality
      ← SettlementResult

What is not in this PR

  • Additional bridge providers (Across, Hop) — implement IBridgeClient, register in app.ts
  • sendTransaction(to, data, value) on IChainClient — currently duck-typed to access EvmClient.signer directly; tracked as a TODO for the next interface revision

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