LambdaClass PropAMM Router: audited version now live

LambdaClass PropAMM Router: audited version now live

The PropAMMRouter, our on-chain router for proprietary AMMs on Ethereum, has completed its first external security audit. Least Authority reviewed the contracts and reported three issues. We fixed all three, Least Authority verified the fixes, and on September 18 the mainnet router was upgraded to the verified code.

The final report is available in our repo here.

The router

PropAMMs stream quotes hundreds of times per block instead of waiting for on-chain state to update. We introduced them, and the router, in PropAMMs: pushing Ethereum to the next level of finance.

Our mainnet PropAMM Router handles about 13M USD across 1,000 swaps a day. It is a single entry point for every PropAMM in pamm.wtf (Fermi, Kipseli, Bebop, Tempest, TaurusFi, Metric and El Zorro) with Uniswap V3 as the fallback. You call it with a token pair, an input amount and the minimum output you accept. It requotes the venues at inclusion time, executes against the best one, and falls back to Uniswap V3 if that venue reverts or under-delivers. It checks your minimum against the balance the recipient actually received. Frontends can also take a per-call fee from the output token through separate fee-bearing entry points.

The just-in-time on-chain requoting gives the taker the best price across all PropAMMs, and it doubles as protection against quote spoofing: a malicious PropAMM that changes its quotes right before the end of the block loses the trade to the honest ones.

The contracts are upgradeable behind an ERC-1967 proxy. An OpenZeppelin AccessManager gates every privileged call. Upgrades and configuration changes carry a seven-day execution delay, venue listing a one-day delay, and pausing is instant. The code, the deployment scripts and the design notes are public in propamm-router-contracts.

The audit

Least Authority is a security consultancy based in Berlin that also audited our Ethereum client, ethrex. Two security researchers reviewed the router between July 15 and July 22, 2026, and delivered the initial report on July 24. They verified our fixes and delivered the final report on August 28.

The scope was the Solidity in src/: the router, the access manager, the fee library, the Uniswap V3 integration and the interfaces. The initial review ran against commit e413bc9 on the contracts/audit/base branch. The verification ran against commit 38b3b42. Dependencies (OpenZeppelin, Uniswap V3 core and periphery, SwapRouter02, forge-std) were pinned and out of scope. The auditors found no known vulnerabilities in them.

They looked for the usual failure modes of a router: incorrect implementation, fund draining or manipulation, gaming of the venue selection, denial of service, reentrancy, excess authority in the admin roles.

The final report lists three issues, all resolved:

Issue Severity Status
A. Swap functions may ignore a better swap quote Medium Resolved
B. Fee-bearing swaps may report more output than the recipient receives Medium Resolved
C. Selected-venue fee swaps attempt venues that cannot meet the gross minimum Low Resolved

No critical or high severity issues, and no findings on the access-control model. The report describes the router as "well designed, with security at the forefront of its architecture" and the code as "generally clean, modular, and well organized." It calls the Foundry test suite sufficient for the router's basic functionality and failure paths.

The fixes

Each fix is a public commit on the contracts/audit/fixes branch, merged to main in PR #88.

Issue A, a design decision we kept. quoteV1 quotes every PropAMM and Uniswap V3 and returns the best. swapV1 picks the best PropAMM and only falls back to Uniswap if no PropAMM meets your minimum. So quoteV1 can name Uniswap as the best venue while swapV1 executes through a PropAMM that still satisfies your minimum. Least Authority offered two remedies: quote Uniswap inside the swap too, or document the swap functions as PropAMM-preferred and provide quote functions whose selection matches. We chose the second.

Quoting Uniswap on-chain in every swap would make swapV1 about 25% more expensive in gas, and in our measurements less than 1% of trades would have routed to Uniswap if it were quoted. Uniswap is still part of the off-chain quoting the taker uses to build the transaction, specifically to set the minimum accepted output. So if every PropAMM underperformed Uniswap badly in that block, the transaction would still fall back to Uniswap even though it was not requoted on-chain.

The NatSpec and README now explain this decision (e1b4e39).

A taker who wants to requote Uniswap at inclusion time, at a slightly higher gas cost, can still do so by including Uniswap in swapViaSelectedVenuesV1 instead of calling the default swapV1.

With the documentation and the reasoning in place, Least Authority marked the issue as resolved.

Issue B, fee-bearing swaps and fee-on-transfer tokens. The fee-bearing entry points routed the swap output to the router, took the frontend fee, and forwarded the net amount to the recipient. The router measured its own balance change against the minimum, but not the recipient's. A token that charges a fee on transfer could credit the recipient less than amountOutMin while the swap succeeded and the Swapped event reported the nominal amount. The fix (38b3b42) snapshots the recipient's balance, measures the delta after the final transfer, enforces the minimum against that delta, and returns and emits the measured amount.

This was the largest contract change to come out of the audit, and it adds support for fee-on-transfer tokens.

Issue C, gross versus net minimum. When a caller supplies their own list of venues and a frontend fee, the router grosses the minimum up to cover the fee. Venue selection compared the quotes against the net minimum while execution required the gross one, so the router could pick a venue that would fail at execution and fall through to Uniswap, wasting gas. The fix (a276c9d) is one line: both stages now compare against the gross minimum.

We shipped the fix with a regression test, and Least Authority marked the issue as resolved.

The upgrade

An audit without an upgrade is just an email chain with PDFs attached, so we waited until the upgrade was live on mainnet before announcing. Here is the full trail:

  • July 3: the router proxy at 0x4ddf368080cd7946db5b459ad591c350158175e1 started running the pre-audit main branch.
  • September 8: new implementation deployed at 0x9b59064685ecfe130ac5d02b401e973c9a065858, source verified on Etherscan.
  • September 10: the upgrader Safe scheduled upgradeToAndCall on the RouterAccessManager (tx). The manager enforced the seven-day delay, so the operation became executable on September 17.
  • September 18 at 14:39 UTC: execution of the upgrade (tx). The proxy emitted Upgraded with the new implementation, and swaps continued through the same address without interruption.

We verified that what runs on chain is what was audited. The main branch differs from the verified commit 38b3b42 only in three NatSpec comments and a forge fmt re-indent. Compiling both commits produces byte-identical runtime code once the CBOR metadata hash, which covers comments, is stripped. Fetching the implementation bytecode from mainnet and masking the UUPS __self immutable gives the same bytes again. Anyone can repeat this with forge build and cast code.

What's next

The router keeps the same address, the same interface and the same SDKs. Nothing changes for integrators, except that fee-bearing swaps are now fully supported.

If you run a frontend or a PropAMM and want to integrate, the README and the SDK docs cover the entry points, and questions are welcome on GitHub, Telegram or X.