TradingClient provides liquidity to the Uniswap V3 pools on Flashnet Execution. Mint a position, grow or shrink it, collect fees, or reposition, each as one signed intent that funds from your Spark balance and routes through the Conductor.
These are the Uniswap V3 pools the Conductor wraps on Execution, managed with
TradingClient. The Spark-native AMM under Markets is a separate system with its own client.Configure for liquidity
LP needs more contract addresses than swaps. OnTradingConfig:
| Field | When required |
|---|---|
positionManagerAddress | every LP method |
wbtcAddress | BTC-paired pools |
permit2Address | funding from Spark (useAvailableBalance) |
lpGasLimit is optional.
new TradingClient(execClient, "regtest") includes all of these, so you can skip the explicit config.
How positions work
A position is a Uniswap V3 concentrated-liquidity NFT held by the NonfungiblePositionManager, identified by atokenId and bounded by two ticks. Four conventions carry across every method:
- Pass pair tokens sorted:
token0 < token1by lowercase address. - The WBTC leg is denominated in wei, not sats (1 sat = 1e10 wei). Convert with
satsToWeiandweiToSatsfrom@flashnet/sdk. - For a BTC-paired pool, use the WBTC address as the BTC leg’s token. The SDK routes to the Conductor’s BTC entry point and funds
msg.value. - Ticks are standard Uniswap V3 ticks, where price = 1.0001^tick.
V3TickMathfrom@flashnet/sdkconverts between prices and ticks.
Funding
LP methods fund the same two ways as swaps. WithuseAvailableBalance: true, the SDK pulls each leg from your Spark balance and bundles the funding transfers with the Conductor call in one intent. Supply the Spark token id for each ERC20 leg (token0SparkId, token1SparkId); the BTC leg funds from the WBTC slot.
ERC20 legs are pulled via Permit2, so each token needs a standing Permit2 approval from your EVM address first:
useAvailableBalance, the methods spend balances already on the Execution side.
Mint a position
addLiquidity mints a new position NFT.
msg.value and takes no Spark token id.
Grow or shrink
increaseLiquidity adds to a position. decreaseLiquidity removes liquidity and withdraws the recovered tokens to Spark.
decreaseLiquidity sends the recovered token0 and token1 to your Spark wallet. sparkRecipient defaults to your identity key.
Collect fees
collectFees sweeps accrued fees to Spark without touching principal.
Reposition
modifyPosition moves a position to a new tick range. It removes all liquidity from the current position, optionally adds capital, and mints a new NFT at the new range.
Read positions
getPosition and listPositions read straight from the NonfungiblePositionManager.
PositionInfo mirrors the NonfungiblePositionManager’s positions() tuple: token0, token1, fee, tickLower, tickUpper, liquidity, tokensOwed0/tokensOwed1, and the fee-growth checkpoints.
What the result tells you
tokenId, the liquidity added, amounts used, and fees collected are knowable only after the intent executes. Read them from the transaction receipt or the Conductor LP events (LiquidityAdded, LiquidityIncreased, LiquidityDecreased, FeesCollected, PositionModified) once the intent reaches INCLUDED_PENDING_FINALITY.
Fund recovery
When anaddLiquidity, increaseLiquidity, or modifyPosition call with useAvailableBalance fails after its funding transfers commit, the SDK claws the committed transfers back to your Spark balance and throws StrandedFundingError with a clawbackSummary of what was recovered. This matches the swap round-trip’s behavior. decreaseLiquidity and collectFees have no inbound funding, so they carry no clawback.