npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bush.fi/maths

v0.1.2

Published

Exact fixed-point reference maths for Bush Protocol pools: the amounts the on-chain Vault returns, computed off-chain.

Readme

Exact fixed-point reference maths for Bush Protocol pools, in TypeScript (bigint). Given a pool's on-chain state, it returns the same amounts the Bush Vault would — to the wei — for swaps, adds and removes. Built for aggregators, routers and front-ends that need to quote without an RPC round-trip per query.

Every pool type here is verified bit-for-bit against a pool deployed through its factory on a local chain (see maths/check in the source repo); the same maths ships for Python and Rust.

Install

npm install @bush.fi/maths

Usage

import { Vault, SwapKind } from '@bush.fi/maths';

const vault = new Vault();

// Raw token amounts in, raw token amounts out — exactly what the Router returns.
const amountOut = vault.swap(
  { amountRaw: 1_000_000n, tokenIn, tokenOut, swapKind: SwapKind.GivenIn },
  poolState,
);

const { bptAmountOutRaw, amountsInRaw } = vault.addLiquidity(
  { pool, maxAmountsInRaw, minBptAmountOutRaw: 0n, kind: AddKind.UNBALANCED },
  poolState,
);

const { amountsOutRaw, bptAmountInRaw } = vault.removeLiquidity(
  { pool, minAmountsOutRaw, maxBptAmountInRaw, kind: RemoveKind.PROPORTIONAL },
  poolState,
);

The Vault reproduces the whole on-chain flow — token scaling, rates, swap fees, hooks — on top of the pool maths, so poolState is the only input.

Building the pool state

poolState is the pool's state as read from the chain, with every number as a bigint. Every pool exposes its state in two calls — get<Pool>ImmutableData() and get<Pool>DynamicData() — and the Vault's getPoolConfig fills in the rest. A Weighted pool, with ethers v6:

import { Contract, JsonRpcProvider } from 'ethers';
import { Vault, SwapKind, type WeightedState } from '@bush.fi/maths';

const provider = new JsonRpcProvider(RPC_URL);
const vault = new Contract(VAULT_ADDRESS, ['function getPoolConfig(address) view returns (tuple(tuple(bool disableUnbalancedLiquidity, bool enableAddLiquidityCustom, bool enableRemoveLiquidityCustom, bool enableDonation) liquidityManagement, uint256 staticSwapFeePercentage, uint256 aggregateSwapFeePercentage, uint256 aggregateYieldFeePercentage, uint40 tokenDecimalDiffs, uint32 pauseWindowEndTime, bool isPoolRegistered, bool isPoolInitialized, bool isPoolPaused, bool isPoolInRecoveryMode))'], provider);
const pool = new Contract(POOL_ADDRESS, [
  'function getWeightedPoolImmutableData() view returns (tuple(address[] tokens, uint256[] decimalScalingFactors, uint256[] normalizedWeights, uint256[] minTokenBalances))',
  'function getWeightedPoolDynamicData() view returns (tuple(uint256[] balancesLiveScaled18, uint256[] tokenRates, uint256 staticSwapFeePercentage, uint256 totalSupply, bool isPoolInitialized, bool isPoolPaused, bool isPoolInRecoveryMode))',
], provider);

const [immutable, dynamic, config] = await Promise.all([
  pool.getWeightedPoolImmutableData(),
  pool.getWeightedPoolDynamicData(),
  vault.getPoolConfig(POOL_ADDRESS),
]);

const poolState: WeightedState = {
  poolType: 'WEIGHTED',
  poolAddress: POOL_ADDRESS,
  tokens: [...immutable.tokens],
  scalingFactors: [...immutable.decimalScalingFactors],
  weights: [...immutable.normalizedWeights],
  minTokenBalances: [...immutable.minTokenBalances],
  balancesLiveScaled18: [...dynamic.balancesLiveScaled18],
  tokenRates: [...dynamic.tokenRates],
  swapFee: dynamic.staticSwapFeePercentage,
  totalSupply: dynamic.totalSupply,
  aggregateSwapFee: config.aggregateSwapFeePercentage,
  supportsUnbalancedLiquidity: !config.liquidityManagement.disableUnbalancedLiquidity,
};

const amountOut = new Vault().swap(
  { amountRaw: 1_000_000n, tokenIn: poolState.tokens[0], tokenOut: poolState.tokens[1], swapKind: SwapKind.GivenIn },
  poolState,
);

(ethers returns bigints already; spread the arrays to get plain bigint[].) The same shape applies to every pool type — the base fields above come from the same getters, and the pool-specific ones from that pool's data structs:

| poolType | State type | Pool-specific fields | From | | --- | --- | --- | --- | | WEIGHTED, WEIGHTED_8020, COW | WeightedState | weights, minTokenBalances | getWeightedPoolImmutableData() | | STABLE | StableState | amp | getAmplificationParameter()[0] (raw, ×1000) | | LIQUIDITY_BOOTSTRAPPING | LiquidityBootstrappingState | weights schedule, projectTokenIndex, isSwapEnabled, currentTimestamp | getLBPoolImmutableData() / getLBPoolDynamicData() | | FIXED_PRICE_LBP | FixedPriceLBPState | projectTokenRate, indices, startTime/endTime, currentTimestamp | same | | RECLAMM | ReClammState | lastVirtualBalances, dailyPriceShiftBase, lastTimestamp, centerednessMargin, the price-ratio update fields, currentTimestamp | getReClammPoolDynamicData() |

A pool with a hook (STABLE + StableSurge) also takes hookType: 'StableSurge' and a hookState ({ hookType: 'StableSurge', amp, surgeThresholdPercentage, maxSurgeFeePercentage }, the last two from the hook contract's getSurgeThresholdPercentage(pool) / getMaxSurgeFeePercentage(pool)), passed as the third argument of swap / addLiquidity / removeLiquidity.

Time-dependent pools (LBPs, ReClamm) take a currentTimestamp: use the timestamp of the block the transaction will land in, not the block the state was read at.

The testData/ files in the source repo show the exact JSON for every pool type, straight from a real pool: pool is the state, swaps / adds / removes are the results the Vault returned for it.

Pool types

| poolType | Class | Factory | | --- | --- | --- | | WEIGHTED, WEIGHTED_8020, COW | Weighted | WeightedPoolFactory, WeightedPool8020Factory, CowPoolFactory | | STABLE (+ StableSurge hook) | Stable, StableSurgeHook | StablePoolFactory, StableSurgePoolFactory | | LIQUIDITY_BOOTSTRAPPING | LiquidityBootstrapping | LBPoolFactory | | FIXED_PRICE_LBP | FixedPriceLBP | FixedPriceLBPoolFactory | | RECLAMM | ReClamm | ReClammPoolFactory |

Notes

  • Integers only, everywhere. Amounts are bigint; the fixed-point primitives (MathSol) are ports of the Solidity FixedPoint / LogExpMath libraries and round the same way the contracts do.
  • Operations the pool would reject (over the max swap ratio, LBP sale restrictions, …) throw.
  • Derived from balancer-maths (MIT), trimmed and extended to the pool types Bush deploys.

Contributing

Pool maths is added to this package together with its factory; the walkthrough is in the source repo's CONTRIBUTING.md.