From 1af4519953673dc8b129c589fb6101e6a95b93be Mon Sep 17 00:00:00 2001 From: Kirill Kuznetcov Date: Thu, 4 Jun 2026 13:02:53 +0400 Subject: [PATCH 1/2] feat(pegged-swap): implement linearWidthFromSymmetricRangePercent --- .../swap-vm/instructions/pegged-swap/index.ts | 1 + .../pegged-swap-math/pegged-swap-math.test.ts | 27 +++++++++++++ .../pegged-swap-math/pegged-swap-math.ts | 38 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/index.ts b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/index.ts index bc616dd..2315f8e 100644 --- a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/index.ts +++ b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/index.ts @@ -4,6 +4,7 @@ export * from './opcodes' export { PeggedSwapArgs } from './pegged-swap-args' export type { PeggedTokenInfo } from './types' export { PeggedSwapCalculator } from './pegged-swap-calculator' +export { linearWidthFromSymmetricRangePercent } from './pegged-swap-math/pegged-swap-math' export { PeggedPrice } from './price' export type { PeggedInitialBalances, PeggedSwapCalculatorArgs } from './pegged-swap-calculator' export type { diff --git a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.test.ts b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.test.ts index ae225ac..2311f78 100644 --- a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.test.ts +++ b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.test.ts @@ -2,6 +2,8 @@ import { describe, expect, it } from 'vitest' import { + linearWidthFromSymmetricRangePercent, + MAX_LINEAR_WIDTH, normalizeReserve, peggedSwapMarginalGtPerLtE18, peggedSwapMarginalWeight, @@ -11,6 +13,31 @@ import { bigintSqrt } from '../../utils' const LINEAR_WIDTH = 8n * 10n ** 26n +describe('linearWidthFromSymmetricRangePercent', () => { + it('computes linearWidth = (1 - X) / (2X) · ONE via mulDiv', () => { + const linearWidth = linearWidthFromSymmetricRangePercent(25) + expect(linearWidth).toBe(15n * 10n ** 26n) + }) + + it('supports fractional percents', () => { + const linearWidth = linearWidthFromSymmetricRangePercent(25.5) + expect(linearWidth).toBe(1460784313725490196078431372n) + }) + + it('allows linearWidth at the on-chain maximum (20% symmetric range)', () => { + expect(linearWidthFromSymmetricRangePercent(20)).toBe(MAX_LINEAR_WIDTH) + }) + + it('rejects zero and 100% symmetric range', () => { + expect(() => linearWidthFromSymmetricRangePercent(0)).toThrow(/must be positive/) + expect(() => linearWidthFromSymmetricRangePercent(100)).toThrow(/less than 100%/) + }) + + it('rejects narrow peg bands whose A exceeds 2', () => { + expect(() => linearWidthFromSymmetricRangePercent(0.2)).toThrow(/exceeds maximum/) + }) +}) + describe('peggedSwapMath', () => { it('normalizeReserve is ONE when current equals initial', () => { const initial = 10n ** 18n diff --git a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.ts b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.ts index cff8a89..cefa152 100644 --- a/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.ts +++ b/typescript/swap-vm/src/swap-vm/instructions/pegged-swap/pegged-swap-math/pegged-swap-math.ts @@ -6,8 +6,42 @@ import { bigintSqrt } from '../../utils' /** Matches `PeggedSwapMath.ONE` in swap-vm. */ export const PEGGED_SWAP_ONE: bigint = 10n ** 27n +/** Maximum on-chain `linearWidth` (`A` ≤ 2). */ +export const MAX_LINEAR_WIDTH: bigint = 2n * PEGGED_SWAP_ONE + const MARGINAL_PRICE_ONE = 10n ** 18n +/** + * On-chain `linearWidth` from symmetric peg-band half-width as a human percent. + * + * @param symmetricRangePercent - Half-width in percent (e.g. `0.2` for ±0.20%, `25.5` for ±25.5%). + * + * A = (1 − X) / (2X), X = percent / 100, `linearWidth` = A · `PEGGED_SWAP_ONE`. + */ +export function linearWidthFromSymmetricRangePercent(symmetricRangePercent: number): bigint { + assert( + Number.isFinite(symmetricRangePercent), + 'PeggedSwapMath: symmetric range percent must be a finite number', + ) + assert(symmetricRangePercent > 0, 'PeggedSwapMath: symmetric range percent must be positive') + assert( + symmetricRangePercent < 100, + 'PeggedSwapMath: symmetric range percent must be less than 100%', + ) + + const x = symmetricRangePercentToScaledFraction(symmetricRangePercent) + + const linearWidth = mulDiv(PEGGED_SWAP_ONE - x, PEGGED_SWAP_ONE, 2n * x) + + assert(linearWidth >= 0n, 'PeggedSwapMath: linearWidth must be non-negative') + assert( + linearWidth <= MAX_LINEAR_WIDTH, + `PeggedSwapMath: linearWidth exceeds maximum (${MAX_LINEAR_WIDTH})`, + ) + + return linearWidth +} + /** * Spot price tokenGt per tokenLt (raw) in 1e18 fixed-point. * @@ -49,6 +83,10 @@ export function peggedSwapMarginalWeight(sqrtCoord: bigint, linearWidth: bigint) return mulDiv(PEGGED_SWAP_ONE, PEGGED_SWAP_ONE, 2n * sqrtCoord) + linearWidth } +function symmetricRangePercentToScaledFraction(percent: number): bigint { + return (BigInt(Math.round(percent * 1e9)) * PEGGED_SWAP_ONE) / (100n * 10n ** 9n) +} + function mulDiv(a: bigint, b: bigint, c: bigint): bigint { if (c === 0n) { throw new Error('mulDiv: division by zero') From f633d67267e8f74e43f304c091b4f19dd7b1fe99 Mon Sep 17 00:00:00 2001 From: CI/CD Bot Date: Thu, 4 Jun 2026 09:41:27 +0000 Subject: [PATCH 2/2] chore(release): - project: swap-vm 0.1.4 --- typescript/swap-vm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/swap-vm/package.json b/typescript/swap-vm/package.json index 760ac92..8565bb3 100644 --- a/typescript/swap-vm/package.json +++ b/typescript/swap-vm/package.json @@ -1,6 +1,6 @@ { "name": "@1inch/swap-vm-sdk", - "version": "0.1.3", + "version": "0.1.4", "description": "1inch Swap VM SDK", "author": "@1inch", "license": "LicenseRef-Degensoft-SwapVM-1.1",