{"version":3,"sources":["../src/compute.ts","../src/constants.ts"],"names":["ZERO","BigRational","maxRational","a","b","minRational","computeInteraction","prices","targetWeights","state","adaInput","adaPerToken","tvl","accTvl","asset","assetAmount","maybeBalancedTokens","adaLeft","getTokensToBalance","amount","mtkPrice","newMtkSupply","adaAvailable","weights","adaEqAmount","targetWeight","weight","maybeBaseCase","assert","baseCaseAsset","baseCaseValue","baseCase","minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw","acc","assetValue","tvlIfThisTokenAmountDoesntChange","tvlOfTokensWithZeroWeight","_asset","tvlOfNonNullWeights","adaDiffToBalance","balancedTokens","approximatedBalance","assetAda","tokenAdaDiffToBalance","computeFees","portfolioState","type","batcherFee","platformFee","microMTKsInOrder","userFee","MTK_DECIMALS","FEES"],"mappings":"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","file":"index.cjs","sourcesContent":["import assert from 'assert';\nimport { BigRational } from 'big-rational-ts';\nimport * as types from './types.js';\n\nconst ZERO = new BigRational(0n, 1n);\n\nfunction maxRational(a: BigRational, b: BigRational) {\n  return a.gt(b) ? a : b;\n}\n\nfunction minRational(a: BigRational, b: BigRational) {\n  return a.lt(b) ? a : b;\n}\n\n/**\n * Computes the next state of the portfolio after an interaction.\n * @param prices The prices of the assets in ADA\n * @param targetWeights The target weights of the assets in the portfolio\n * @param state The current state of the portfolio\n * @param adaInput The amount of ADA to be added (if negative removed) to the portfolio\n *\n * @returns The new state of the portfolio\n */\nexport function computeInteraction(\n  prices: types.Prices,\n  targetWeights: types.Weights,\n  state: types.PortfolioState<bigint, bigint>,\n  adaInput: BigRational,\n): types.PortfolioState<BigRational, BigRational> {\n  /** How much per asset in ADA in the portfolio before the deposit */\n  const adaPerToken: types.RationalDict = {};\n\n  const tvl = Object.entries(state.assets).reduce(\n    (accTvl, [asset, assetAmount]) => {\n      adaPerToken[asset] = new BigRational(assetAmount, 1n).mul(prices[asset]);\n      return accTvl.add(adaPerToken[asset]);\n    },\n    ZERO,\n  );\n\n  /**\n   * First condition: At least for one token in the collection,\n   * there is a state for which this asset has the same amount of tokens\n   * and the portfolio is balanced without removing tokens of a\n   * different asset (i.e., it only needs to add tokens to the rest)\n   *\n   * Notice that tokens that satisfy this condition,\n   * maximize the worth of tokens needed to reach balance.\n   * If we find the asset with the max worth, we can know the minimum\n   * amount of ADA needed to balance the portfolio.\n   */\n\n  const [maybeBalancedTokens, adaLeft] = getTokensToBalance(\n    prices,\n    targetWeights,\n    adaPerToken,\n    tvl,\n    adaInput,\n  );\n\n  /**\n   * Only enters here when the assets are actually balanced and there is more to\n   * change in the portfolio\n   */\n  const adaLeftToModify = adaInput.gte(ZERO)\n    ? adaLeft.gt(ZERO)\n    : adaLeft.lt(ZERO);\n\n  if (adaLeftToModify) {\n    for (const [asset, amount] of Object.entries(maybeBalancedTokens)) {\n      maybeBalancedTokens[asset] = adaLeft\n        .mul(targetWeights[asset])\n        .div(prices[asset])\n        .add(amount)\n        .reduce();\n    }\n  }\n\n  const mtkPrice = tvl.div(new BigRational(state.mtkSupply, 1n));\n  const newMtkSupply = tvl.add(adaInput).div(mtkPrice).reduce();\n  return { assets: maybeBalancedTokens, mtkSupply: newMtkSupply };\n}\n\n/**\n * This function returns the amount of tokens that would end up in the portfolio\n * if the input is used to balance the portfolio.\n * @param prices The prices of the assets in ADA\n * @param targetWeights The target weights of the assets in the portfolio\n * @param adaPerToken The amount of ADA per token in the portfolio\n * @param tvl The total value locked in the portfolio\n * @param adaAvailable The amount of ADA available to balance the portfolio\n *\n * @returns The amount of tokens that would end up in the portfolio and how much\n * ADA would be left after the interaction\n *\n */\nfunction getTokensToBalance(\n  prices: types.Prices,\n  targetWeights: types.Weights,\n  adaPerToken: types.RationalDict,\n  tvl: BigRational,\n  adaAvailable: BigRational,\n): [types.RationalDict, BigRational] {\n  /** The actual token ratio of the portfolio */\n  const weights: types.RationalDict = {};\n  for (const [asset, adaEqAmount] of Object.entries(adaPerToken)) {\n    weights[asset] = adaEqAmount.div(tvl).reduce();\n  }\n\n  // Already balanced => No changes needed\n  if (\n    Object.entries(targetWeights).every(([asset, targetWeight]) =>\n      targetWeight.eq(weights[asset]),\n    )\n  ) {\n    return [\n      Object.fromEntries(\n        Object.entries(targetWeights).map(([asset, weight]) => [\n          asset,\n          tvl.mul(weight).div(prices[asset]).reduce(),\n        ]),\n      ),\n      adaAvailable,\n    ];\n  }\n\n  /**\n   * Base case for minimum tvl to balance can't be a token with target 0 as it may not be balanceable\n   */\n  const maybeBaseCase = Object.entries(adaPerToken).find(([asset]) => {\n    const targetWeight = targetWeights[asset];\n    assert(targetWeight, new Error('Invalid portfolio weights'));\n    return !targetWeight.eq(ZERO);\n  });\n\n  assert(maybeBaseCase, new Error('Invalid portfolio assets'));\n  const [baseCaseAsset, baseCaseValue] = maybeBaseCase;\n  const baseCase = baseCaseValue.div(targetWeights[baseCaseAsset]);\n\n  /**\n   * The minimum balanced TVL if no tokens are removed in a deposit (or inserted in a withdraw) is determined by\n   * the token for which, if nothing is added to it, the portfolio\n   * needs the most (hence maxRational) ADA to complete the rest of\n   * the ratios.\n   *\n   */\n  const minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw =\n    Object.entries(adaPerToken).reduce((acc, [asset, assetValue]) => {\n      // skip zero target weight\n      if (targetWeights[asset].eq(ZERO)) {\n        return acc;\n      }\n      /** Check if this is the token that needs the most (least in case of withdrawal) ADA to complete the rest */\n      const tvlIfThisTokenAmountDoesntChange = assetValue.div(\n        targetWeights[asset],\n      );\n\n      return adaAvailable.gte(ZERO)\n        ? maxRational(tvlIfThisTokenAmountDoesntChange, acc)\n        : minRational(tvlIfThisTokenAmountDoesntChange, acc);\n    }, baseCase);\n\n  /**\n   * How much the input should be to have a a perfect balance after\n   * the interaction (restricting to the tokens with non-zero weights\n   */\n  const tvlOfTokensWithZeroWeight = Object.entries(targetWeights)\n    .filter(([_asset, weight]) => weight.eq(ZERO))\n    .reduce((acc, [asset]) => {\n      return acc.add(adaPerToken[asset]).reduce();\n    }, ZERO);\n\n  const tvlOfNonNullWeights = tvl\n    .add(tvlOfTokensWithZeroWeight.negate())\n    .reduce();\n\n  const adaDiffToBalance = adaAvailable.gte(ZERO)\n    ? minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw\n        .add(tvlOfNonNullWeights.negate())\n        .reduce()\n    : minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw.add(\n        tvl.negate(),\n      );\n  // If there is enough input to get to a balanced state\n  const enoughAdaToBalance = adaAvailable.gte(ZERO)\n    ? /*\n       * If the input is positive, then the portfolio can be balanced with this\n       * this interaction if the adaNecessaryToBalance is less than the input\n       * Enough ADA (equivalent) coming in to add to the tokens that need it\n       */\n      adaDiffToBalance.lte(adaAvailable)\n    : /*\n       * If the input is negative, then the portfolio can be balanced with this\n       * this interaction if the adaNecessaryToBalance is greater than the input\n       * Removing enough ADA (equivalent) to the tokens that have too much\n       */\n      adaDiffToBalance.gte(adaAvailable);\n\n  if (enoughAdaToBalance) {\n    const balancedTokens: types.RationalDict = {};\n    for (const [asset, targetWeight] of Object.entries(targetWeights)) {\n      if (targetWeight.eq(ZERO) && adaAvailable.gte(ZERO)) {\n        balancedTokens[asset] = adaPerToken[asset].div(prices[asset]).reduce();\n      } else {\n        balancedTokens[asset] =\n          minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw\n            .mul(targetWeight)\n            .div(prices[asset])\n            .reduce();\n      }\n    }\n    const adaLeft = adaAvailable.add(adaDiffToBalance.negate()).reduce();\n    return [balancedTokens, adaLeft];\n  }\n  // there is not enough to balance it out\n  else {\n    const approximatedBalance: types.RationalDict = {};\n    for (const [asset, assetAda] of Object.entries(adaPerToken)) {\n      if (targetWeights[asset].eq(ZERO) && adaAvailable.gte(ZERO)) {\n        approximatedBalance[asset] = assetAda.div(prices[asset]).reduce();\n      } else {\n        /**\n         * How much of a given token is missing (or too much of) to reach\n         * perfect balance\n         */\n        const tokenAdaDiffToBalance =\n          minTvlIfBalancedAndNoTokensRemovedOnDepositNorAddedOnWithdraw\n            .mul(targetWeights[asset])\n            .add(assetAda.negate());\n\n        approximatedBalance[asset] = adaAvailable\n          .mul(tokenAdaDiffToBalance)\n          .div(adaDiffToBalance)\n          .div(prices[asset])\n          .add(assetAda.div(prices[asset]))\n          .reduce();\n      }\n    }\n    const adaLeft = ZERO;\n    return [approximatedBalance, adaLeft];\n  }\n}\n\n/**\n * Computes how much of each fee in the smart contract this interaction contains\n */\nexport function computeFees({\n  portfolioState,\n  amount,\n  type,\n}: types.FeeComputation) {\n  const batcherFee = portfolioState.batcherFee;\n\n  const platformFee = amount\n    .multipliedBy(portfolioState.platformFee)\n    .div(10_000);\n\n  const microMTKsInOrder = amount.div(portfolioState.microMtkPrice);\n\n  // divided by 10_000 to turn the fee into a multiplying factor\n  const userFee =\n    type === 'mint'\n      ? microMTKsInOrder.multipliedBy(portfolioState.entryFee).div(10_000)\n      : microMTKsInOrder.multipliedBy(portfolioState.exitFee).div(10_000);\n\n  return {\n    /**\n     * Batcher fee in lovelace\n     */\n    batcherFee: batcherFee.abs(),\n    /**\n     * Platform fee in lovelace\n     */\n    platformFee: platformFee.abs(),\n    /**\n     * User fees in microMTKs\n     */\n    userFee: userFee.abs(),\n  };\n}\n","export const MTK_DECIMALS = 6;\n\nexport const FEES = {\n  entry: {\n    max: 500n,\n    min: 1n,\n  },\n  exit: {\n    max: 500n,\n    min: 1n,\n  },\n  batcher: {\n    max: 4_000_000n,\n    min: 1n,\n  },\n  platform: {\n    max: 100n,\n    min: 1n,\n  },\n} as const;\n"]}