UNPKG

aftermath-ts-sdk

Version:
648 lines (647 loc) 29.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Pool = void 0; const cmmmCalculations_1 = require("./utils/cmmmCalculations"); const caller_1 = require("../../general/utils/caller"); const _1 = require("."); const utils_1 = require("../../general/utils"); /** * The `Pool` class encapsulates all the functionality needed to interact * with a specific AMM pool on the Aftermath platform. It allows you to * calculate trade amounts, deposit/withdraw amounts, fetch transactions, * and retrieve on-chain statistics and event data. * * @example * ```typescript * const afSdk = new Aftermath("MAINNET"); * await afSdk.init(); // initialize provider * * const pools = afSdk.Pools(); * const pool = await pools.getPool({ objectId: "0x..." }); * * const stats = await pool.getStats(); * const tradeTx = await pool.getTradeTransaction({ * walletAddress: "0x...", * coinInType: "0x2::sui::SUI", * coinInAmount: BigInt(1e9), * coinOutType: "0x<yourCoin>", * slippage: 0.01, * }); * ``` */ class Pool extends caller_1.Caller { /** * Creates a new instance of the `Pool` class for on-chain interaction. * * @param pool - The fetched `PoolObject` from Aftermath API or on-chain query. * @param config - Optional caller configuration (e.g., network, access token). * @param Provider - An optional `AftermathApi` instance for advanced transaction usage. */ constructor(pool, config, Provider) { super(config, `pools/${pool.objectId}`); this.pool = pool; this.Provider = Provider; /** * Retrieves the 24-hour volume for this specific pool. * * @returns A promise resolving to a number (volume in 24h). * * @example * ```typescript * const vol24h = await pool.getVolume24hrs(); * console.log("Pool 24h Volume:", vol24h); * ``` */ this.getVolume24hrs = () => __awaiter(this, void 0, void 0, function* () { return this.fetchApi("volume-24hrs"); }); // ========================================================================= // Calculations // ========================================================================= /** * Calculates the instantaneous spot price for swapping from `coinInType` * to `coinOutType` within this pool. Optionally includes fees in the price. * * @param inputs - Object specifying input coin, output coin, and a boolean for `withFees`. * @returns The numerical spot price (float). * * @example * ```typescript * const price = pool.getSpotPrice({ * coinInType: "0x<coinA>", * coinOutType: "0x<coinB>", * withFees: true, * }); * console.log("Spot Price:", price); * ``` */ this.getSpotPrice = (inputs) => { const spotPriceWithDecimals = cmmmCalculations_1.CmmmCalculations.calcSpotPriceWithFees(utils_1.Helpers.deepCopy(this.pool), inputs.coinInType, inputs.coinOutType, !inputs.withFees); // Adjust for decimals difference return ((spotPriceWithDecimals * Number(this.pool.coins[inputs.coinOutType].decimalsScalar)) / Number(this.pool.coins[inputs.coinInType].decimalsScalar)); }; // TODO: account for referral discount for all calculations /** * Calculates how much output coin you would receive when trading * a given input coin and amount in this pool, factoring in protocol * and optional DAO fees. * * @param inputs - Includes `coinInType`, `coinInAmount`, and `coinOutType`. * @returns A bigint representing how many output coins you'd get. * @throws Error if the trade amount is too large relative to the pool balance. * * @example * ```typescript * const amountOut = pool.getTradeAmountOut({ * coinInType: "0x<coinA>", * coinInAmount: BigInt(1000000), * coinOutType: "0x<coinB>", * }); * ``` */ this.getTradeAmountOut = (inputs) => { const pool = utils_1.Helpers.deepCopy(this.pool); const coinInPoolBalance = pool.coins[inputs.coinInType].balance; const coinOutPoolBalance = pool.coins[inputs.coinOutType].balance; const coinInAmountWithFees = this.getAmountWithDAOFee({ amount: _1.Pools.getAmountWithProtocolFees({ amount: inputs.coinInAmount, }), }); if (Number(coinInAmountWithFees) / Number(coinInPoolBalance) >= _1.Pools.constants.bounds.maxTradePercentageOfPoolBalance - Pool.constants.percentageBoundsMarginOfError) throw new Error("coinInAmountWithFees / coinInPoolBalance >= maxTradePercentageOfPoolBalance"); const coinOutAmount = cmmmCalculations_1.CmmmCalculations.calcOutGivenIn(pool, inputs.coinInType, inputs.coinOutType, coinInAmountWithFees); if (coinOutAmount <= 0) throw new Error("coinOutAmount <= 0"); if (Number(coinOutAmount) / Number(coinOutPoolBalance) >= _1.Pools.constants.bounds.maxTradePercentageOfPoolBalance - Pool.constants.percentageBoundsMarginOfError) throw new Error("coinOutAmount / coinOutPoolBalance >= maxTradePercentageOfPoolBalance"); return coinOutAmount; }; /** * Calculates how much input coin is required to obtain a certain output coin amount * from this pool, factoring in fees. * * @param inputs - Includes `coinInType`, desired `coinOutAmount`, and `coinOutType`. * @returns A bigint representing the needed input amount. * @throws Error if the desired output is too large relative to pool balances. * * @example * ```typescript * const amountIn = pool.getTradeAmountIn({ * coinInType: "0x<coinA>", * coinOutAmount: BigInt(1000000), * coinOutType: "0x<coinB>" * }); * ``` */ this.getTradeAmountIn = (inputs) => { const pool = utils_1.Helpers.deepCopy(this.pool); const coinInPoolBalance = pool.coins[inputs.coinInType].balance; const coinOutPoolBalance = pool.coins[inputs.coinOutType].balance; if (Number(inputs.coinOutAmount) / Number(coinOutPoolBalance) >= _1.Pools.constants.bounds.maxTradePercentageOfPoolBalance - Pool.constants.percentageBoundsMarginOfError) throw new Error("coinOutAmount / coinOutPoolBalance >= maxTradePercentageOfPoolBalance"); const coinInAmount = cmmmCalculations_1.CmmmCalculations.calcInGivenOut(pool, inputs.coinInType, inputs.coinOutType, inputs.coinOutAmount); if (coinInAmount <= 0) throw new Error("coinInAmount <= 0"); if (Number(coinInAmount) / Number(coinInPoolBalance) >= _1.Pools.constants.bounds.maxTradePercentageOfPoolBalance - Pool.constants.percentageBoundsMarginOfError) throw new Error("coinInAmount / coinInPoolBalance >= maxTradePercentageOfPoolBalance"); const coinInAmountWithoutFees = this.getAmountWithoutDAOFee({ amount: _1.Pools.getAmountWithoutProtocolFees({ amount: coinInAmount, }), }); return coinInAmountWithoutFees; }; /** * Calculates how many LP tokens you receive for providing liquidity * in specific coin amounts. Also returns a ratio for reference. * * @param inputs - Contains the amounts in for each coin in the pool. * @returns An object with `lpAmountOut` and `lpRatio`. * * @example * ```typescript * const depositCalc = pool.getDepositLpAmountOut({ * amountsIn: { "0x<coinA>": BigInt(1000000), "0x<coinB>": BigInt(500000) }, * }); * console.log(depositCalc.lpAmountOut, depositCalc.lpRatio); * ``` */ this.getDepositLpAmountOut = (inputs) => { const calcedLpRatio = cmmmCalculations_1.CmmmCalculations.calcDepositFixedAmounts(this.pool, Object.entries(inputs.amountsIn).reduce((acc, [coin, amount]) => (Object.assign(Object.assign({}, acc), { [coin]: this.getAmountWithDAOFee({ amount }) })), {})); if (calcedLpRatio >= utils_1.Casting.Fixed.fixedOneB) throw new Error("lpRatio >= 1"); const lpRatio = utils_1.Casting.bigIntToFixedNumber(calcedLpRatio); const lpAmountOut = BigInt(Math.floor(Number(this.pool.lpCoinSupply) * (1 / lpRatio - 1))); return { lpAmountOut, lpRatio, }; }; /** * Calculates how many coins a user will receive when withdrawing a specific ratio or LP amount. * This method is used in multi-coin withdrawals where you specify how much of each coin you want. * * @param inputs - The LP ratio and an object specifying direction amounts for each coin. * @returns A `CoinsToBalance` object with final amounts out, factoring in DAO fees. * * @example * ```typescript * const outAmounts = pool.getWithdrawAmountsOut({ * lpRatio: 0.1, * amountsOutDirection: { "0x<coinA>": BigInt(500000) }, * }); * console.log(outAmounts); * ``` */ this.getWithdrawAmountsOut = (inputs) => { const amountsOut = cmmmCalculations_1.CmmmCalculations.calcWithdrawFlpAmountsOut(this.pool, inputs.amountsOutDirection, inputs.lpRatio); for (const coin of Object.keys(amountsOut)) { if (!(coin in inputs.amountsOutDirection) || inputs.amountsOutDirection[coin] <= BigInt(0)) continue; const amountOut = amountsOut[coin]; if (amountOut <= 0) { throw new Error(`amountsOut[${coin}] <= 0`); } if (Number(amountOut) / Number(this.pool.coins[coin].balance) >= _1.Pools.constants.bounds.maxWithdrawPercentageOfPoolBalance) { throw new Error("coinOutAmount / coinOutPoolBalance >= maxWithdrawPercentageOfPoolBalance"); } amountsOut[coin] = this.getAmountWithDAOFee({ amount: amountOut }); } return amountsOut; }; /** * A simplified multi-coin withdraw approach: calculates all outputs by proportion of the * user's LP share among selected coin types. Useful for approximate or "blind" all-coin out logic. * * @param inputs - Contains the `lpCoinAmountIn` to burn, and which coin types to receive. * @returns A record mapping coin type => final amounts out. */ this.getWithdrawAmountsOutSimple = (inputs) => { const { lpCoinAmountIn, coinTypesOut, referral } = inputs; const lpCoinSupply = this.pool.lpCoinSupply; let withdrawAmountsEstimates = {}; coinTypesOut.forEach((poolCoin) => { const poolCoinAmountInPool = this.pool.coins[utils_1.Helpers.addLeadingZeroesToType(poolCoin)] .balance; const poolCoinAmount = Number(poolCoinAmountInPool) * (Number(lpCoinAmountIn) / Number(lpCoinSupply)); withdrawAmountsEstimates[utils_1.Helpers.addLeadingZeroesToType(poolCoin)] = BigInt(Math.floor(poolCoinAmount)); }); const lpRatio = this.getMultiCoinWithdrawLpRatio({ lpCoinAmountIn, }); const amountsOut = this.getWithdrawAmountsOut({ lpRatio, amountsOutDirection: withdrawAmountsEstimates, referral, }); for (const coin of Object.keys(amountsOut)) { if (!coinTypesOut .map((coinOut) => utils_1.Helpers.addLeadingZeroesToType(coinOut)) .includes(coin)) continue; const amountOut = amountsOut[coin]; if (amountOut <= BigInt(0)) throw new Error(`amountsOut[${coin}] <= 0 `); if (amountOut / this.pool.coins[coin].balance >= _1.Pools.constants.bounds.maxWithdrawPercentageOfPoolBalance) throw new Error("coinOutAmount / coinOutPoolBalance >= maxWithdrawPercentageOfPoolBalance"); amountsOut[coin] = this.getAmountWithDAOFee({ amount: amountOut, }); } return amountsOut; }; /** * Calculates how many coins you get when withdrawing **all** coin types from the pool, * given a ratio. This is typically used for proportionate withdrawal. * * @param inputs - Includes `lpRatio`, the portion of your LP to burn (0 < ratio < 1). * @returns A record of coin type => amounts out, after factoring in any fees. * * @example * ```typescript * const allOut = pool.getAllCoinWithdrawAmountsOut({ lpRatio: 0.1 }); * console.log(allOut); // amounts for each coin * ``` */ this.getAllCoinWithdrawAmountsOut = (inputs) => { if (inputs.lpRatio >= 1) throw new Error("lpRatio >= 1"); const amountsOut = Object.entries(this.pool.coins).reduce((acc, [coin, info]) => { return Object.assign(Object.assign({}, acc), { [coin]: this.getAmountWithDAOFee({ amount: BigInt(Math.floor(Number(info.balance) * inputs.lpRatio)), }) }); }, {}); return amountsOut; }; /** * For multi-coin withdraw, calculates the ratio of how much LP you are burning * relative to the total supply. e.g. if user burns 100 of 1000 supply => ratio 0.1. * * @param inputs - Contains the `lpCoinAmountIn` to burn. * @returns A float ratio (0 < ratio < 1). */ this.getMultiCoinWithdrawLpRatio = (inputs) => Number(this.pool.lpCoinSupply - inputs.lpCoinAmountIn) / Number(this.pool.lpCoinSupply); /** * For an all-coin withdraw, calculates the ratio of how much LP is burned * relative to total supply. e.g. if user burns 50 of 200 supply => ratio 0.25. * * @param inputs - Contains the `lpCoinAmountIn`. * @returns A float ratio, typically 0 < ratio < 1. */ this.getAllCoinWithdrawLpRatio = (inputs) => Number(inputs.lpCoinAmountIn) / Number(this.pool.lpCoinSupply); // ========================================================================= // Getters // ========================================================================= /** * Returns an array of coin types in ascending lexicographic order * for the coins contained in this pool. * * @returns An array of coin type strings. */ this.coins = () => { return Object.keys(this.pool.coins).sort((a, b) => a.localeCompare(b)); }; /** * Returns an array of `PoolCoin` objects, one for each coin in this pool, * sorted lexicographically by coin type. * * @returns An array of `PoolCoin`. */ this.poolCoins = () => { return Object.entries(this.pool.coins) .sort((a, b) => a[0].localeCompare(b[0])) .map((data) => data[1]); }; /** * Returns an array of `[CoinType, PoolCoin]` pairs, sorted by coin type. * * @returns An array of coin-type => `PoolCoin` pairs. */ this.poolCoinEntries = () => { return Object.entries(this.pool.coins).sort((a, b) => a[0].localeCompare(b[0])); }; /** * Returns the current DAO fee percentage, if configured (0 < fee <= 100%). * * @returns A decimal fraction representing the fee (e.g., 0.01 = 1%) or `undefined`. */ this.daoFeePercentage = () => { return this.pool.daoFeePoolObject ? utils_1.Casting.bpsToPercentage(this.pool.daoFeePoolObject.feeBps) : undefined; }; /** * Returns the Sui address that currently receives the DAO fee portion of * pool trades, or `undefined` if no DAO fee is configured. * * @returns The DAO fee recipient address. */ this.daoFeeRecipient = () => { var _a; return (_a = this.pool.daoFeePoolObject) === null || _a === void 0 ? void 0 : _a.feeRecipient; }; // ========================================================================= // Private Helpers // ========================================================================= /** * Applies the DAO fee (if present) to a given `amount`, effectively reducing * that amount by the fee fraction. e.g. if fee is 2%, it returns 98% of the input. * * @param inputs - Contains `amount` as a bigint. * @returns The post-fee amount as a bigint. */ this.getAmountWithDAOFee = (inputs) => { const daoFeePercentage = this.daoFeePercentage(); if (!daoFeePercentage) return inputs.amount; return BigInt(Math.floor(Number(inputs.amount) * (1 - daoFeePercentage))); }; /** * The inverse operation of `getAmountWithDAOFee`, used in internal calculations * when we need to back out how much input was needed prior to the fee cut. * * @param inputs - Contains `amount` as a bigint. * @returns The pre-fee amount as a bigint. */ this.getAmountWithoutDAOFee = (inputs) => { const daoFeePercentage = this.daoFeePercentage(); if (!daoFeePercentage) return inputs.amount; return BigInt(Math.floor(Number(inputs.amount) * (1 / (1 - daoFeePercentage)))); }; /** * Provides an instance of the Pools provider from `AftermathApi`. * Throws an error if not defined. */ this.useProvider = () => { var _a; const provider = (_a = this.Provider) === null || _a === void 0 ? void 0 : _a.Pools(); if (!provider) throw new Error("missing AftermathApi Provider"); return provider; }; this.pool = pool; } // ========================================================================= // Transactions // ========================================================================= /** * Builds or fetches a deposit transaction to add liquidity to this pool. * The resulting `Transaction` can be signed and submitted by the user. * * @param inputs - The deposit parameters including coin amounts, slippage, etc. * @returns A `Transaction` to deposit funds into the pool. * * @example * ```typescript * const depositTx = await pool.getDepositTransaction({ * walletAddress: "0x...", * amountsIn: { "0x<coin>": BigInt(1000000) }, * slippage: 0.01, * }); * ``` */ getDepositTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildDepositTx(Object.assign(Object.assign({}, inputs), { pool: this })); }); } /** * Builds or fetches a withdrawal transaction to remove liquidity from this pool. * * @param inputs - The parameters specifying how much LP is burned, desired coins out, slippage, etc. * @returns A `Transaction` to withdraw funds from the pool. * * @example * ```typescript * const withdrawTx = await pool.getWithdrawTransaction({ * walletAddress: "0x...", * amountsOutDirection: { * "0x<coin>": BigInt(500000), * }, * lpCoinAmount: BigInt(1000000), * slippage: 0.01, * }); * ``` */ getWithdrawTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildWithdrawTx(Object.assign(Object.assign({}, inputs), { pool: this })); }); } /** * Builds or fetches a transaction to withdraw all coin types from this pool, * effectively "burning" an LP position in exchange for multiple coin outputs. * * @param inputs - The parameters specifying how much LP to burn. * @returns A `Transaction` to withdraw all coins from the pool in proportion. * * @example * ```typescript * const allCoinWithdrawTx = await pool.getAllCoinWithdrawTransaction({ * walletAddress: "0x...", * lpCoinAmount: BigInt(500000), * }); * ``` */ getAllCoinWithdrawTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildAllCoinWithdrawTx(Object.assign(Object.assign({}, inputs), { pool: this })); }); } /** * Builds or fetches a trade transaction to swap between two coin types in this pool. * * @param inputs - The trade parameters including coin in/out, amounts, slippage, etc. * @returns A `Transaction` that can be signed and executed for the swap. * * @example * ```typescript * const tradeTx = await pool.getTradeTransaction({ * walletAddress: "0x...", * coinInType: "0x<coinA>", * coinInAmount: BigInt(1000000), * coinOutType: "0x<coinB>", * slippage: 0.005, * }); * ``` */ getTradeTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildTradeTx(Object.assign(Object.assign({}, inputs), { pool: this })); }); } /** * Builds a transaction to update the DAO fee percentage for this pool, * if it has a DAO fee configured. The user must own the appropriate * `daoFeePoolOwnerCap`. * * @param inputs - Includes user wallet, `daoFeePoolOwnerCapId`, and the new fee percentage. * @returns A `Transaction` that can be signed to update the DAO fee on chain. * @throws If this pool has no DAO fee configuration. * * @example * ```typescript * const tx = await pool.getUpdateDaoFeeTransaction({ * walletAddress: "0x...", * daoFeePoolOwnerCapId: "0x<capId>", * newFeePercentage: 0.01, // 1% * }); * ``` */ getUpdateDaoFeeTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { var _a; const daoFeePoolId = (_a = this.pool.daoFeePoolObject) === null || _a === void 0 ? void 0 : _a.objectId; if (!daoFeePoolId) throw new Error("this pool has no DAO fee"); return this.useProvider().buildDaoFeePoolUpdateFeeBpsTx(Object.assign(Object.assign({}, inputs), { daoFeePoolId, lpCoinType: this.pool.lpCoinType, newFeeBps: utils_1.Casting.percentageToBps(inputs.newFeePercentage) })); }); } /** * Builds a transaction to update the DAO fee recipient for this pool, * if it has a DAO fee configured. The user must own the appropriate * `daoFeePoolOwnerCap`. * * @param inputs - Includes user wallet, `daoFeePoolOwnerCapId`, and the new fee recipient. * @returns A `Transaction` that can be signed to update the DAO fee recipient on chain. * @throws If this pool has no DAO fee configuration. * * @example * ```typescript * const tx = await pool.getUpdateDaoFeeRecipientTransaction({ * walletAddress: "0x...", * daoFeePoolOwnerCapId: "0x<capId>", * newFeeRecipient: "0x<recipient>", * }); * ``` */ getUpdateDaoFeeRecipientTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { var _a; const daoFeePoolId = (_a = this.pool.daoFeePoolObject) === null || _a === void 0 ? void 0 : _a.objectId; if (!daoFeePoolId) throw new Error("this pool has no DAO fee"); return this.useProvider().buildDaoFeePoolUpdateFeeRecipientTx(Object.assign(Object.assign({}, inputs), { daoFeePoolId, lpCoinType: this.pool.lpCoinType, newFeeRecipient: utils_1.Helpers.addLeadingZeroesToType(inputs.newFeeRecipient) })); }); } // ========================================================================= // Inspections // ========================================================================= /** * Fetches comprehensive pool statistics (volume, TVL, fees, APR, etc.) from the Aftermath API. * Also caches the result in `this.stats`. * * @returns A promise resolving to `PoolStats` object. * * @example * ```typescript * const stats = await pool.getStats(); * console.log(stats.volume, stats.fees, stats.apr); * ``` */ getStats() { return __awaiter(this, void 0, void 0, function* () { const stats = yield this.fetchApi("stats"); this.setStats(stats); return stats; }); } /** * Caches the provided stats object into `this.stats`. * * @param stats - The `PoolStats` object to store. */ setStats(stats) { this.stats = stats; } /** * Fetches an array of volume data points for a specified timeframe. * This is often used for charting or historical references. * * @param inputs - Contains a `timeframe` key, such as `"1D"` or `"1W"`. * @returns A promise resolving to an array of `PoolDataPoint`. * * @example * ```typescript * const volumeData = await pool.getVolumeData({ timeframe: "1D" }); * console.log(volumeData); // e.g. [{ time: 1686000000, value: 123.45 }, ...] * ``` */ getVolumeData(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.fetchApi(`volume/${inputs.timeframe}`); }); } /** * Fetches an array of fee data points for a specified timeframe. * * @param inputs - Contains a `timeframe` key, e.g., `"1D"` or `"1W"`. * @returns A promise resolving to an array of `PoolDataPoint`. * * @example * ```typescript * const feeData = await pool.getFeeData({ timeframe: "1D" }); * console.log(feeData); * ``` */ getFeeData(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.fetchApi(`fees/${inputs.timeframe}`); }); } // ========================================================================= // Events // ========================================================================= /** * Fetches user interaction events (deposit/withdraw) with this pool, optionally paginated. * * @param inputs - Includes user `walletAddress` and optional pagination fields. * @returns A promise that resolves to `PoolDepositEvent | PoolWithdrawEvent` objects with a cursor if more exist. * * @example * ```typescript * const events = await pool.getInteractionEvents({ walletAddress: "0x...", limit: 10 }); * console.log(events.events, events.nextCursor); * ``` */ getInteractionEvents(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.fetchApiIndexerEvents("interaction-events-by-user", inputs); }); } } exports.Pool = Pool; /** * Internal margin of error used in trade calculations to prevent * exceeding maximum allowed percentages of pool balances. */ Pool.constants = { percentageBoundsMarginOfError: 0.001, // 0.1% };