Skip to content

feat(adapter-integration): add BopAMM swap adapter#1082

Closed
tvinagre wants to merge 2 commits into
mainfrom
tl/bopamm-adapter
Closed

feat(adapter-integration): add BopAMM swap adapter#1082
tvinagre wants to merge 2 commits into
mainfrom
tl/bopamm-adapter

Conversation

@tvinagre

Copy link
Copy Markdown
Collaborator

What

Sell-side ISwapAdapter for BopAMM (Bebop's on-chain PMM), wrapping the BopAmmV2 settlement contract (0xdB13ad0fcD134E9c48f2fDaEa8f6751a0F5349ca), plus manifest and Foundry fork tests.

  • Pool id packs settlement (20 bytes) | assetId (12 bytes), matching the vm:bopamm substreams component ids (feat(substreams): add BopAMM VM integration #1081).
  • price/getLimits use the settlement's quote() view. quote() reverts InsufficientLiquidity() above the committed lane size, so limits are found by probing + bisection. The maker's inventory is not checked by quote() — limits can overestimate what swap() settles (the interface prefers overestimation, and the operator sizes lanes to inventory).
  • swap() is sell-only: pull from caller, approve settlement, swap with recipient = msg.sender, output measured by balance diff. Buy orders revert NotImplemented — the venue quotes exact-input only.
  • Capabilities: SellOrder, PriceFunction, ConstantPrice (verified constant up to the lane cap), HardLimits.
  • getTokens/getPoolIds enumerate getAssetConfig(0..64) on the pricing module; every book is asset/USDC.

Staleness gate

The update registry reverts StaleUpdate() unless block.timestamp equals the book's committed update timestamp (MAX_UPDATE_AGE == 0). Fork tests re-stamp the committed lane timestamp via vm.store (keccak256(abi.encode(module, bookId)), timestamp in the top 32 bits). At simulation runtime the timestamp is pinned via the override_block_timestamp component attribute (emitted by the substreams in #1081, consumed by tycho-simulation in #1034).

Testing

14/14 fork tests pass against a mainnet fork (ETH_RPC_URL): pool/token enumeration, capabilities, limits in both directions on both books, constant-price check up to the limit, sell swaps both directions with balance assertions, and revert cases (above limit, stale timestamp, paused venue, pool/token mismatch, foreign settlement pool id, buy order). BopAmmV2::swap measures ~120k gas (manifest protocol_gas: 120000).

Depends on #1081 (component id format) and #1034 (override_block_timestamp support) for end-to-end simulation; the files here are self-contained and the tests run on main.

🤖 Generated with Claude Code

Sell-side ISwapAdapter for BopAMM (Bebop's on-chain PMM) wrapping the
BopAmmV2 settlement contract. Pool ids pack settlement (20 bytes) and
assetId (12 bytes), matching the substreams component ids. Limits are
found by bisecting quote(), which reverts InsufficientLiquidity above
the committed lane size. Buy orders revert NotImplemented: the venue
quotes exact-input only.

Quotes are gated on block.timestamp equalling the book's committed
update timestamp (StaleUpdate() otherwise); fork tests re-stamp the
registry lane via vm.store, and simulation pins the timestamp via the
override_block_timestamp component attribute.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The simulation engine deploys the adapter in a context where the
settlement contract's code is injected at call time, not at
construction. The constructor read settlement.pricing()/usdc() eagerly,
so deployment reverted there (the protocol-testing harness caught this;
the fork tests passed only because setUp forks mainnet where settlement
already exists). Resolve both lazily via public view functions instead.

Pin the test fork to a quote-commit block (25266710): the venue is
operator-driven, so forking latest drifts (a third book was added,
quotes go stale) and broke the limit/price/swap assertions. At the
pinned block exactly two books exist and the block timestamp matches
book 0's committed quote.

Verified: adapter fork tests 14/14, and the protocol-testing harness
builds and deploys the adapter and passes test_book_discovery 1/1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tvinagre

Copy link
Copy Markdown
Collaborator Author

Superseded by #1095, which aggregates the substreams, simulation adapter, and execution layers into one branch (reconciled against current main — FermiSwap #1034 had since merged and shares the execution wiring). Closing in favor of that PR.

@tvinagre tvinagre closed this Jun 16, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in Tycho Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants