UNPKG

@meteraprotocol/core

Version:

Core functionality and constants for the Metera Protocol

1 lines 16.3 kB
{"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"]}