feat(api): bridge API — deposit quotes, registration, status - #375
Draft
sadiq1971 wants to merge 4 commits into
Draft
feat(api): bridge API — deposit quotes, registration, status#375sadiq1971 wants to merge 4 commits into
sadiq1971 wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is ❌ Your patch status has failed because the patch coverage (38.53%) is below the target coverage (50.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## feat/xreserve-inbound #375 +/- ##
=========================================================
+ Coverage 35.24% 35.27% +0.03%
=========================================================
Files 170 176 +6
Lines 13243 13569 +326
=========================================================
+ Hits 4667 4787 +120
- Misses 8198 8390 +192
- Partials 378 392 +14
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
sadiq1971
force-pushed
the
feat/xreserve-inbound
branch
from
July 27, 2026 11:04
164aa29 to
b6ad2bd
Compare
sadiq1971
force-pushed
the
feat/bridge-api
branch
from
July 27, 2026 11:09
850f752 to
a1833fb
Compare
sadiq1971
force-pushed
the
feat/xreserve-inbound
branch
from
July 27, 2026 11:53
b6ad2bd to
dac0db3
Compare
sadiq1971
force-pushed
the
feat/bridge-api
branch
from
July 27, 2026 11:56
a1833fb to
6b32691
Compare
sadiq1971
force-pushed
the
feat/xreserve-inbound
branch
from
July 27, 2026 12:29
dac0db3 to
864d5ff
Compare
Token-agnostic bridge endpoints on the api-server (#358). The dapp never encodes a bridge transaction: it asks for a quote (fully ABI-encoded unsigned txs), signs blindly, then registers the sent tx for tracking - the EVM mirror of the Canton prepare/execute pattern. - GET /api/v2/bridge/tokens - bridgeable tokens, fees, indicative ETA - POST /api/v2/bridge/deposit/quote - conditional ERC-20 approve + mechanism deposit call; recipient encoding (keccak256 party id + hookData for xreserve) resolved server-side from the authenticated user's Canton party - POST /api/v2/bridge/deposits - forwards the tx hash to the relayer; all transfer params come from the stored quote, never the caller - GET /api/v2/bridge/transfers/{id} - relayer status proxy pkg/bridgeapi: DepositQuoter per mechanism (xreserve first), in-memory TTL quote store, relayer HTTP client, optional eth_call allowance checker (no eth_rpc_url -> approve always included). Enabled by the optional 'bridge' config block on APIServer. depositToRemote ABI follows Circle's published interface; mainnet verification tracked in #360.
Quoting is a pure encoding function; nothing is persisted. Registration
takes {token, amount, tx_hash} with the recipient party re-derived from
the authenticated session, so no quote binding is needed - the chain is
the source of truth, and a registration with wrong parameters only
yields a status row the adapter never completes. Deletes the quote
store, quote ids, TTLs, and ownership plumbing.
- validate tx_hash as a 0x-prefixed 32-byte hex string and namespace the
registered transfer id by mechanism (was: an unvalidated/suffix-crafted
tx_hash could collide with the on-chain observer's <txhash>-<logindex>
ids and squat a real deposit's row)
- reject amounts whose base-unit value exceeds uint256 (was: silent
mod-2^256 wrap in the encoded calldata, diverging from the shown amount)
- require a session on GET /bridge/transfers/{id} (was: unauthenticated
read of any user's sender/recipient/amount by public tx-hash id)
- document that the single approve(amount) step is correct for USDC-class
tokens and would need an approve(0) reset for USDT-style tokens
sadiq1971
force-pushed
the
feat/bridge-api
branch
from
July 27, 2026 12:31
6b32691 to
2e1fdd7
Compare
The APIServer.bridge block (deposit quotes) landed but the shipped configs never used it. Wire it into the docker config (devstack); local-devnet + mainnet carry a documented commented block pending the relayer URL and confirmed Circle endpoints (#360).
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.
Part of #358 and epic #361. Stacked on #374 (xreserve adapter) — review the last two commits until #374 merges.
What
Token-agnostic bridge endpoints on the api-server (
pkg/bridgeapi). The dapp never encodes a bridge transaction: it asks for a quote — fully ABI-encoded unsigned transactions — signs blindly, then registers the sent tx hash for status tracking. The EVM mirror of the Canton prepare/execute pattern; one dapp flow for every token, present and future.Endpoints
GET /api/v2/bridge/tokens— bridgeable tokens (symbol, mechanism, decimals, fee, indicative ETA)POST /api/v2/bridge/deposit/quote(auth) —{token, amount}→{chain_id, steps[], fees, estimated_seconds}. Steps: conditional ERC-20approve+ the mechanism's deposit call. Recipient encoding (keccak256(partyId)+ party id in hookData for xreserve) is resolved server-side from the authenticated user's Canton party and never leaves the server. Quoting is stateless — a pure encoding function, nothing persisted, quotes can be re-requested freely.POST /api/v2/bridge/deposits(auth) —{token, amount, tx_hash}→ forwards to the relayer'sPOST /api/v1/transfers. Token/amount are re-validated and the recipient party is re-derived from the authenticated session, never taken from the caller. No quote binding is needed: the chain is the source of truth, and a registration with wrong parameters only yields a status row the adapter never completes.GET /api/v2/bridge/transfers/{id}— relayer status proxy (status+stage)Auth is the existing per-handler
X-Signature/X-MessageEIP-191 pattern (same aspkg/transfer).Design points
DepositQuoterper mechanism, xreserve first. The xreserve quoter'sdepositToRemotecalldata is round-trip tested (unpack + assert every argument, including the keccak recipient hash). The ABI follows Circle's published interface; mainnet verification is a chore(bridge): hardening + mainnet enablement #360 item.eth_rpc_urlconfigured, aneth_calldecides whether to include the approve step; unset (or on RPC failure) the approve step is always included — a redundant approve is safe, a missing one strands the deposit. This keeps the api-server free of a hard outbound-EVM dependency.bridgeblock onAPIServer(relayer_url,chain_id, optionaleth_rpc_url, per-tokenmechanism/evm_address/decimals/xreserve{contract, remote_domain, max_fee}). Omitted → no routes mounted.Not in this PR
GET /bridge/transfers?address=) — needs an address filter on the relayer list endpoint; follow-up.Testing
golangci-lintclean; config tests pass with the new optional field