From 563ad75b2d2b715f7afba64a7504e0728e19a45a Mon Sep 17 00:00:00 2001 From: Nicholas Addison Date: Tue, 28 Jul 2026 21:32:22 +1000 Subject: [PATCH] Fixed parsing of buy and sell amounts in pricing --- docs/ACTIONS.md | 5 +++-- src/js/tasks/actions/setPricesEthena.ts | 8 ++++---- src/js/tasks/actions/setPricesUSDC.ts | 8 ++++---- src/js/tasks/actions/setPricesWETH.ts | 8 ++++---- src/js/tasks/armPrices.js | 22 +++++++++------------- src/js/utils/arm.js | 6 ++---- test/js/armPrices.test.js | 19 +++++++++++++++++++ 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/docs/ACTIONS.md b/docs/ACTIONS.md index b1073612..32eae675 100644 --- a/docs/ACTIONS.md +++ b/docs/ACTIONS.md @@ -17,8 +17,9 @@ The mainnet `setPrices*` actions use `--amount` as the DEX swap amount when fetching the reference price quote. This is separate from `--buy-amount` and `--sell-amount`, which set the buy-side liquidity-asset and sell-side base-asset liquidity remaining on the Ethena, USDC, and WETH ARMs. If omitted, each limit is -set to the maximum `uint128` value. Liquidity amounts are integer native token -units (for example, `100000000` is 100 tokens for an asset with 6 decimals). +set to the maximum `uint128` value. Liquidity amounts are token-denominated: +`1` is one liquidity or base token, with the appropriate token decimals applied +by the action. `--buy-price` and `--sell-price` bypass DEX-derived pricing and set an exact pair. Both must be supplied together; `--amount` is not used in this mode. diff --git a/src/js/tasks/actions/setPricesEthena.ts b/src/js/tasks/actions/setPricesEthena.ts index 57d69222..0fb32c9a 100644 --- a/src/js/tasks/actions/setPricesEthena.ts +++ b/src/js/tasks/actions/setPricesEthena.ts @@ -32,15 +32,15 @@ action({ ) .addOptionalParam( "buyAmount", - "Liquidity asset amount remaining at the buy price for multi-base ARMs, as an integer in native token units.", + "USDe remaining at the buy price, in token units (1 = 1 USDe).", undefined, - types.string, + types.float, ) .addOptionalParam( "sellAmount", - "Base asset amount remaining at the sell price for multi-base ARMs, as an integer in native token units.", + "sUSDe remaining at the sell price, in token units (1 = 1 sUSDe).", undefined, - types.string, + types.float, ) .addOptionalParam( "maxBuyPrice", diff --git a/src/js/tasks/actions/setPricesUSDC.ts b/src/js/tasks/actions/setPricesUSDC.ts index fb01f995..138171e1 100644 --- a/src/js/tasks/actions/setPricesUSDC.ts +++ b/src/js/tasks/actions/setPricesUSDC.ts @@ -36,15 +36,15 @@ action({ ) .addOptionalParam( "buyAmount", - "Liquidity asset amount remaining at the buy price for multi-base ARMs, as an integer in native token units.", + "USDC remaining at the buy price, in token units (1 = 1 USDC).", undefined, - types.string, + types.float, ) .addOptionalParam( "sellAmount", - "Base asset amount remaining at the sell price for multi-base ARMs, as an integer in native token units.", + "Base asset remaining at the sell price, in token units (1 = 1 token).", undefined, - types.string, + types.float, ) .addOptionalParam( "maxBuyPrice", diff --git a/src/js/tasks/actions/setPricesWETH.ts b/src/js/tasks/actions/setPricesWETH.ts index 8e1ff8ea..b03ad089 100644 --- a/src/js/tasks/actions/setPricesWETH.ts +++ b/src/js/tasks/actions/setPricesWETH.ts @@ -56,15 +56,15 @@ action({ ) .addOptionalParam( "buyAmount", - "WETH remaining at the buy price, as an integer in native token units.", + "WETH remaining at the buy price, in token units (1 = 1 WETH).", undefined, - types.string, + types.float, ) .addOptionalParam( "sellAmount", - "Base asset remaining at the sell price, as an integer in native token units.", + "Base asset remaining at the sell price, in token units (1 = 1 token).", undefined, - types.string, + types.float, ) .addOptionalParam( "maxBuyPrice", diff --git a/src/js/tasks/armPrices.js b/src/js/tasks/armPrices.js index a709beb4..a0916fef 100644 --- a/src/js/tasks/armPrices.js +++ b/src/js/tasks/armPrices.js @@ -88,6 +88,13 @@ const setPrices = async (options) => { // Base asset decimals used to scale aggregator quote amounts. Legacy ARM // configs don't expose baseAssetDecimals as all their assets are 18 decimals. const baseDecimals = Number(config.baseAssetDecimals ?? 18); + const liquidityDecimals = Number( + await new Contract( + liquidityAddress, + ["function decimals() view returns (uint8)"], + signer, + ).decimals(), + ); log(`Getting current ARM prices:`); log(`base asset : ${baseSymbol}`); @@ -111,17 +118,6 @@ const setPrices = async (options) => { ? 10n : 30n; - // The liquidity asset decimals are not in the base asset config so read - // them on-chain. Can differ from the base asset decimals, eg an 18 - // decimals base asset over a 6 decimals USDC liquidity asset. - const liquidityDecimals = Number( - await new Contract( - liquidityAddress, - ["function decimals() view returns (uint8)"], - signer, - ).decimals(), - ); - // 2.1 Get reference prices let referencePrices; if (midPrice) { @@ -361,8 +357,8 @@ const setPrices = async (options) => { const toleranceScaled = parseUnits(tolerance.toString(), 36 - 4); log(`tolerance : ${formatUnits(toleranceScaled, 32)} basis points`); - const targetBuyAmount = parseSwapCap(buyAmount); - const targetSellAmount = parseSwapCap(sellAmount); + const targetBuyAmount = parseSwapCap(buyAmount, liquidityDecimals); + const targetSellAmount = parseSwapCap(sellAmount, baseDecimals); const swapCapsChanged = haveSwapCapsChanged( baseContext, targetBuyAmount, diff --git a/src/js/utils/arm.js b/src/js/utils/arm.js index 2c9b473d..e1aba540 100644 --- a/src/js/utils/arm.js +++ b/src/js/utils/arm.js @@ -136,12 +136,10 @@ const getArmBaseSymbols = async ({ arm, armName, base, blockTag }) => { return [defaultBaseSymbol(armName)]; }; -const parseSwapCap = (amount) => { +const parseSwapCap = (amount, decimals = 18) => { if (amount === undefined || amount === null) return MAX_SWAP_LIQUIDITY; if (typeof amount === "bigint") return amount; - if (typeof amount === "number") return parseUnits(amount.toString(), 18); - const value = amount.toString(); - return value.includes(".") ? parseUnits(value, 18) : BigInt(value); + return parseUnits(amount.toString(), decimals); }; const toConfigObject = (config) => ({ diff --git a/test/js/armPrices.test.js b/test/js/armPrices.test.js index ed44d553..813561dc 100644 --- a/test/js/armPrices.test.js +++ b/test/js/armPrices.test.js @@ -1,6 +1,25 @@ const assert = require("assert"); const { haveSwapCapsChanged } = require("../../src/js/utils/priceUpdate"); +const { parseSwapCap } = require("../../src/js/utils/arm"); + +assert.strictEqual( + parseSwapCap("1", 18), + 10n ** 18n, + "one 18-decimal token should not be parsed as one wei", +); + +assert.strictEqual( + parseSwapCap("1", 6), + 10n ** 6n, + "one 6-decimal token should use the token's native decimals", +); + +assert.strictEqual( + parseSwapCap("1.5", 6), + 1_500_000n, + "fractional token amounts should be supported", +); const multiBaseContext = (buyLiquidityRemaining, sellLiquidityRemaining) => ({ version: "multiBase",