diff --git a/components/research/TermMaxGuarantee.jsx b/components/research/TermMaxGuarantee.jsx new file mode 100644 index 0000000..28e3325 --- /dev/null +++ b/components/research/TermMaxGuarantee.jsx @@ -0,0 +1,105 @@ +import { useState, useEffect, useRef } from 'react' +import { ExternalLinkIcon } from './ExternalLink' + +const FORMAL_GUARANTEE = + '∀ s amountIn, virtualXtReserve′ = virtualXtReserve − singleSegmentBuyXtTokenAmtOut(daysToMaturity, virtualXtReserve, amountIn, feeRatio, liqSquare, offset)' + +export default function TermMaxGuarantee({ specsHref }) { + const [showEnglish, setShowEnglish] = useState(true) + const timerRef = useRef(null) + + useEffect(() => { + timerRef.current = setTimeout(() => setShowEnglish(false), 5000) + return () => clearTimeout(timerRef.current) + }, []) + + const handleToggle = () => { + if (timerRef.current) { + clearTimeout(timerRef.current) + timerRef.current = null + } + setShowEnglish((prev) => !prev) + } + + return ( +
+ + +
+
+ + {FORMAL_GUARANTEE} + +
+
+

+ When a user swaps debt tokens for XT, the reserve goes down by + exactly what the pricing curve says it should. +

+
+
+ +

+ s: the contract state + (storage) at the time of the swap. +
+ virtualXtReserve /{' '} + virtualXtReserve′: XT + reserve before / after. +
+ + singleSegmentBuyXtTokenAmtOut(...) + + : how many XT the curve says the user gets. Modeled in Lean from the + Solidity source. +

+ +
+ + View Lean + + +
+
+ ) +} diff --git a/data/research.js b/data/research.js index 07d639e..5775a56 100644 --- a/data/research.js +++ b/data/research.js @@ -1,4 +1,14 @@ export const research = [ + { + slug: 'termmax-order-v2-buy-xt-single-segment', + title: 'TermMax Single-Segment Buy-XT Reserve Integrity', + subtitle: + 'Formally verified reserve-update integrity of the single-segment debtToken-to-XT swap path.', + description: + 'How we proved that the single-segment buy-XT path in TermMax OrderV2 updates the virtual XT reserve by exactly the curve-computed output amount, using Verity and Lean 4.', + date: '2026-04-24', + tag: 'Case study' + }, { slug: 'midas-feed-growth-safety', title: 'Midas Growth-Aware Feed Safety Guarantees', diff --git a/pages/research/termmax-order-v2-buy-xt-single-segment.jsx b/pages/research/termmax-order-v2-buy-xt-single-segment.jsx new file mode 100644 index 0000000..1d57134 --- /dev/null +++ b/pages/research/termmax-order-v2-buy-xt-single-segment.jsx @@ -0,0 +1,428 @@ +import Head from 'next/head' +import Link from 'next/link' +import PageLayout from '../../components/PageLayout' +import ResearchCard from '../../components/ResearchCard' +import CodeBlock from '../../components/research/CodeBlock' +import Disclosure from '../../components/research/Disclosure' +import ExternalLink from '../../components/research/ExternalLink' +import Hypothesis from '../../components/research/Hypothesis' +import TermMaxGuarantee from '../../components/research/TermMaxGuarantee' +import { getSortedResearch } from '../../lib/getSortedResearch' + +const BENCHMARK_COMMIT_HASH = + 'f70d95716422175702266c0153e4d063c399a5d8' + +const VERIFY_COMMAND = `git clone https://github.com/lfglabs-dev/verity-benchmark +cd verity-benchmark +git checkout ${BENCHMARK_COMMIT_HASH} +lake build Benchmark.Cases.TermMax.OrderV2BuyXtSingleSegment.Compile` + +const UPSTREAM_ORDER_V2 = + 'https://github.com/term-structure/termmax-contract-v2/blob/64bd47b98e064c7fb91ab4a59b70520e0ec285d5/contracts/v2/TermMaxOrderV2.sol' +const UPSTREAM_CURVE = + 'https://github.com/term-structure/termmax-contract-v2/blob/64bd47b98e064c7fb91ab4a59b70520e0ec285d5/contracts/v1/lib/TermMaxCurve.sol' +const UPSTREAM_MATH = + 'https://github.com/term-structure/termmax-contract-v2/blob/64bd47b98e064c7fb91ab4a59b70520e0ec285d5/contracts/v1/lib/MathLib.sol' + +const BENCHMARK_REPO = + 'https://github.com/lfglabs-dev/verity-benchmark' + +const BENCHMARK_COMMIT = + `https://github.com/lfglabs-dev/verity-benchmark/blob/${BENCHMARK_COMMIT_HASH}` + +const CASE_PATH = 'Benchmark/Cases/TermMax/OrderV2BuyXtSingleSegment' +const CONTRACT_LINK = `${BENCHMARK_COMMIT}/${CASE_PATH}/Contract.lean` +const SPECS_LINK = `${BENCHMARK_COMMIT}/${CASE_PATH}/Specs.lean` +const PROOFS_LINK = `${BENCHMARK_COMMIT}/${CASE_PATH}/Proofs.lean` +const CASE_LINK = `${BENCHMARK_COMMIT}/${CASE_PATH}` + +export default function TermMaxOrderV2BuyXtSingleSegmentPage() { + const otherResearch = getSortedResearch().filter( + (r) => r.slug !== 'termmax-order-v2-buy-xt-single-segment' + ) + + return ( + <> + + + TermMax Single-Segment Buy-XT Reserve Integrity | Research | LFG Labs + + + + +
+ + +
+

+ TermMax Single-Segment Buy-XT Reserve Integrity +

+
+ +
+ +

+ TermMax (by{' '} + + Term Structure + + ) is a fixed-rate lending protocol. Users trade between debt tokens + and XT (extended tokens) through on-chain order books, where pricing + is governed by a bonding curve parameterized by liquidity, a + time-dependent interest factor, and configurable fee ratios. +

+

+ The{' '} + + TermMaxOrderV2 + {' '} + contract's{' '} + swapExactTokenToToken{' '} + function is the entry point for exact-input swaps. This case study + isolates the{' '} + debtToken → XT path + on a single curve segment: the path a user takes when borrowing + against a single liquidity position. The proof tracks the full call + stack from the swap entry point down through the curve library and + confirms that the on-chain reserve update matches the curve + computation exactly. +

+
+ +
+

+ Why this matters +

+

+ The{' '} + virtualXtReserve is + the accounting variable that tracks how much XT liquidity remains in + the order. Every subsequent swap prices off this value. If the + reserve update diverges from the curve computation, even by one + wei, all future swaps on that order will price off an incorrect + base, and the order's implied interest rate will drift from its + configured curve. +

+ +

+ The modeled call stack follows the{' '} + debtToken → XT{' '} + branch of{' '} + + swapExactTokenToToken + {' '} + through every internal helper down to the curve computation: +

+
    +
  • + + swapExactTokenToToken + {' '} + → _swapAndUpdateReserves{' '} + → _buyXt{' '} + → _buyToken{' '} + → _buyXtStep{' '} + → TermMaxCurve.buyXt{' '} + → cutsReverseIter{' '} + → calcIntervalProps +
  • +
  • + MathLib.plusInt256{' '} + (signed-integer addition on uint256) is also modeled. +
  • +
  • + The{' '} + nonReentrant{' '} + modifier is included. The reentrancy lock is modeled as a + separate storage slot. +
  • +
+

+ Out of scope: other token-pair branches of{' '} + + swapExactTokenToToken + + , access control ( + + onlyBorrowingIsAllowed + + ), token transfers, rebalancing, events, and multi-segment curve + iteration. +

+
+
+ +
+

+ How this was modeled +

+

+ The benchmark extracts eight Solidity functions across three source + files ( + + TermMaxOrderV2.sol + + ,{' '} + + TermMaxCurve.sol + + ,{' '} + + MathLib.sol + + ) into a single Verity contract block. Each Solidity helper is kept + as a separate Verity function to preserve the call structure of the + original code. +

+

+ TermMax's curve can have multiple segments, each with its own + liquidity parameters. This model covers the case where there is + exactly one segment. That means the Solidity loop that iterates + over segments runs exactly once, and if the swap needs more + liquidity than that single segment provides, the contract reverts + with{' '} + + InsufficientLiquidity() + + . +

+ +

+ The model faithfully reproduces the Solidity logic, but with a + few deliberate simplifications. The full details are documented + in{' '} + Contract.lean. +

+
    +
  • + Single curve segment only. The Solidity + contract supports multiple curve segments and iterates over + them. This model covers exactly one segment. Multi-segment + swaps are not covered. +
  • +
  • + One swap direction. Only the{' '} + debtToken → XT{' '} + path is modeled. The other token-pair branches of{' '} + + swapExactTokenToToken + {' '} + are not included. +
  • +
  • + Access control omitted. The{' '} + + onlyBorrowingIsAllowed + {' '} + modifier does not touch the reserve, so it is left out without + weakening the guarantee. +
  • +
  • + Post-swap side effects omitted. Token + transfers, rebalancing, and events all happen after the + reserve write and are guarded by{' '} + nonReentrant, + so they cannot affect the proven property. +
  • +
+

+ A few cosmetic differences also exist: struct fields are passed + as individual parameters instead of structs, and library + functions are inlined as helpers. These change the shape of the + code but not its logic. +

+
+ + {VERIFY_COMMAND} +

+ If the build succeeds, the proofs are correct.{' '} + + Source repository + +

+
+
+ +
+

+ Proof status +

+

+ This case has one headline theorem in Lean 4. The corresponding + proof file in the{' '} + + Verity benchmark repository + {' '} + is sorry-free, so + the claim below is accepted by Lean's kernel rather than by + informal review. +

+
+ + + + + + + + + + + + + +
+ Guarantee + + Status +
+ virtualXtReserve′ = virtualXtReserve − curveOutput + + proven +
+
+

+ View specs in Lean + View proofs in Lean +

+
+ +
+

+ Assumptions +

+

+ The proof uses zero axioms. The five hypotheses below are + preconditions for successful execution. Each one mirrors an + explicit check or implicit requirement in the Solidity source. +

+
    + + The Solidity entry point skips the swap body when the input is + zero. The model covers the non-trivial execution path. + + + The function uses a{' '} + nonReentrant{' '} + modifier. The lock must be open (not already held by a prior + call) for the swap to execute. + + + The curve formula divides the liquidity squared by the virtual XT + reserve (after applying the curve offset). A zero virtual reserve + would cause a division-by-zero revert. + + + The computed XT output cannot exceed the available reserve. In the + Solidity source, exceeding this triggers{' '} + + revert InsufficientLiquidity() + + . + + + The net output (XT received plus debt tokens returned) must meet + the caller's minimum. In the Solidity source, failing this + triggers{' '} + + revert UnexpectedAmount(minTokenOut, netOut) + + . + +
+

+ View specs in Lean + View proofs in Lean +

+
+ +
+

+ Learn more +

+

+ Upstream Solidity contracts:{' '} + + TermMaxOrderV2.sol + + {', '} + + TermMaxCurve.sol + + {', '} + + MathLib.sol + + . +

+

+ + Full case directory + + {' in the Verity benchmark repository.'} +

+

+ + Verity benchmark repository + + {', which contains the formal specification, machine-checked proofs, and the broader benchmark suite this case belongs to.'} +

+

+ + What is a formal proof? + {' '} + A short explanation for non-specialists. +

+
+ + {otherResearch.length > 0 && ( +
+

+ More research +

+
+ {otherResearch.map((r) => ( + + ))} +
+
+ )} +
+
+ + ) +}