AddLiquidityCard

The supply composer — two legs paired at the live pool ratio, each approval named separately, and a refusal to quote on a venue whose addLiquidity signature has not been verified on chain.

import { AddLiquidityCard } from '@flarekit-dev/react-ui'

AddLiquidityCard supplies two assets to a V2 pool. You enter one amount; the other is read from the live reserves, because a supply that is not at the pool ratio leaves a silent excess. It is a position, not a deposit, and the card says so in those words — the split between the two assets changes as the price moves.

Live#

The preview runs the gallery's own states. Each record is built by walking the real add-liquidity state machine over fixture pool readings, so the switcher only offers states the card actually reaches.

gallery states

Add liquidity

Coston2
You supply
Balance 247.500000
And
Balance 12.340000
Paired at pool ratio1.176000 USD₮0
Expected pool share0.40%
Expected LP1.080000000000000000 BLAZE-LP
Minimum supplied0.995000 FXRP · 1.170120 USD₮0 Protected at 0.50% — the ratio can drift before the tx confirms.
A position, not a deposit
The two amounts are paired at the live pool ratio; their split changes as the price moves.

Usage#

The card is prop-driven and holds no wallet client. You quote, read the two allowances, apply the result to the operation, and hand it the record; onSubmit hands the decision back to you.

import {
  applyAddQuote,
  createAddLiquidity,
  quoteAddLiquidity,
  readAllowance,
  startQuoting,
} from '@flarekit-dev/core'
import { AddLiquidityCard } from '@flarekit-dev/react-ui'
import '@flarekit-dev/react-ui/styles.css'

const CHAIN = 114 // Coston2

export async function quoteSupply(reader, owner, now = Date.now()) {
  const intent = {
    tokenAKey: 'FXRP',
    tokenBKey: 'USDT0',
    amountADesired: 1_000000n,
    slippageBips: 50,
    recipient: owner,
    deadline: Math.floor(now / 1000) + 1200,
  }
  const [result, allowanceA, allowanceB] = await Promise.all([
    quoteAddLiquidity({ reader, chainId: CHAIN, tokenAKey: 'FXRP', tokenBKey: 'USDT0', amountADesired: intent.amountADesired, slippageBips: 50, now }),
    readAllowance(reader, CHAIN, 'FXRP', owner),
    readAllowance(reader, CHAIN, 'USDT0', owner),
  ])
  const quoting = startQuoting(createAddLiquidity({ chainId: CHAIN, intent, now }), now).record
  return { operation: applyAddQuote(quoting, { result, allowanceA, allowanceB, now }).record, result }
}

Props#

PropTypeDefaultDescription
operationrequiredAddLiquidityOperationThe add-liquidity record. Its state drives the CTA, the note and whether the spine appears; its quote drives the terms.
tokenArequiredDexTokenThe token whose amount you enter. Symbol, address and decimals, from `@flarekit-dev/contracts`.
tokenBrequiredDexTokenThe paired token. Its amount is never entered — it comes from the quote, at the pool ratio.
quoteResultAddLiquidityQuoteResultThe raw quote outcome: a quote, `no_pool`, or `unavailable` with its reason. Passing it is what lets the card state why there is nothing to supply instead of showing an empty form.
amountATextstringThe controlled text of the entry field, when the host owns the input. Left unset, the field shows the quoted amount for tokenA.
balanceAAmountThe signer's tokenA balance. Supplied, it enables the insufficient-balance block; omitted, the card does not guess and does not warn.
balanceBAmountThe signer's tokenB balance, on the same terms as balanceA.
mockLabelstringNames the mock driving the card, which renders a mock note at the top. Mock mode is explicit and labelled — never a fallback the card enters on its own.
networkLabelstringThe network shown in the panel subtitle, e.g. `Coston2`.
onAmountAChange(text: string) => voidCalled as the tokenA amount is typed. The field is editable only before a plan exists.
onSelectA() => voidCalled when the reader opens token selection for the first leg.
onSelectB() => voidCalled when the reader opens token selection for the paired leg.
onMax() => voidCalled when the reader takes the max affordance on the tokenA leg.
onSubmit() => voidCalled when the reader takes the CTA. The card signs nothing and broadcasts nothing — the host owns the transaction.
theme'light' | 'dark'Overrides the inherited theme. Normally left unset — the widget follows data-theme.
classNamestringExtra class on the outer element, so a host layout can place the card.

What it renders#

One panel: the two supply legs, then the terms — Paired at pool ratio, Expected pool share, Expected LP and Minimum supplied, each in the mono face with its asset and full precision. The minimums carry the slippage they are protected at, because the ratio can drift between quote and confirmation.

Below the terms sits the note for the current state, and once a transaction is genuinely in flight, the OperationTimeline spine. The quote block disappears the moment the operation concludes: a pre-execution estimate is not the amount that moved, so it is never left on screen next to a result.

States#

Every state in the switcher above is imported from packages/react-ui/gallery/, one source of truth for both the gallery and these docs:

  • quote — the paired amount, the expected LP and the resulting pool share, with the note that this is a position rather than a deposit.
  • needs approval, token A short — the approval is its own transaction, and it is named for the token that actually needs it.
  • needs approval, token B short — the same, for the other leg. When both allowances are short both tokens are named; one approval is never hidden inside the other.
  • approving — the approval transaction is in flight, and the spine shows it as the active step.
  • adding — the approval is done and the add transaction is submitted. The approval hash is on the spine as evidence; the add is submitted, not succeeded.
  • successLiquidity added, with both the approval and the add transaction as evidence.
  • no pool — the pair has no pool on this network. Stated with its reason, never rendered as a pool holding zero.
  • unverified venue — the network's router uses an addLiquidity signature this build has not verified on chain, so the card refuses to quote rather than emit a plan that would revert after you had already approved real tokens.
  • ratio exceeded — the pool moved past your minimums before the add confirmed. Nothing moved, so it is offered as re-quotable, and it is not presented as a kit failure.
  • insufficient balance — the supply is blocked and the short asset is named.

Mock to live#

The card takes a record and a quote result; it never reaches for a network itself. Whatever produces them — a mock or a live reader — the component is the same, so moving to a live network swaps what feeds it, not the screen. Router, factory and token addresses come from @flarekit-dev/contracts; network is configuration.

// From this…
<FlareProvider kit={createMockKit({ seed: 'demo' })}>

// …to this. The component does not change.
<FlareProvider kit={createFlareKit({ network: 'coston2', signer })}>

What it will not do#

It will not quote on a venue whose addLiquidity signature has not been verified on chain — Coston2's BlazeSwap router takes non-standard feeBipsA/feeBipsB parameters, which the real-first run found before any mock copied the standard shape, and a router that expects the standard signature would revert against the calldata this kit builds. It will not present a submitted add as a completed one, it will not fold two approvals into one button, and it will not render a missing pool as a pool with nothing in it.