Skip to content

feat(api): bridge API — deposit quotes, registration, unified status #358

Description

@sadiq1971

Goal

Token-agnostic bridge API on the api-server so the dapp can bridge any configured token without knowing its mechanism. Today there are no bridge endpoints at all (PROMPT withdrawals are initiated by scripts only).

Design rule

The dapp never encodes a bridge transaction. Different tokens need different EVM calls (PROMPT: approve + depositToCanton on our CantonBridge; USDCx: approve + depositToRemote on Circle's xReserve with domain 10001, keccak recipient, hookData). The API returns a quote — fully ABI-encoded unsigned transactions the wallet signs blindly. EVM mirror of the existing Canton prepare/execute pattern.

Endpoints

  • GET /api/v2/bridge/tokens — supported tokens, mechanism, limits, fees, indicative ETA
  • POST /api/v2/bridge/deposit/quote — body {token, amount}; authenticated user's Canton party resolved server-side (SIWE session -> userstore), so recipient encoding never leaks to the dapp:
{
  "quote_id": "q_7f3a...",
  "chain_id": 1,
  "steps": [
    {"kind": "approve", "to": "0x<token>",  "data": "0x095ea7b3...", "value": "0"},
    {"kind": "deposit", "to": "0x<bridge>", "data": "0x...",         "value": "0"}
  ],
  "fees": {"bridge_fee": "0.15", "currency": "USDC"},
  "estimated_seconds": 900,
  "expires_at": "..."
}
  • approve step included only when on-chain allowance is insufficient
  • fees/ETA come from the mechanism (seconds + zero fee for wayfinder; ~15 min + Circle fee for xreserve)
  • quote params persisted against quote_id so registration can be validated against what was quoted
  • future optimization: EIP-2612 permit (USDC supports it) to collapse the approve step
  • POST /api/v2/bridge/deposits{quote_id, tx_hash}: registers the transfer for tracking (no-op-ish for wayfinder — the Source detects independently; required for xreserve)
  • POST /api/v2/bridge/withdraw/prepare|execute — mechanism-dispatched Canton-side signing: wayfinder builds InitiateWithdrawal; xreserve builds BridgeUserAgreement_Burn. Execute registers the transfer.
  • GET /api/v2/bridge/transfers?address=... and /{id} — unified status (status + stage)

Dapp flow for any token, present and future: GET tokens -> POST deposit/quote -> sign & send steps -> POST deposits -> poll GET transfers/{id}. Adding a token changes the token list, nothing else.

Structure

// pkg/bridgeapi — api-server local; quote construction is stateless ABI
// encoding and does not belong in the relayer.
type DepositQuoter interface {
    DepositQuote(ctx context.Context, req QuoteRequest) (*Quote, error)
}

One implementation per mechanism, built from the same per-token config as the relayer registry. Registration and status read go to the relayer (internal POST /api/v1/transfers, GET /api/v1/transfers).

Work items

  • pkg/bridgeapi/ — types, DepositQuoter, wayfinder + xreserve quoters, allowance check via eth client
  • Quote persistence + validation on registration
  • HTTP handlers + routes (chi), SIWE-authenticated
  • Withdraw prepare/execute dispatch (wayfinder InitiateWithdrawal gives PROMPT withdrawals their first production API)
  • Wayfinder withdrawal UX note: dapp shows stage honestly (offer submitted vs settled)
  • Unit tests per quoter; e2e: quote -> sign (devstack key) -> deposit -> status

Depends on

  • Relayer multi-token refactor (shared per-token config, transfers registration endpoint)

Related

  • Works for PROMPT alone even before the xreserve adapter lands; USDCx quotes activate with the xreserve inbound issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CIP-0086: Phase 2Phase 2 multi-token generalizationDeliverable: D3D3: Middleware ServiceType: FeatureAdded to issues and PRs to identify that the change is a new feature.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions