proxyTransport, see
Partner backend) or straight to the hub’s gateway with your
public project id (projectTransport), and the user signs in their wallet. Everything else is
the same client as on a server.
Two ways to reach the hub
Through your backend: proxyTransport
proxyTransport sends no hub credential: only your own authentication (headers), which your
backend’s resolveUser reads. Use credentials: "include" when your backend authenticates by cookie
on another origin. From an https page, the proxy’s URL must be https too, or on this computer
(http://localhost): the transport refuses, before any call, an address the browser would block as
mixed content. It waits longer than your proxy does: 15 s on reads, 30 s on quotes, 35 s on
submissions.
With a project id: projectTransport
- List your pages’ origins on the project in the hub dashboard: the exact scheme, host and port
of each page, and for development, for example,
http://localhost:3000. While the list is empty, any origin is accepted; once it is set, a page on an unlisted origin getsEPX0010(403). - Every route but the order history is open (
PROJECT_ROUTES): the catalog, the quotes, order submission, both status views, the chain facts and the refund routes.GET /orderswould show every visitor all of your orders: a call to it throws anInteropError(invalid-input) before any request. - Keep the ids of the orders the page submits (the
submittedevent). The order history is not open to a project id, so list those ids yourself. - Tracking and refunds work as on a server. The chain facts, the open flow’s lifecycle view and
the refund routes are open to a project id, so
executeQuotefollows an order to its end, thefilledevent included, and the page can refund an order itself (History and refunds). - It names the visitor. The hub requires
X-Infinity-Subjecton the quotes, order submission andGET /order/{orderId}/status, so the transport sends one random id per browser and project there:visitor-and a UUID, kept inlocalStorage(for the life of the transport when storage is unavailable). Each visitor’s quotes and statuses stay apart, and a reload keeps the same id. Asubjecton the transport, the client or a call sends yours instead. The project id is public, so anyone can send any subject: it labels a visitor and proves nothing. Scope your signed-in users from your server (Authentication — your users). - The transport sends
x-project-id,Content-Typeon a POST, the subject where the hub requires it and the standardAccept, nothing more: the gateway sets the rest. It refuses a secret key inprojectId, with a message that says so. - The gateway limits each visitor IP, plus a pool shared by every visitor of your project; a
429is aHubErrorwhoseretryAfterMssays how long to wait. Its timeouts are 25 s on quotes, 12 s on reads and 15 s on submissions, a little longer than the gateway’s own.
The user’s wallets
window.ethereum belongs to whichever extension loaded last: with MetaMask and Phantom both
installed, it may not be the wallet your user wants. List the installed wallets instead, each of
which announces itself (EIP-6963), and pass the provider
of the one they pick:
A bridge screen, step by step
1
Pickers from the catalog
await client.getCatalog() returns a CatalogIndex: chains, their assets, which assets can be a
source (canBeSource), which are native (isNativeSource), and the token-in chains
(acceptsTokenIn). It is cached for 5 minutes, and readable before your user signs in: through
your backend’s proxy, or with your project id.2
Estimates while the user types (optional)
client.previewQuotes(request) returns non-binding offers per provider, and nothing to sign. An
empty offers array is a normal answer: no provider takes this route or amount right now.3
A quote to review
client.getQuote(request) returns a Quote. Show the user:- what they pay:
quote.payAmount(a ceiling whenquote.payAmountIsCeiling, on the token-in lane); - what the recipient gets:
quote.amountOut, a floor (a fill usually pays more, as the order is an auction); - the time left:
quoteTimeLeftMs(quote), a client-clock countdown from when this quote was first received; - the checks:
await client.vetQuote(quote), one row per check, all of which must pass.
4
Execute
ReviewRequiredError with both quotes: show the new one and ask again.5
Show the outcome
A
filled event means the chain shows the recipient paid, usually seconds after submission.
result.outcome is success once the order has settled. See
Executing orders for every event.History and refunds
Through your backend,client.listAllOrders() pages through the user’s orders (your backend scopes
them to the user, by its records or by the user’s subject), and client.summarizeOrder(entry) turns each into a row with readable sides and a
phase. A row whose order was never filled offers a refund once the order’s deadline has passed: see
Refunds.
With a project id, the history needs your backend, but refunds do not: show the orders this browser
submitted, from the ids it kept, follow each with trackOrder, and refund one from the page with
refundEvmOrder or refundSolanaOrder.
A complete example
The SDK repository’sexamples/bridge-demo is a bridge page built this way; a hosted copy runs at
demo.infinimesh.net. It has two views, switched from its header (and
remembered; ?face=dev or ?face=simple in a link picks one). Both share one connection, the
wallets and the browser’s order list:
- Simple, a swap page with the essentials in the c8ntinuum look: the two tokens and the amount, a quote that refreshes on its own, one button that says what to do next, the route as the order moves (signed, submitted, filled), and your orders with their state and a refund button. It connects with your project id.
- Dev, the walk-through: a Connection card for your project id (App ID, straight to the
gateway) or your proxy server (
examples/proxy-server), the step-by-step timeline, the check table and the raw wire calls.