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:
GET /v2/auth/sign-in-message→ an EIP-4361 message string plus its structured params.- sign the message string with the wallet (
personal_sign). POST /v2/auth/loginwith 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
domainstringRFC 4501 DNS authority requesting the sign-in, e.g. app.example.com. No scheme, no path.
uristringRFC 3986 URI of the requesting dApp, e.g. https://app.example.com.
statementstringoptionalHuman-readable ASCII line shown inside the signed message.
fromAddressoptionalSigner hint passed to getWalletClient when the config can resolve more than one signer.
chainIdnumberoptionalTarget chain id. Defaults to the config’s defaultChainId.
Exported as AuthenticateListingParameters.
Returns
ListingAuthTokenaccessTokenstringThe Bearer access token — the value to send as Authorization: Bearer <accessToken> on authed listing calls.
tokenTypestringThe token scheme, e.g. "bearer".
Throws
LISTING_NOT_CONFIGURED— aSymmError(kind: "config") fromresolveListingService, before any request, when the chain has nolistingbackend configured.LISTING_LOGIN_FAILED— aSymmApiErrorwhen/v2/auth/loginrejects 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
addressAddressThe address that will sign (EIP-55 checksum).
domainstringRFC 4501 DNS authority requesting the sign-in.
uristringRFC 3986 URI of the requesting dApp.
statementstringoptionalOptional human-readable statement shown in the message.
chainIdnumberoptionalTarget chain id. Defaults to the config’s defaultChainId.
Exported as GetListingSignInMessageParameters.
Returns
ListingSignInMessagemessagestringThe full EIP-4361 message string to sign.
paramsListingSiweParamsThe structured SIWE fields behind message (domain, address, uri, version, chainId, issuedAt, nonce,
statement) — passed back verbatim on login.
Related
useAuthenticateListing— the React hook, withdomain/uridefaulted to the current origin.resolveListingService— the availability gate every auth call runs first.- Pools — the slice overview.
- Errors — the
SymmError/SymmApiErrorhierarchy these codes belong to.