Skip to Content
Symmio Trading-SDK — the SDK surface for builders on Arbitrum
CorePoolsauthenticateListing

Listing auth (SIWE)

Everything a user owns on the listing backend — their pools, their stake, their revenue, and the create/withdraw/claim actions — is gated by a Bearer token, not by a wallet filter. This page mints that token. It is a Sign-In With Ethereum (EIP-4361) exchange: fetch a challenge, sign it with the wallet, hand the signature back, and receive the token.

This slice only mints the token and hands it to you — it does not store it or attach it to later requests. Where the token lives (memory, context, storage) and how authed pool reads/writes pick it up lands in a later slice.

The flow is three steps, and authenticateListing runs all three for you:

  1. GET /v2/auth/sign-in-message → an EIP-4361 message string plus its structured params.
  2. sign the message string with the wallet (personal_sign).
  3. POST /v2/auth/login with the params + signature → { accessToken, tokenType }.

There is no separate nonce round-trip — the sign-in message already embeds a single-use nonce.

Every function here resolves the listing backend through resolveListingService, so listing is chain-level: a chain without a listing backend throws LISTING_NOT_CONFIGURED before any network call.

authenticateListing

The whole flow in one call. Resolves the wallet client from the config, signs with its account, and returns the token.

import { authenticateListing } from "@symmio/trading-core"; const { accessToken, tokenType } = await authenticateListing(config, { domain: "app.example.com", // the SIWE DNS authority — no scheme uri: "https://app.example.com", // the requesting dApp URI }); // accessToken is the "access code": send it as `Authorization: Bearer <accessToken>`.

The signing address is the wallet account’s own address — it is read from config.getWalletClient(), not passed in. Point from at a specific signer when the config resolves more than one (e.g. a session key vs. the connected wallet).

Parameters

domainstring

RFC 4501 DNS authority requesting the sign-in, e.g. app.example.com. No scheme, no path.

uristring

RFC 3986 URI of the requesting dApp, e.g. https://app.example.com.

statementstringoptional

Human-readable ASCII line shown inside the signed message.

fromAddressoptional

Signer hint passed to getWalletClient when the config can resolve more than one signer.

chainIdnumberoptional

Target chain id. Defaults to the config’s defaultChainId.

Exported as AuthenticateListingParameters.

Returns

ListingAuthToken
accessTokenstring

The Bearer access token — the value to send as Authorization: Bearer <accessToken> on authed listing calls.

tokenTypestring

The token scheme, e.g. "bearer".

Throws

  • LISTING_NOT_CONFIGURED — a SymmError (kind: "config") from resolveListingService, before any request, when the chain has no listing backend configured.
  • LISTING_LOGIN_FAILED — a SymmApiError when /v2/auth/login rejects the signature or errors.
  • The wallet’s own rejection (viem UserRejectedRequestError) passes through unwrapped when the user declines the signature.

As a mutation

For TanStack, authenticateListingMutationOptions wraps it:

import { authenticateListingMutationOptions } from "@symmio/trading-core"; import { useMutation } from "@tanstack/react-query"; const login = useMutation(authenticateListingMutationOptions(config)); login.mutate({ domain: "app.example.com", uri: "https://app.example.com" });

In React, reach for useAuthenticateListing instead — it fills domain / uri from the current origin.

getListingSignInMessage

Step 1 on its own, for callers that sign the message themselves (a custom signer, a hardware flow, a server relay). Returns the challenge; you sign message and post it to login yourself.

import { getListingSignInMessage } from "@symmio/trading-core"; const { message, params } = await getListingSignInMessage(config, { address: "0xUser…", domain: "app.example.com", uri: "https://app.example.com", });

Parameters

addressAddress

The address that will sign (EIP-55 checksum).

domainstring

RFC 4501 DNS authority requesting the sign-in.

uristring

RFC 3986 URI of the requesting dApp.

statementstringoptional

Optional human-readable statement shown in the message.

chainIdnumberoptional

Target chain id. Defaults to the config’s defaultChainId.

Exported as GetListingSignInMessageParameters.

Returns

ListingSignInMessage
messagestring

The full EIP-4361 message string to sign.

paramsListingSiweParams

The structured SIWE fields behind message (domain, address, uri, version, chainId, issuedAt, nonce, statement) — passed back verbatim on login.

  • useAuthenticateListing — the React hook, with domain / uri defaulted to the current origin.
  • resolveListingService — the availability gate every auth call runs first.
  • Pools — the slice overview.
  • Errors — the SymmError / SymmApiError hierarchy these codes belong to.
Last updated on