@ocap/util
Version:
utils shared across multiple forge js libs, works in both node.js and browser
160 lines (158 loc) • 6.07 kB
JavaScript
const require_bn = require('./bn.cjs');
//#region src/curve.ts
const ZERO = new require_bn.BN(0);
const TWO = new require_bn.BN(2);
const THREE = new require_bn.BN(3);
const TEN = new require_bn.BN(10);
let Direction = /* @__PURE__ */ function(Direction$1) {
Direction$1["Mint"] = "mint";
Direction$1["Burn"] = "burn";
return Direction$1;
}({});
/**
* Constant Curve
* Price = fixedPrice
*/
function calcConstantPrice({ fixedPrice }) {
return new require_bn.BN(fixedPrice);
}
/**
* Constant Curve
* Cost = amount * fixedPrice
*/
function calcConstantCost({ amount, fixedPrice, decimal }) {
const decimalFactor = TEN.pow(new require_bn.BN(decimal));
return new require_bn.BN(amount).mul(new require_bn.BN(fixedPrice)).div(decimalFactor);
}
/**
* linear Curve
* Price = basePrice + slope * currentSupply
*/
function calcLinearPrice({ basePrice, slope, currentSupply, decimal }) {
const decimalFactor = TEN.pow(new require_bn.BN(decimal));
return new require_bn.BN(basePrice).add(new require_bn.BN(slope).mul(new require_bn.BN(currentSupply)).div(decimalFactor));
}
/**
* Linear Curve
* mint Cost = (slope / 2) * ( (currentSupply + amount) ** 2 - currentSupply ** 2 ) + basePrice * amount;
* burn Cost = (slope / 2) * ( currentSupply ** 2 - (currentSupply - amount) ** 2 ) + basePrice * amount;
*/
function calcLinearCost(params) {
const amount = new require_bn.BN(params.amount);
const currentSupply = new require_bn.BN(params.currentSupply);
const basePrice = new require_bn.BN(params.basePrice);
const slope = new require_bn.BN(params.slope);
const decimalFactor = TEN.pow(new require_bn.BN(params.decimal));
if (slope.lte(ZERO)) throw new Error("INVALID_SLOPE");
if (amount.lt(ZERO)) throw new Error("INVALID_AMOUNT");
if (currentSupply.lt(ZERO)) throw new Error("INVALID_CURRENT_SUPPLY");
if (basePrice.lt(ZERO)) throw new Error("INVALID_BASE_PRICE");
if (decimalFactor.lte(ZERO)) throw new Error("INVALID_DECIMAL_FACTOR");
if (params.direction === Direction.Burn && amount.gt(currentSupply)) throw new Error("SUPPLY_INSUFFICIENT");
const integralDelta = params.direction === Direction.Mint ? amount.mul(currentSupply.mul(TWO).add(amount)) : amount.mul(currentSupply.mul(TWO).sub(amount));
const linearTerm = basePrice.mul(amount).div(decimalFactor);
const integralTerm = slope.mul(integralDelta).div(TWO.mul(decimalFactor.sqr()));
return linearTerm.add(integralTerm);
}
/**
* Quadratic Curve
* Price = basePrice + currentSupply ** 2 / constant
*/
function calcQuadraticPrice(params) {
const basePrice = new require_bn.BN(params.basePrice);
const currentSupply = new require_bn.BN(params.currentSupply);
const constant = new require_bn.BN(params.constant);
const decimalFactor = TEN.pow(new require_bn.BN(params.decimal));
if (constant.lte(ZERO)) throw new Error("INVALID_CONSTANT");
if (currentSupply.lt(ZERO)) throw new Error("INVALID_CURRENT_SUPPLY");
if (basePrice.lt(ZERO)) throw new Error("INVALID_BASE_PRICE");
if (decimalFactor.lte(ZERO)) throw new Error("INVALID_DECIMAL_FACTOR");
const denom = constant.mul(decimalFactor);
const a = currentSupply.sqr().div(denom);
return basePrice.add(a);
}
/**
* Quadratic Curve
* Price = basePrice + currentSupply ** 2 / constant
* mint Cost = ( (currentSupply + amount)^3 / 3 - currentSupply^3 / 3 ) / constant + basePrice * amount;
* burn Cost = ( currentSupply^3 / 3 - (currentSupply - amount)^3 / 3 ) / constant + basePrice * amount;
*/
function calcQuadraticCost(params) {
const amount = new require_bn.BN(params.amount);
const currentSupply = new require_bn.BN(params.currentSupply);
const basePrice = new require_bn.BN(params.basePrice);
const constant = new require_bn.BN(params.constant);
const decimalFactor = TEN.pow(new require_bn.BN(params.decimal));
if (constant.lte(ZERO)) throw new Error("INVALID_CONSTANT");
if (amount.lt(ZERO)) throw new Error("INVALID_AMOUNT");
if (amount.isZero()) return ZERO;
const newSupply = params.direction === Direction.Mint ? currentSupply.add(amount) : currentSupply.sub(amount);
if (params.direction === Direction.Burn && newSupply.lt(ZERO)) throw new Error("SUPPLY_INSUFFICIENT");
const diff = params.direction === Direction.Mint ? newSupply.sub(currentSupply) : currentSupply.sub(newSupply);
const sumSquaresAndProduct = newSupply.sqr().add(newSupply.mul(currentSupply)).add(currentSupply.sqr());
const cubicDelta = diff.mul(sumSquaresAndProduct);
const denom = THREE.mul(constant).mul(decimalFactor.sqr());
const cubicCost = cubicDelta.div(denom);
const linearCost = basePrice.mul(amount).div(decimalFactor);
return cubicCost.add(linearCost);
}
function calcFee({ reserveAmount, feeRate }) {
return new require_bn.BN(reserveAmount).mul(new require_bn.BN(feeRate)).div(new require_bn.BN(1e4));
}
function calcPrice({ currentSupply, decimal, curve }) {
const calculator = {
constant: () => calcConstantPrice({
currentSupply,
decimal,
...curve
}),
linear: () => calcLinearPrice({
currentSupply,
decimal,
...curve
}),
quadratic: () => calcQuadraticPrice({
currentSupply,
decimal,
...curve
})
}[curve.type];
if (!calculator) throw new Error("INVALID_CURVE_TYPE");
return calculator();
}
function calcCost({ amount, decimal, currentSupply, direction, curve }) {
const calculator = {
constant: () => calcConstantCost({
amount,
decimal,
...curve
}),
linear: () => calcLinearCost({
amount,
decimal,
currentSupply,
direction,
...curve
}),
quadratic: () => calcQuadraticCost({
amount,
decimal,
currentSupply,
direction,
...curve
})
}[curve.type];
if (!calculator) throw new Error("INVALID_CURVE_TYPE");
return calculator();
}
//#endregion
exports.Direction = Direction;
exports.calcConstantCost = calcConstantCost;
exports.calcConstantPrice = calcConstantPrice;
exports.calcCost = calcCost;
exports.calcFee = calcFee;
exports.calcLinearCost = calcLinearCost;
exports.calcLinearPrice = calcLinearPrice;
exports.calcPrice = calcPrice;
exports.calcQuadraticCost = calcQuadraticCost;
exports.calcQuadraticPrice = calcQuadraticPrice;