Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
eba628d
feat: implement XYCSwapStrictAdditive with fee reinvestment and add c…
ifelsedeveloper Jan 26, 2026
9bbb876
refactor: optimize StrictAdditiveMath for gas efficiency and enhance …
ifelsedeveloper Jan 26, 2026
3d8847d
test: enhance RoundingInvariants tests and add fee reinvestment analysis
ifelsedeveloper Jan 27, 2026
6361495
refactor: enhance XYCSwapStrictAdditive with comprehensive tests and …
ifelsedeveloper Jan 27, 2026
7f421ad
refactor: update XYCSwapStrictAdditive to implement two curves design…
ifelsedeveloper Jan 27, 2026
2ca9802
test: refine XYCSwapStrictAdditive tests for strict additivity and sp…
ifelsedeveloper Jan 27, 2026
daaf93c
refactor: update XYCSwapStrictAdditive to remove obsolete PDF and add…
ifelsedeveloper Jan 27, 2026
ac49add
docs: update authorship in LaTeX documentation for two-curve model
ifelsedeveloper Jan 27, 2026
21fea2e
docs: update LaTeX documentation and comments for dissipative design …
ifelsedeveloper Jan 27, 2026
f613a26
docs: enhance LaTeX documentation with implementation formulas and nu…
ifelsedeveloper Jan 28, 2026
5151544
added some tests (security, invariants)
Jan 28, 2026
7557e7f
Merge branch 'feature/Strict-Additive-Fees-Reinvested-Inside-Pricing'…
Jan 28, 2026
d7cbaf0
added critical security cases test for StrictAdditiveSwap
Jan 29, 2026
7476e04
added XYCConcentrateStrictAdditive instruction, allowing concentratio…
Feb 13, 2026
1b3f5c7
docs: update LaTeX documentation for strict additivity and introduce …
ifelsedeveloper Feb 21, 2026
20a9fcf
Merge branch 'feature/Strict-Additive-Fees-Reinvested-Inside-Pricing'…
ifelsedeveloper Feb 21, 2026
4f8b139
docs: update LaTeX documentation for strict-additive fees with new au…
ifelsedeveloper Feb 21, 2026
99dcb48
docs: update author section in LaTeX documentation for strict-additiv…
ifelsedeveloper Feb 21, 2026
7c7bb58
docs: expand LaTeX documentation with new section on fee regimes unde…
ifelsedeveloper Mar 23, 2026
1f81a6c
docs: enhance LaTeX documentation with new definitions and sections o…
ifelsedeveloper Mar 23, 2026
caee217
docs: refine definitions and table for strict additivity in LaTeX doc…
ifelsedeveloper Mar 23, 2026
f2cfe34
docs: update LaTeX
ifelsedeveloper Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,055 changes: 539 additions & 516 deletions .gas-snapshot

Large diffs are not rendered by default.

Binary file added docs/reinvested_strict_additivity_two_curves.pdf
Binary file not shown.
1,300 changes: 1,300 additions & 0 deletions docs/reinvested_strict_additivity_two_curves.tex

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions snapshots/AMMGas.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"ConcentrateGrowLiquidity_XYCSwap_quote_exactIn": "46003",
"ConcentrateGrowLiquidity_XYCSwap_quote_exactOut": "46708",
"ConcentrateGrowLiquidity_XYCSwap_swap_exactIn": "171421",
"ConcentrateGrowLiquidity_XYCSwap_swap_exactOut": "172125",
"ConcentrateGrowPriceRange_XYCSwap_quote_exactIn": "42287",
"ConcentrateGrowPriceRange_XYCSwap_swap_exactIn": "143637",
"Concentrate_Decay_XYCSwap_quote_exactIn": "55530",
"Concentrate_Decay_XYCSwap_swap_exactIn": "228265",
"Decay_XYCSwap_quote_exactIn": "48280",
"Decay_XYCSwap_quote_exactOut": "48986",
"Decay_XYCSwap_swap_exactIn": "196947",
"Decay_XYCSwap_swap_exactOut": "197652",
"FullAMM_quote_exactIn": "58979",
"FullAMM_swap_exactIn": "231714",
"XYCSwap_FlatFeeIn_quote_exactIn": "42198",
"XYCSwap_FlatFeeIn_swap_exactIn": "143548",
"XYCSwap_FlatFeeOut_quote_exactIn": "42187",
"XYCSwap_FlatFeeOut_swap_exactIn": "143537",
"XYCSwap_quote_exactIn": "38753",
"XYCSwap_quote_exactOut": "39545",
"XYCSwap_swap_exactIn": "140103",
"XYCSwap_swap_exactOut": "140894"
"ConcentrateGrowLiquidity_XYCSwap_quote_exactIn": "46064",
"ConcentrateGrowLiquidity_XYCSwap_quote_exactOut": "46768",
"ConcentrateGrowLiquidity_XYCSwap_swap_exactIn": "171481",
"ConcentrateGrowLiquidity_XYCSwap_swap_exactOut": "172184",
"ConcentrateGrowPriceRange_XYCSwap_quote_exactIn": "42348",
"ConcentrateGrowPriceRange_XYCSwap_swap_exactIn": "143697",
"Concentrate_Decay_XYCSwap_quote_exactIn": "55591",
"Concentrate_Decay_XYCSwap_swap_exactIn": "228325",
"Decay_XYCSwap_quote_exactIn": "48342",
"Decay_XYCSwap_quote_exactOut": "49047",
"Decay_XYCSwap_swap_exactIn": "197008",
"Decay_XYCSwap_swap_exactOut": "197712",
"FullAMM_quote_exactIn": "59040",
"FullAMM_swap_exactIn": "231774",
"XYCSwap_FlatFeeIn_quote_exactIn": "42260",
"XYCSwap_FlatFeeIn_swap_exactIn": "143609",
"XYCSwap_FlatFeeOut_quote_exactIn": "42249",
"XYCSwap_FlatFeeOut_swap_exactIn": "143598",
"XYCSwap_quote_exactIn": "38815",
"XYCSwap_quote_exactOut": "39607",
"XYCSwap_swap_exactIn": "140164",
"XYCSwap_swap_exactOut": "140955"
}
72 changes: 36 additions & 36 deletions snapshots/LimitSwapGas.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
{
"Deadline_LimitSwap_quote_exactIn": "33687",
"Deadline_LimitSwap_swap_exactIn": "92063",
"DutchAuctionIn_LimitSwap_quote_exactIn": "36204",
"DutchAuctionIn_LimitSwap_quote_exactOut": "36909",
"DutchAuctionIn_LimitSwap_swap_exactIn": "94580",
"DutchAuctionIn_LimitSwap_swap_exactOut": "95285",
"DutchAuctionOut_LimitSwap_quote_exactIn": "36259",
"DutchAuctionOut_LimitSwap_quote_exactOut": "36964",
"DutchAuctionOut_LimitSwap_swap_exactIn": "94635",
"DutchAuctionOut_LimitSwap_swap_exactOut": "95340",
"FullLimitSwap_quote_exactIn": "43620",
"FullLimitSwap_swap_exactIn": "122884",
"InvalidateBit_LimitSwap_quote_exactIn": "37126",
"InvalidateBit_LimitSwap_swap_exactIn": "116316",
"LimitSwap_FlatFeeIn_quote_exactIn": "35304",
"LimitSwap_FlatFeeIn_swap_exactIn": "93684",
"LimitSwap_FlatFeeOut_quote_exactIn": "35293",
"LimitSwap_FlatFeeOut_swap_exactIn": "93669",
"LimitSwap_InvalidateTokenIn_quote_exactIn": "36664",
"LimitSwap_InvalidateTokenIn_swap_exactIn": "115928",
"LimitSwap_ProgressiveFee_quote_exactIn": "35435",
"LimitSwap_ProgressiveFee_swap_exactIn": "93811",
"LimitSwap_quote_exactIn": "31863",
"LimitSwap_quote_exactOut": "32654",
"LimitSwap_swap_exactIn": "90239",
"LimitSwap_swap_exactOut": "91029",
"MinRate_LimitSwap_quote_exactIn": "36279",
"MinRate_LimitSwap_quote_exactOut": "37326",
"MinRate_LimitSwap_swap_exactIn": "94655",
"MinRate_LimitSwap_swap_exactOut": "95701",
"Salt_LimitSwap_quote_exactIn": "33527",
"Salt_LimitSwap_swap_exactIn": "91903",
"TWAP_LimitSwap_quote_exactIn": "48438",
"TWAP_LimitSwap_quote_exactOut": "49230",
"TWAP_LimitSwap_swap_exactIn": "167815",
"TWAP_LimitSwap_swap_exactOut": "168606"
"Deadline_LimitSwap_quote_exactIn": "33748",
"Deadline_LimitSwap_swap_exactIn": "92123",
"DutchAuctionIn_LimitSwap_quote_exactIn": "36265",
"DutchAuctionIn_LimitSwap_quote_exactOut": "36970",
"DutchAuctionIn_LimitSwap_swap_exactIn": "94640",
"DutchAuctionIn_LimitSwap_swap_exactOut": "95344",
"DutchAuctionOut_LimitSwap_quote_exactIn": "36320",
"DutchAuctionOut_LimitSwap_quote_exactOut": "37025",
"DutchAuctionOut_LimitSwap_swap_exactIn": "94695",
"DutchAuctionOut_LimitSwap_swap_exactOut": "95399",
"FullLimitSwap_quote_exactIn": "43682",
"FullLimitSwap_swap_exactIn": "122946",
"InvalidateBit_LimitSwap_quote_exactIn": "37187",
"InvalidateBit_LimitSwap_swap_exactIn": "116376",
"LimitSwap_FlatFeeIn_quote_exactIn": "35365",
"LimitSwap_FlatFeeIn_swap_exactIn": "93744",
"LimitSwap_FlatFeeOut_quote_exactIn": "35354",
"LimitSwap_FlatFeeOut_swap_exactIn": "93729",
"LimitSwap_InvalidateTokenIn_quote_exactIn": "36725",
"LimitSwap_InvalidateTokenIn_swap_exactIn": "115988",
"LimitSwap_ProgressiveFee_quote_exactIn": "35496",
"LimitSwap_ProgressiveFee_swap_exactIn": "93871",
"LimitSwap_quote_exactIn": "31923",
"LimitSwap_quote_exactOut": "32716",
"LimitSwap_swap_exactIn": "90299",
"LimitSwap_swap_exactOut": "91090",
"MinRate_LimitSwap_quote_exactIn": "36340",
"MinRate_LimitSwap_quote_exactOut": "37387",
"MinRate_LimitSwap_swap_exactIn": "94716",
"MinRate_LimitSwap_swap_exactOut": "95762",
"Salt_LimitSwap_quote_exactIn": "33588",
"Salt_LimitSwap_swap_exactIn": "91963",
"TWAP_LimitSwap_quote_exactIn": "48499",
"TWAP_LimitSwap_quote_exactOut": "49291",
"TWAP_LimitSwap_swap_exactIn": "167876",
"TWAP_LimitSwap_swap_exactOut": "168667"
}
108 changes: 108 additions & 0 deletions src/instructions/XYCConcentrateStrictAdditive.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: LicenseRef-Degensoft-SwapVM-1.1
pragma solidity 0.8.30;

/// @custom:license-url https://github.com/1inch/swap-vm/blob/main/LICENSES/SwapVM-1.1.txt
/// @custom:copyright © 2025 Degensoft Ltd

import { Calldata } from "@1inch/solidity-utils/contracts/libraries/Calldata.sol";
import { Context, ContextLib } from "../libs/VM.sol";
import { StrictAdditiveMath } from "../libs/StrictAdditiveMath.sol";

/// @notice Arguments builder for XYCConcentrateStrictAdditive instruction
library XYCConcentrateStrictAdditiveArgsBuilder {
using Calldata for bytes;

uint256 internal constant ALPHA_SCALE = StrictAdditiveMath.ALPHA_SCALE;
uint256 internal constant ONE = 1e18;

error ConcentrateStrictAdditiveTwoTokensMissingDeltaLt();
error ConcentrateStrictAdditiveTwoTokensMissingDeltaGt();
error ConcentrateStrictAdditiveInconsistentPrices(uint256 price, uint256 priceMin, uint256 priceMax);

/// @notice Compute initial deltas for x^α·y=K concentrated liquidity
/// @dev For each direction, marginal price at boundary satisfies:
/// P_boundary/P_initial = (δ/(balance+δ))^(1+1/α)
/// Solving: δ = balance · r / (1 - r), where r = priceBound^(α/(α+1))
/// @dev For α=1 (ALPHA_SCALE), degenerates to the standard x·y=k formula:
/// r = sqrt(priceBound), matching XYCConcentrateArgsBuilder.computeDeltas
/// @param balanceA Initial balance of tokenA
/// @param balanceB Initial balance of tokenB
/// @param price Current price (tokenB/tokenA with 1e18 precision)
/// @param priceMin Minimum price for concentration range
/// @param priceMax Maximum price for concentration range
/// @param alpha Alpha exponent scaled by ALPHA_SCALE (e.g. 997_000_000 for α=0.997)
/// @return deltaA Virtual reserve addition for tokenA
/// @return deltaB Virtual reserve addition for tokenB
function computeDeltas(
uint256 balanceA,
uint256 balanceB,
uint256 price,
uint256 priceMin,
uint256 priceMax,
uint256 alpha
) public pure returns (uint256 deltaA, uint256 deltaB) {
require(priceMin <= price && price <= priceMax,
ConcentrateStrictAdditiveInconsistentPrices(price, priceMin, priceMax));

// Exponent: α/(α+1) where alpha is scaled by ALPHA_SCALE
// alphaExp = alpha * ALPHA_SCALE / (alpha + ALPHA_SCALE), result in ALPHA_SCALE units
uint256 alphaExp = alpha * ALPHA_SCALE / (alpha + ALPHA_SCALE);

if (price != priceMin) {
// rA = (priceMin/price)^(α/(α+1))
uint256 rA = StrictAdditiveMath.powRatio(priceMin, price, alphaExp);
// δA = balanceA · rA / (ONE - rA)
deltaA = balanceA * rA / (ONE - rA);
}

if (price != priceMax) {
// rB = (priceMax/price)^(α/(α+1))
uint256 rB = StrictAdditiveMath.powRatio(priceMax, price, alphaExp);
// δB = balanceB · ONE / (rB - ONE), since rB > ONE
deltaB = balanceB * ONE / (rB - ONE);
}
}

function build2D(address tokenA, address tokenB, uint256 deltaA, uint256 deltaB) internal pure returns (bytes memory) {
(uint256 deltaLt, uint256 deltaGt) = tokenA < tokenB ? (deltaA, deltaB) : (deltaB, deltaA);
return abi.encodePacked(deltaLt, deltaGt);
}

function parse2D(
bytes calldata args,
address tokenIn,
address tokenOut
) internal pure returns (uint256 deltaIn, uint256 deltaOut) {
uint256 deltaLt = uint256(bytes32(args.slice(0, 32, ConcentrateStrictAdditiveTwoTokensMissingDeltaLt.selector)));
uint256 deltaGt = uint256(bytes32(args.slice(32, 64, ConcentrateStrictAdditiveTwoTokensMissingDeltaGt.selector)));
(deltaIn, deltaOut) = tokenIn < tokenOut ? (deltaLt, deltaGt) : (deltaGt, deltaLt);
}
}

/// @title XYCConcentrateStrictAdditive - Stateless concentration for x^α·y=K AMM
/// @notice Adds virtual reserves (deltas) to concentrate liquidity, then runs inner swap
contract XYCConcentrateStrictAdditive {
using Calldata for bytes;
using ContextLib for Context;

error ConcentrateStrictAdditiveShouldBeUsedBeforeSwapAmountsComputed(uint256 amountIn, uint256 amountOut);

/// @notice Concentrate liquidity for 2 tokens, then run inner swap instruction(s)
/// @dev Fixed deltas remain correct across swaps because x^α·y invariant
/// is preserved by XYCSwapStrictAdditive formula.
/// @param ctx The swap context containing balances and amounts
/// @param args Encoded deltas: deltaLt (32 bytes) + deltaGt (32 bytes)
function _xycConcentrateStrictAdditive2D(Context memory ctx, bytes calldata args) internal {
require(
ctx.swap.amountIn == 0 || ctx.swap.amountOut == 0,
ConcentrateStrictAdditiveShouldBeUsedBeforeSwapAmountsComputed(ctx.swap.amountIn, ctx.swap.amountOut)
);

(uint256 deltaIn, uint256 deltaOut) =
XYCConcentrateStrictAdditiveArgsBuilder.parse2D(args, ctx.query.tokenIn, ctx.query.tokenOut);
ctx.swap.balanceIn += deltaIn;
ctx.swap.balanceOut += deltaOut;

ctx.runLoop();
}
}
122 changes: 122 additions & 0 deletions src/instructions/XYCSwapStrictAdditive.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: LicenseRef-Degensoft-SwapVM-1.1
pragma solidity 0.8.30;

/// @custom:license-url https://github.com/1inch/swap-vm/blob/main/LICENSES/SwapVM-1.1.txt
/// @custom:copyright © 2025 Degensoft Ltd

import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Calldata } from "@1inch/solidity-utils/contracts/libraries/Calldata.sol";

import { Context, ContextLib } from "../libs/VM.sol";
import { StrictAdditiveMath } from "../libs/StrictAdditiveMath.sol";

/// @notice Arguments builder for XYCSwapStrictAdditive instruction
library XYCSwapStrictAdditiveArgsBuilder {
using Calldata for bytes;

error XYCSwapStrictAdditiveAlphaOutOfRange(uint256 alpha);
error XYCSwapStrictAdditiveMissingAlpha();

uint256 internal constant ALPHA_SCALE = StrictAdditiveMath.ALPHA_SCALE;

/// @notice Build args for the strict additive swap instruction
/// @param alpha The alpha exponent scaled by 1e9 (e.g., 997_000_000 for α=0.997)
/// @dev Alpha must be in range (0, 1e9] where 1e9 = 1.0 (no fee, standard x*y=k)
/// @dev Lower alpha means higher fee reinvested into pricing
/// @dev Common values: 0.997e9 (0.3% effective fee), 0.99e9 (1% effective fee)
function build(uint32 alpha) internal pure returns (bytes memory) {
require(alpha > 0 && alpha <= ALPHA_SCALE, XYCSwapStrictAdditiveAlphaOutOfRange(alpha));
return abi.encodePacked(alpha);
}

function parse(bytes calldata args) internal pure returns (uint32 alpha) {
alpha = uint32(bytes4(args.slice(0, 4, XYCSwapStrictAdditiveMissingAlpha.selector)));
}
}

/// @title XYCSwapStrictAdditive - AMM with strict additive fee reinvested inside pricing
/// @notice Implements x^α * y = K constant function market maker
/// @dev Based on the paper "Strict-Additive Fees Reinvested Inside Pricing for AMMs"
/// @dev Key properties:
/// - Full input credit: x' = x + Δx (all input goes to reserve)
/// - Fees reinvested inside pricing (no external fee bucket)
/// - Strict additivity: swap(a+b) = swap(b) ∘ swap(a) (split invariance)
/// @dev The parameter α controls the fee:
/// - α = 1.0: No fee, standard x*y=k
/// - α < 1.0: Fee is reinvested, lowering output for same input
/// - Lower α = higher effective fee
/// @dev Mathematical formulas (DISSIPATIVE design - single deterministic mapping):
/// - Single rule: power α is always applied to the INPUT token's reserve
/// - X→Y: K = y * x^α (power on input X)
/// - Y→X: K = x * y^α (power on input Y)
/// - ExactIn: Δy = y * (1 - (x / (x + Δx))^α)
/// - ExactOut: Δx = x * ((y / (y - Δy))^(1/α) - 1) [inverse on same curve]
contract XYCSwapStrictAdditive {
using ContextLib for Context;

error XYCSwapStrictAdditiveRecomputeDetected();
error XYCSwapStrictAdditiveRequiresBothBalancesNonZero(uint256 balanceIn, uint256 balanceOut);

/// @notice Execute strict additive swap using x^α * y = K formula
/// @dev Instruction suffix XD: Dynamic args from program, supports both ExactIn and ExactOut
/// @param ctx The swap context containing balances and amounts
/// @param args Encoded alpha parameter (4 bytes, uint32 scaled by 1e9)
/// @dev Uses balanceIn and balanceOut from ctx.swap which should be set by Balances instruction
///
/// ╔═══════════════════════════════════════════════════════════════════════════════════════╗
/// ║ STRICT ADDITIVE FEE WITH REINVESTMENT INSIDE PRICING (DISSIPATIVE DESIGN) ║
/// ║ ║
/// ║ Single Deterministic Dissipative Mapping: ║
/// ║ - ONE rule: power α is always applied to the INPUT token's reserve ║
/// ║ - X→Y: K = y * x^α | Y→X: K = x * y^α ║
/// ║ ║
/// ║ ExactIn: Δy = y * (1 - (x / (x + Δx))^α) ║
/// ║ ExactOut: Δx = x * ((y / (y - Δy))^(1/α) - 1) [inverse on same direction] ║
/// ║ ║
/// ║ Properties: ║
/// ║ - BOTH ExactIn and ExactOut are strictly additive ║
/// ║ - Non-conservative: round trips dissipate trader value into pool ║
/// ║ - Time-irreversible: creates real bid-ask spread for economic incentive ║
/// ║ - Full input credit (all input goes to reserve) ║
/// ║ ║
/// ║ Alpha parameter guide: ║
/// ║ - α = 1.000 (1e9): No fee, standard constant product ║
/// ║ - α = 0.997 (997e6): ~0.3% equivalent fee ║
/// ║ - α = 0.990 (990e6): ~1% equivalent fee ║
/// ║ - α = 0.950 (950e6): ~5% equivalent fee ║
/// ╚═══════════════════════════════════════════════════════════════════════════════════════╝
function _xycSwapStrictAdditiveXD(Context memory ctx, bytes calldata args) internal pure {
require(
ctx.swap.balanceIn > 0 && ctx.swap.balanceOut > 0,
XYCSwapStrictAdditiveRequiresBothBalancesNonZero(ctx.swap.balanceIn, ctx.swap.balanceOut)
);

uint256 alpha = XYCSwapStrictAdditiveArgsBuilder.parse(args);

if (ctx.query.isExactIn) {
require(ctx.swap.amountOut == 0, XYCSwapStrictAdditiveRecomputeDetected());

// 0 < α <= 1
// Δy = y * (1 - (x / (x + Δx))^α)
// Floor division for tokenOut is desired behavior (protects maker)
ctx.swap.amountOut = StrictAdditiveMath.calcExactIn(
ctx.swap.balanceIn,
ctx.swap.balanceOut,
ctx.swap.amountIn,
alpha
);
} else {
require(ctx.swap.amountIn == 0, XYCSwapStrictAdditiveRecomputeDetected());

// ExactOut: use inverse formula on the SAME curve (strictly additive)
// Δx = x * ((y / (y - Δy))^(1/α) - 1)
// Ceiling division for tokenIn is desired behavior (protects maker)
ctx.swap.amountIn = StrictAdditiveMath.calcExactOut(
ctx.swap.balanceIn,
ctx.swap.balanceOut,
ctx.swap.amountOut,
alpha
);
}
}
}
Loading