This changelog covers the
@coinlist-co/react SDK.July 27, 2026 — Fix token-sale participation amount sent in the wrong units
Fixed
Token-sale participations recorded the amount in the wrong units
coinlist.tokenSale.executeTokenSale recorded the participation with the raw base-unit amount (e.g. "25000000" for 25 USDC) instead of a decimal token amount ("25"). The participations API rescales the amount by the asset’s decimals to check it against the on-chain approval allowance, so the base-unit value overflowed the approved allowance — the backend rejected every participation and funds failed to commit. executeTokenSale now records the decimal amount, so participations go through as expected.If you drive the lower-level coinlist.tokenSale.createParticipation yourself, pass amount as a decimal token amount (e.g. "100" for 100 USDC), not raw base units. This has always been what the backend expects; only the CreateParticipationParams.amount doc comment was wrong, and it is now corrected.July 24, 2026 — Token-sale flow, erc20/tokenSale namespaces, and stricter offer models
Breaking Changes
Participation methods moved to the tokenSale namespace
The flat participation methods on CoinListClient and CoinListServer now live under coinlist.tokenSale, mirroring how swaps sit under coinlist.swap.Migration: insert the
tokenSale namespace before the method name. The parameters and return types are unchanged.ERC-20 reads moved from swap to a new erc20 namespace
getTokenAllowance and getTokenBalance are generic ERC-20 reads shared across the swap and token-sale flows, so they moved off coinlist.swap onto coinlist.erc20.Migration: replace
swap.getTokenAllowance with erc20.getTokenAllowance and swap.getTokenBalance with erc20.getTokenBalance. The endpoints and parameters are unchanged.Offer.endsAt and OfferDetail.endsAt are now nullable
An offer can legitimately have no end date, so endsAt is now Date | null (was Date). fromDto maps a missing end date to null.Migration: handle the null case when reading endsAt.Added
Token-sale flow
A newtokenSale namespace encapsulates the on-chain invest flow, mirroring executeSwap. On the client, coinlist.tokenSale.executeTokenSale() runs a sale end-to-end against a connected EvmWallet:approve() for the sale amount (resetting a stale non-zero allowance to 0 first for USDT-style tokens), waits for it to mine, and records the participation with CoinList. Wallet connection and chain switching remain the host’s responsibility. See Build the invest flow for the end-to-end integration.An
approve() is submitted even when the existing allowance already covers the amount: the backend requires a fresh approval transaction hash on every participation and verifies it on-chain before confirming.allowance-reset / allowance-reset-reverted versus approval / approval-reverted) so you can tell which of the two wallet prompts the user rejected, and the approval hash is surfaced on a backend failure so recording can be retried without re-approving on-chain. New types: ExecuteTokenSaleParams, TokenSaleExecutionPhase, TokenSaleExecutionError, TokenSaleExecutionResult, and the shared Erc20ApprovalError (reused across the swap and token-sale flows).erc20 namespace
coinlist.erc20 exposes the generic ERC-20 reads shared across on-chain flows:Offer type
Offer and OfferDetail now expose a type field (OfferType, one of 'sale' or 'swap') so you can distinguish token-sale offers from swap offers. OfferType is exported from @coinlist-co/react/shared.Changed
Stricter offer models
Fields the backend now guarantees as non-null are typed as required (string instead of string | null):Offer:tagline,bannerUrl,logoUrl.OfferDetail:tagline,bannerUrl,logoUrl,category.
OfferDetail.about stays nullable. OfferCard now hides its “Ends” row when an offer has no end date.July 20, 2026 — Swap namespace, external-wallet connect, and KYC/tax/PII flows
This release folds in the 0.7.0 and 0.8.0 development versions.Shared (
The provider is now an alias of
Added
Swap namespace
client.swap and server.swap wrap the swap, token, and wallet endpoints:On the client, the namespace also drives a caller-supplied wallet:
authorizeWallet({ wallet, offerId, contractAddress, chain, onProgress })— proves wallet ownership and allow-lists it for a swap offer, with typed progress phases and asuccess/errorresult. Idempotent: returns success immediately if the wallet is already authorized.executeSwap({ wallet, contractAddress, chain, inputTokenAddress, quote, slippageBps, onProgress })— runs the full on-chain swap: status check, ERC-20 approval (with USDT-style allowance reset), swap submission, and confirmation. The confirmed output is decoded from theSwappedevent.
viem ^2 is a new peer dependency.Swap hooks
useSwapOutputToken— reads the swap contract’s output token (the fund share).useSwapQuote— polls a live quote (15s default), skipping until the output-token decimals and a valid amount are known.useSwapTokenBalances— polls the user’s balance for one or more input assets.
Wallet abstraction
EvmSigner / EvmWallet interfaces plus typed WalletError classification — bring your own wallet stack (wagmi, viem, Privy, …). New phase and error types: WalletAuthorizationPhase, WalletAuthorizationError, SwapExecutionPhase, SwapExecutionError, SwapExecutionResult.See Build the swap flow for the end-to-end integration.External wallet connect
Prove and bind an external wallet to an offer option:createWalletOwnershipChallenge(params)→POST /v1/wallet-ownershipconnectExternalWallet(offerId, params)→POST /v1/offers/{offer_id}/addresseslistOptionAddresses(offerId, offerOptionId)→GET /v1/offers/{offer_id}/addressesremoveOptionAddress(offerId, addressId)→DELETE /v1/offers/{offer_id}/addresses/{id}
ConnectWalletModal and ConnectedWalletList (list, change, and remove bound wallets), with the useConnectWallet and useOptionAddresses hooks.Identity, KYC, and tax documents
createKycToken(levelName?, reset?)→POST /v1/kyc-token(Sumsub)fetchPii()→GET /v1/pii, pre-fill data for tax formssubmitDocument(documentType, fields)→POST /v1/documents/{document_type}/submission- Components
IdentityVerificationandTaxDocumentModal; hooksuseKycTokenanduseTaxDocument. handleRequirementnow resolves thekyc_approved,identity_verified,proof_of_address,source_of_funds,accreditation,external_wallet,whitelisted_wallet,document, andjurisdictionrequirement types.
identity_verified, proof_of_address, and source_of_funds have since been removed from the API. The SDK still handles them, but offers no longer return them, so you do not need to write branches for those three.App-level authentication
CoinListServer.clientCredentialsOAuth() performs the OAuth 2.0 client_credentials grant for app-level access without a user session. The server offer reads (fetchOffers, fetchOffersPage, fetchOfferDetails, fetchOfferRequirements) accept an optional clientCreds argument to use it.Shared (@coinlist-co/react/shared)
BlockchainAmount(withadd/sub),parseBlockchainAmount,formatAmount,computeSlip, and theBpsnewtype.- Constants and helpers:
DEFAULT_SLIPPAGE_BPS,SLIPPAGE_OPTIONS_BPS,SWAP_POLL_INTERVAL_MS,SUPERSTATE_SWAP_CONTRACT_ADDRESS_SEPOLIA,TOKEN_REGISTRY,USDC_SYMBOL,txExplorerUrl. - Swap, KYC, PII, document-submission, and wallet-ownership types.
Changed
CoinListProvider is safe to mount app-wide
The provider is now an alias of CoinListContextProvider and renders no DOM or styles. Components self-scope their styling through CoinListStyleScope, so mounting the provider high in your tree no longer affects the rest of the app.July 2, 2026 — Required approvalTransactionHash, stricter wallet type, CSS isolation
Breaking Changes
approvalTransactionHash is required on createParticipation
CreateParticipationParams now requires approvalTransactionHash — the hash of the ERC-20 allowance transaction that precedes the participation. Submit the approval on-chain first, then pass its hash.WalletAddress narrowed to `0x${string}`
WalletAddress is now the template-literal type `0x${string}` instead of string. Values that aren’t 0x-prefixed literals need validation or a cast at the boundary.Added
CSS isolation
SDK styles are isolated behind a prefixed Tailwind build with scoped provider injection, and context is separated from styling so the context functions can be used without pulling in SDK styles. This work completes in v0.9.0, whereCoinListProvider becomes safe to mount app-wide.coinlist.co support
Added first-class support for the coinlist.co environment.May 7, 2026 — Read-only SessionStore support
Breaking Changes
SessionStore.setSession is now optional
setSession has been changed from a required method to an optional one (setSession?). This formalises read-only store mode for execution contexts — such as Next.js Server Components — that can read cookies but cannot write them.Previously the only workaround was a no-op setSession: async () => {}, which silently discarded refreshed sessions after consuming the refresh token over the network, effectively causing a silent logout. Omitting setSession is now the explicit, safe contract: the SDK skips token refresh entirely, making no network calls and consuming no refresh tokens.Migration: if you currently pass a no-op setSession, remove it. If your store is writable, no change is needed.Added
WritableSessionStoreRequiredError
New error class exported from @coinlist-co/react/server. completeOAuth() and logout() throw this immediately when called on a read-only store (no setSession), before any network call is made.Fixed
No unnecessary network retries on expired tokens in read-only mode
Previously, when a request returned a 401 and the store was read-only, the SDK would retry the request with the same expired token — wasting a round-trip that always failed. The SDK now detects the read-only store and surfaces the 401 immediately without retrying.May 5, 2026 — API consistency, new /shared entry point, and Base* component removal
Breaking Changes
Client methods renamed: fetchAll* → fetch*
fetchAllOffers and fetchAllParticipations have been renamed on both CoinListClient and CoinListServer to drop the redundant All prefix.Migration: do a global find-and-replace in your codebase:
Hooks renamed: useCoinList* → use*
The CoinList infix has been dropped from all hook names and their associated option/result/reason types.Migration: rename the hook calls and any imported types. Example:
SSR prop renamed: serverOffers / serverData → data
The SSR pre-fetch prop has been unified to data across all hooks and components that accept server-side data. The RequirementsServerData type is also renamed to RequirementsData.Migration: rename the prop/option to
data wherever you pass pre-fetched server results.LoadRequirementsState CONTENT shape: requirementsByOptionId → requirements
If you read the CONTENT state returned by useRequirements (or previously useCoinListRequirements) directly, the field name has changed.Base* components removed
BaseOffersGrid, BaseOffersGridProps, BaseRequirementsChecklist, and BaseRequirementsChecklistProps have been removed. The connected components (OffersGrid, RequirementsChecklist) now accept all the same customization props directly — including the optional data prop for pre-fetched server data — so there is no longer a need for a separate base variant.Migration: replace BaseOffersGrid and BaseRequirementsChecklist usage with the main components and pass the same props directly.Import paths restructured — new @coinlist-co/react/shared entry point
Sub-path exports (/client, /client/hooks, /client/components, /client/core) have been removed. The package now exposes three canonical entry points:Domain types that were previously re-exported from both
@coinlist-co/react and @coinlist-co/react/server are now the sole responsibility of @coinlist-co/react/shared. They remain re-exported from the client and server entry points as well, so most imports will continue to work without changes. However, if you were importing from the now-deleted sub-paths, update your imports:OffersGridProps type renamed from Props
The exported type for OffersGrid props was the generic name Props. It is now exported as OffersGridProps.April 30, 2026 — SSR data fetching, useParticipations hook, and requirement action defaults
Added
Server (@coinlist-co/react/server)
CoinListServernow exposes the full data-fetching surface previously only available onCoinListClient:fetchAllOffers(),fetchOffersPage(),fetchOfferDetails(),fetchAllParticipations(),fetchParticipationsPage(),fetchParticipation(),createParticipation(),fetchOfferRequirements(), andfetchRequirementStatuses(). Use these in Next.js Route Handlers and Server Components without shipping any client bundle.@coinlist-co/react/servernow re-exports all shared types —Offer,OfferDetail,Participation,Requirement,RequirementStatusInfo, pagination helpers,NotAuthenticatedError, and related types — so you no longer need to import them from the client entry.
Client (@coinlist-co/react, @coinlist-co/react/client)
useParticipations(offerId?)hook — loads all participations for the authenticated user with aLOADING / CONTENT / ERRORstate machine, optionally filtered by offer. New exported types:UseParticipationsResult,LoadParticipationsState,LoadParticipationsReason.handleRequirement(requirement)onCoinListClient— opens the corresponding CoinList page for completing a requirement in a new tab (/verify-identity,/wallet, etc.). No-op forjurisdictionrequirements.contactSupport()onCoinListClient— opens the CoinList support ticket page in a new tab.
Changed
Components
RequirementsChecklist:onRequirementActionandonContactSupportprops have been renamed toonRequirementActionOverrideandonContactSupportOverride. Both now default toCoinListClient#handleRequirementandCoinListClient#contactSupportrespectively, so most integrations can omit them entirely. Passnullto disable the corresponding button/link.RequirementItem:onActionandonContactSupportnow acceptnull(in addition toundefined) to suppress rendering of action buttons.
Removed
Client (@coinlist-co/react, @coinlist-co/react/client)
sandboxoption removed fromClientConfig. Sandbox offers are no longer toggled via the SDK config.StaticRequirementsChecklistPropstype removed. UseRequirementsChecklistwith the connected API orBaseRequirementsChecklistfor fully custom rendering.
April 23, 2026 — Requirements API, sandbox mode, and participations filtering
Added
Client (@coinlist-co/react, @coinlist-co/react/client)
fetchOfferRequirements(offerId)onCoinListClient— returns requirements grouped by option ID (Record<OfferOptionId, Requirement[]>).fetchRequirementStatuses(offerId)onCoinListClient— returns the authenticated user’s status for each requirement.useCoinListRequirements(offerId)hook for loading requirements and statuses with aLOADING / CONTENT / ERRORstate machine.sandboxoption onClientConfig— whentrue, passessandbox=trueto offers API calls so sandbox offers are included.- New exported types:
Requirement,RequirementId,RequirementType,RequirementStatusValue,RequirementStatusInfo,ParticipationsPaginationParams. - New exported enums/constants:
RequirementVariant,RequirementStatus,ChecklistStatus. - New hook types:
UseCoinListRequirementsResult,LoadRequirementsState,LoadRequirementsReason. fetchAllParticipations(offerId?)andfetchParticipationsPage(params)now accept an optionalofferIdto fetch participations for a specific offer.fetchParticipationsPagetakes the newParticipationsPaginationParamstype (superset ofPaginationParams) which carries theofferIdfield.
Fixed
Components
- Fixed missing dark mode CSS variables in Next.js projects — prebuilt styles now include the full set of design-system tokens so components render correctly under
darkclass orprefers-color-scheme: dark.
April 13, 2026 — Participations API
Added
Client (@coinlist-co/react, @coinlist-co/react/client)
fetchAllParticipations()onCoinListClientfor fetching all participations across pages.fetchParticipationsPage()onCoinListClientfor paginated participation fetching.fetchParticipation()onCoinListClientfor fetching a single participation by id.createParticipation()onCoinListClientfor creating a new participation.- New exported types:
Participation,ParticipationId,ParticipationStatus,CreateParticipationParams,Blockchain,WalletAddress.
April 8, 2026 — Support UI component customization
Added
Client (@coinlist-co/react, @coinlist-co/react/client)
- Added support for passing
classNameandcontainerClassNameto<OffersGrid />,<OfferCard />, and<CoinListSignInCard />.
April 7, 2026 — Publish missing exports
Fixed
Client (@coinlist-co/react, @coinlist-co/react/client)
- Re-export offer, offer detail, and pagination types from the package root and client core entry so you can type against
Offer,OfferDetail, pagination params, and related helpers without reaching into internal modules. - Export
BaseCoinListSignInCardandBaseCoinListSignInCardPropsfrom@coinlist-co/react/clientfor custom sign-in layouts built on the same primitives asCoinListSignInCard.
April 2026 — Offers UI and OAuth helpers
Added
Client (@coinlist-co/react/client)
fetchAllOffers(),fetchOffersPage(), andfetchOfferDetails()onCoinListClientfor fetching offers data.<OffersGrid />— batteries-included component that loads offers and displays them in a responsive grid, with<OfferCard />for each tile.useCoinListOffers()hook for loading offers when you want full control over layout instead of the grid.useCoinListOfferDetails()hook for a single offer’s details.useCompleteCoinListOAuth()hook to run the OAuth redirect callback once on mount.- Optional
authorizationPageUrlon client config so you can pointstartOAuth()at a non-production authorization page.
March 2026 — Initial release
Added
Client (@coinlist-co/react/client)
CoinListProvideranduseCoinList()hook for React context-based SDK initialization.createCoinListClient(config)factory for manual or non-React usage.- OAuth 2.0 with PKCE via
startOAuth()andcompleteOAuth(). getAuthState()andlogout().
Server (@coinlist-co/react/server)
createCoinListServer(config)factory for BFF / Next.js API routes.completeOAuth(),accessToken()(auto-refreshing), andlogout().
Components
CoinListSignInCardandCoinListSignInButtonfor OAuth sign-in flows.RequirementsChecklist/RequirementItem— eligibility checklist with accordion UI.PoweredByCoinListattribution badge.- Prebuilt CSS — no
tailwindcsspeer dependency required.
Infrastructure
- Subpath exports:
@coinlist-co/react,@coinlist-co/react/client,@coinlist-co/react/server. - Requires React 18+.