Sitelet https://github.com/piekstra/lofty-cli/pull/7
Skip to content

Fix: AMM fee rates must include the pool's LP fee - #7

Merged
piekstra merged 1 commit into
mainfrom
fix/amm-fee-includes-lp
Jul 28, 2026
Merged

piekstra merged 1 commit into
mainfrom
fix/amm-fee-includes-lp

Conversation

@piekstra

@piekstra piekstra commented Jul 28, 2026 •

Copy link
Copy Markdown
Owner

The bug

An AMM swap pays the platform fee and the pool's LP fee. The pool reports them as separate lines, so the all-in rate is their sum — but venue_fees took platformBuy/platformSell alone, understating every AMM figure by the LP fee.

For our pools that's 3% instead of 5% on a buy. Consequences:

  • The "acquired via AMM" break-even came out identical to the order-book one (both 3%), so the command's whole point — showing that where you bought changes your true cost — silently collapsed.
  • A recovery ask priced off it sits ~2% too low, i.e. you'd take a "profit" that isn't one.

How it was found

Dogfooding. Running lofty account breakeven --margin 5 against a real account printed:

ACQUIRED VIA | ASK +5.0% | BREAK-EVEN ASK | BUY FEE % | TRUE COST
amm          | $71.37    | $67.97         | 3.00      | $65.59
book         | $71.37    | $67.97         | 3.00      | $65.59

Two identical rows with a 3.00% "AMM" fee is the tell — no unit test would have caught it, because the test fixture asserted whatever the code already did.

Verification — measured, not inferred

Live AMM quotes for the same pool:

side fees.total usdcAmount ratio composition
buy 3.096917 61.938345 5.00% platform 3 + lp 2
sell 3.406609 61.672950 5.52% platform 3.5 + lp 2

This also matches the independent evidence already on record: a 1-token buy with totalSpent $11.90 debited $12.50 — exactly ×1.05.

After the fix, the same command reproduces the hand-derived numbers:

amm          | $57.12    | $54.40         | 5.00      | $52.50
book         | $71.37    | $67.97         | 3.00      | $65.59

Scope

  • The order book keeps its own rates — the LP fee compensates AMM liquidity providers and has no order-book analogue, so mtBuyFeePct/mtSellFeePct stand alone.
  • A pool with no lp line falls back to the platform rate (not a missing rate).
  • A pool with no platform rate is still genuinely unknown and yields no scenario, rather than a wrong one.

Testing

New amm_rates_include_the_pools_lp_fee pins all three cases. The existing pool fixture was also corrected to the real observed shape ({lp: 2, platformBuy: 3, platformSell: 3.5}) instead of invented values — which flipped one assertion honestly: swapping out now costs more than resting an ask, so it needs a higher break-even, not a lower one.

All 60 tests, fmt, and clippy -D warnings clean; re-verified live after installing.

Figures in this description are illustrative, not account data.

An AMM swap pays the platform fee AND the pool's LP fee; the pool reports them as
separate lines, so the all-in rate is their sum. `venue_fees` took `platformBuy`/
`platformSell` alone, understating every AMM figure by the LP fee — for our pools
a buy read 3% instead of 5%, making an "acquired via AMM" break-even identical to
the order-book one and quoting a recovery ask ~2% too low.

Verified against live quotes rather than inferred:

  buy   fees.total 3.096917 / usdcAmount 61.938345 = 5.00%   (platform 3 + lp 2)
  sell  fees.total 3.406609 / usdcAmount 61.672950 = 5.52%   (platform 3.5 + lp 2)

The order book keeps its own rates: the LP fee compensates AMM liquidity
providers and has no order-book analogue.

Found by dogfooding — the output showed both acquisition scenarios with an
identical 3.00% buy fee, which is exactly the tell.

A pool with no `lp` line falls back to the platform rate; a pool with no platform
rate is still genuinely unknown and yields no scenario.

Claude-Session: https://claude.ai/code/session_01J7bz8aijn2Uwv1yF5rUenW
@piekstra
piekstra requested a review from piekstra-dev July 28, 2026 18:38

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: bb82811de2d0
Profile: reviewer - Posting as: piekstra-dev

Summary

Reviewer Findings
documentation:docs 0
policies:conventions 0

Reviewer Coverage

Reviewer Status Inspected Skipped Constraints
documentation:docs complete_broad README.md unavailable unavailable
policies:conventions complete_broad README.md unavailable No sibling cli-common/.github convention docs were present in the review context, so this check relied only on repo-local docs (README.md, AGENTS.md, CONTRIBUTING.md) visible in the workbench.
unassigned incomplete_unassigned unavailable src/commands/account.rs changed files were not assigned to a selected reviewer

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 1m 28s | $1.11 | claude-sonnet-5 | cr 0.10.268
Field Value
Model claude-sonnet-5
Reviewers documentation:docs, policies:conventions
Engine claude_cli · claude-sonnet-5
Reviewed by cr · piekstra-dev
Duration 1m 28s wall · 2m 15s compute
Cost $1.11
Tokens 60 in / 9.4k out

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost Duration
orchestrator-selection claude-sonnet-5 6 1.8k 32.8k 31.4k $0.23 25s
documentation:docs claude-sonnet-5 24 3.4k 378.5k 37.4k $0.39 49s
policies:conventions claude-sonnet-5 24 3.7k 353.3k 35.5k $0.38 49s
orchestrator-rollup claude-sonnet-5 6 478 59.7k 16.1k $0.12 10s

@piekstra
piekstra merged commit 4048c72 into main Jul 28, 2026
2 checks passed
@piekstra
piekstra deleted the fix/amm-fee-includes-lp branch July 28, 2026 18:41
piekstra added a commit that referenced this pull request Jul 28, 2026
Three read-only commands for the questions the raw API can't answer directly,
plus one fee correctness fix:

- `account coverage`   — USDC reserved backing bids vs free to spend (#4)
- `account breakeven`  — fee-inclusive sell price; costBasis hides the buy fee (#5)
- `rewards eligibility`— are my orders earning right now, and if not why (#6)
- fix: AMM rates are platform + the pool's LP fee (#7)

All three verified against a live account before release.

Claude-Session: https://claude.ai/code/session_01J7bz8aijn2Uwv1yF5rUenW
piekstra added a commit that referenced this pull request Jul 30, 2026
An AMM swap pays the platform fee AND the pool's LP fee; the pool reports them as
separate lines, so the all-in rate is their sum. `venue_fees` took `platformBuy`/
`platformSell` alone, understating every AMM figure by the LP fee — for our pools
a buy read 3% instead of 5%, making an "acquired via AMM" break-even identical to
the order-book one and quoting a recovery ask ~2% too low.

Verified against live quotes rather than inferred:

  buy   fees.total 3.096917 / usdcAmount 61.938345 = 5.00%   (platform 3 + lp 2)
  sell  fees.total 3.406609 / usdcAmount 61.672950 = 5.52%   (platform 3.5 + lp 2)

The order book keeps its own rates: the LP fee compensates AMM liquidity
providers and has no order-book analogue.

Found by dogfooding — the output showed both acquisition scenarios with an
identical 3.00% buy fee, which is exactly the tell.

A pool with no `lp` line falls back to the platform rate; a pool with no platform
rate is still genuinely unknown and yields no scenario.
piekstra added a commit that referenced this pull request Jul 30, 2026
Three read-only commands for the questions the raw API can't answer directly,
plus one fee correctness fix:

- `account coverage`   — USDC reserved backing bids vs free to spend (#4)
- `account breakeven`  — fee-inclusive sell price; costBasis hides the buy fee (#5)
- `rewards eligibility`— are my orders earning right now, and if not why (#6)
- fix: AMM rates are platform + the pool's LP fee (#7)

All three verified against a live account before release.
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.

2 participants