Skip to content

feat(lp-sdk): add LP gas-estimation helpers - #642

Open
claude[bot] wants to merge 2 commits into
mainfrom
feat/lp-sdk-gas-estimation
Open

feat(lp-sdk): add LP gas-estimation helpers#642
claude[bot] wants to merge 2 commits into
mainfrom
feat/lp-sdk-gas-estimation

Conversation

@claude

@claude claude Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Requested by Bruno Nunes · Slack thread

Description

Before: when a user taps "Max" on a native-token input in an LP flow, web reserves a hardcoded 0.015 ETH as a gas buffer — too much on cheap chains, potentially too little during fee spikes. A backend RPC stack was drafted to compute a real estimate server-side (#7487, #7490, #7492, #7493, building on #7208), but per the Slack discussion the logic belongs in the SDK so the frontend can estimate directly instead of adding new backend RPCs. This PR supersedes that stack.

After: clients call SDK helpers that return the full ordered transaction list for an LP flow — required ERC-20/Permit2 approvals plus the create/increase position transaction — and run eth_estimateGas over it with their own clients, summing the results into a real gas buffer.

How: this adds a new cross-version package, @uniswap/lp-sdk (per Bruno's "one for liquidity" call in the thread; named to avoid confusion with the existing liquidity-launcher-sdk), following the router-sdk pattern: workspace:* deps on sdk-core + v2/v3/v4-sdk, three-tsconfig tsc build, bun test, changesets. It exports:

  • pickPreEstimateIndependentAmount(balance0, balance1) — pure port of the backend's pickPreEstimateIndependent (#7487) minus the RPC reads: caps each wallet balance at 10^(decimals + 3) raw units, returns the larger side as the independent simulation amount, null when both are zero.
  • getV3AddLiquidityGasEstimateTransactions(params) — approvals to the NonfungiblePositionManager + the mint/increase tx via NonfungiblePositionManager.addCallParameters (mint vs increase selected by tokenId, createPool supported).
  • getV4AddLiquidityGasEstimateTransactions(params) — ERC-20 approvals to Permit2, Permit2.approve grants to the v4 position manager, + the mint/increase tx via V4PositionManager.addCallParameters.
  • getV2AddLiquidityGasEstimateTransactions(params) — approvals to the v2 router + addLiquidity/addLiquidityETH.

Helpers are chain-agnostic and make no network calls: they take pool/pair entities, the picked independent amount, and optional allowance state as inputs, and return { to, calldata, value } tuples. Omitted allowances are treated as zero, so the default output is the pessimistic (all-approvals) estimate. Position transactions reuse the existing SDK calldata builders — the same ones the backend liquidity service calls — so estimates match the transactions users actually sign.

Parity notes vs the backend liquidity service

Places where the helpers intentionally differ from the backend BLs (CreatePositionBL / CreateClassicPositionBL / IncreasePositionBL and buildCheckedApprovalPreCalls):

  • Permit2 expiration / "is permit active": the backend uses wall-clock now + 30d for new Permit2 approvals and wall-clock now for activity checks. SDKs have no clock, so the caller's deadline is used both as the new approval's expiration and as the reference time when deciding whether an existing Permit2 allowance still counts. No material gas difference.
  • USDT-style approve(0) resets: the backend consults a per-chain resetApprovalTokens list; the SDK is chain-agnostic, so callers flag requiresReset per token instead.
  • v4 native effective slippage: the backend's computeEffectiveSlippage (solves the max slippage that fits the user's native balance when the 0.05% default inflates value past it) is not ported — callers can pass an explicit slippageTolerance. Slippage defaults otherwise match the backend: 2.5%, and 0.05% for native v4 pools.
  • v2 encoding: v2-sdk's Router has no add-liquidity builder, so the helper encodes addLiquidity/addLiquidityETH against the canonical V2Router02 ABI — byte-identical to the backend's ethers populateTransaction output.
  • Approval thresholds: v4 compares allowances against the slippage-inflated maximums (mintAmountsWithSlippage) that V4PositionManager may settle; v3 compares against mintAmounts, which is the exact amount0Desired/amount1Desired upper bound the v3 manager pulls (v3's mintAmountsWithSlippage are lower-bound minimums, not maximums). The backend compares against the pre-slippage desired amounts in both cases — the v4 check here is intentionally stricter so a borderline allowance can't drop a required approval from the estimate.
  • Liquidity derivation: the backend derives the dependent amount via Position.fromAmount0/1 and then recomputes liquidity with maxLiquidityForAmounts over both amounts; the helpers call Position.fromAmount0/1 directly. Same math, same result.
  • v4 signing flow: the production signing path may embed a signed Permit2 batch permit inside the position call instead of onchain Permit2 approvals. The helpers model the approval-transaction path — the same choice the backend estimate makes with includeApprovalSimulation preflight calls.
  • Deadline: the backend defaults to now + 30m; the SDK requires a caller-provided deadline.

How Has This Been Tested?

Unit tests (bun test, 25 tests): balance-cap edge cases (both zero → null, cap at 10^(decimals+3), larger-side selection, tie → token0, native balances), approval inclusion/skipping per allowance state, approve(0) resets, Permit2 approval expiry vs deadline, native-side handling (value attached, approval skipped), mint vs increase, borderline allowances between v4's desired and slippage-maximum amounts, and byte-equality of position calldata against NonfungiblePositionManager.addCallParameters / V4PositionManager.addCallParameters / V2Router02 ABI encoding. bun run g:build, g:lint, and g:check:deps:mismatch pass locally.

Are there any breaking changes?

No. New package only — no changes to existing SDKs. @uniswap/lp-sdk starts at 0.x with a minor changeset.

(Optional) Feedback Focus

  • Package naming and placement (@uniswap/lp-sdk under sdks/lp-sdk) — decided in the Slack thread, flagging for visibility.
  • The allowance-input API (TokenAllowanceInput / Permit2AllowanceInput): raw allowances in, helper decides which approvals to include. Alternative was caller-computed booleans.

(Optional) Follow Ups

  • Decrease/collect/migrate estimation helpers if the frontend needs them.
  • Port computeEffectiveSlippage for native v4 "Max" flows if estimate/actual drift matters there.
  • CODEOWNERS entry for sdks/lp-sdk/ (left out to keep this PR reviewable by the default owners; needs a team decision on ownership).

Introduces @uniswap/lp-sdk, a cross-version package (router-sdk pattern)
with helpers that let clients estimate the real gas cost of LP flows
instead of reserving a hardcoded native-token buffer:

- pickPreEstimateIndependentAmount: picks the larger capped wallet
  balance as the independent simulation amount (null when both zero)
- getV2/getV3/getV4AddLiquidityGasEstimateTransactions: build the
  ordered approval + create/increase transaction lists for
  eth_estimateGas, reusing the existing v2/v3/v4 calldata builders

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Bzp1ZmbwPvLbyKSuaRv2X
@claude
claude Bot requested a review from a team as a code owner July 14, 2026 04:35
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

● Reviewed · against 77929e0 · 2026-07-14 04:43 UTC · 2 reviews · view run ↗

Note

Approved.

Adds @uniswap/lp-sdk, a new cross-version package of chain-agnostic helpers that build the ordered transaction list for an LP add-liquidity flow so callers can run eth_estimateGas locally.

Assessment

Self-contained new package with no changes to existing SDKs, so blast radius is limited to consumers of the new helpers. The v3/v4 approval-sufficiency thresholds now match what each position manager actually pulls — v4 checks against mintAmountsWithSlippage, v3 against mintAmounts (the correct maximum, since v3's slippage amounts are lower-bound minimums), with the divergence documented in the parity notes. Structure follows router-sdk.

Iteration history · 2 reviews
2026-07-14 04:43 UTC · ✅ approved · 0 findings · 77929e0 · run ↗

(no findings)

2026-07-14 04:39 UTC · ✅ approved · 2 findings · 6c8ab7b · run ↗
  • sdks/lp-sdk/src/v3.ts:114 — warning · correctness
  • sdks/lp-sdk/src/v4.ts:130 — warning · correctness

Tip

Teach the reviewer. React 👍 on findings that helped, 👎 on false positives. Reply to push back or add context — we aggregate this weekly to tune the bot.

Comment @request-claude-review to re-run.

@graphite-app
graphite-app Bot requested review from a team July 14, 2026 04:38
Comment thread sdks/lp-sdk/src/v3.ts
Comment thread sdks/lp-sdk/src/v4.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Approved — see full review in the sticky comment ↑

@graphite-app

graphite-app Bot commented Jul 14, 2026

Copy link
Copy Markdown

Graphite Automations

"Request reviewers once CI passes on sdks monorepo" took an action on this PR • (07/14/26)

3 reviewers were added to this PR based on Siyu Jiang (See-You John)'s automation.

V4PositionManager settles up to mintAmountsWithSlippage (amount0Max/
amount1Max), so an allowance between the desired amounts and the
maximums must still produce an approval transaction. v3 keeps the
mintAmounts threshold: its manager pulls at most amount0Desired, and
its mintAmountsWithSlippage are lower-bound minimums.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Bzp1ZmbwPvLbyKSuaRv2X
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant