rspace-online/server/bonding-curve.ts

98 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* CRDT-native bonding curve — pure math functions for $MYCO ↔ cUSDC swaps.
*
* Polynomial curve: price = basePrice + coefficient × supply²
* Ported from payment-infra/services/bonding-curve-service for CRDT ledger integration.
*
* All amounts in base units:
* - cUSDC: 6 decimals (1_000_000 = $1.00)
* - $MYCO: 6 decimals (using 6 effective decimals same as cUSDC for simplicity)
*/
// ── Curve parameters ──
/** Base price in cUSDC units (6 decimals) — $0.01 */
const BASE_PRICE = 10_000;
/** Coefficient — controls curve steepness */
const COEFFICIENT = 0.000001;
/** Exponent — quadratic curve */
const EXPONENT = 2;
/** Sell fee in basis points (0.5% = 50 bps) */
const SELL_FEE_BPS = 50;
// ── Price functions ──
/** Calculate token price at a given supply (both in base units). Returns cUSDC base units per MYCO. */
export function calculatePrice(supplyBaseUnits: number): number {
const supplyTokens = supplyBaseUnits / 1_000_000;
return Math.round(BASE_PRICE + COEFFICIENT * Math.pow(supplyTokens, EXPONENT) * 1_000_000);
}
/** Calculate tokens received for cUSDC input (buy). */
export function calculateBuyReturn(cUSDCAmount: number, currentSupply: number): {
tokensOut: number;
averagePrice: number;
priceImpact: number;
endPrice: number;
} {
const startPrice = calculatePrice(currentSupply);
// First estimate with start price
const estimatedTokens = Math.floor((cUSDCAmount * 1_000_000) / startPrice);
const endPrice = calculatePrice(currentSupply + estimatedTokens);
const averagePrice = Math.round((startPrice + endPrice) / 2);
// Recalculate with average price
const tokensOut = Math.floor((cUSDCAmount * 1_000_000) / averagePrice);
// Price impact as percentage (0-100)
const priceImpact = startPrice > 0
? Number(((endPrice - startPrice) / startPrice * 100).toFixed(2))
: 0;
return { tokensOut, averagePrice, priceImpact, endPrice };
}
/** Calculate cUSDC received for $MYCO input (sell), after fee. */
export function calculateSellReturn(mycoAmount: number, currentSupply: number): {
cUSDCOut: number;
grossAmount: number;
fee: number;
averagePrice: number;
priceImpact: number;
endPrice: number;
} {
if (mycoAmount > currentSupply) {
throw new Error('Cannot sell more than current supply');
}
const startPrice = calculatePrice(currentSupply);
const endPrice = calculatePrice(currentSupply - mycoAmount);
const averagePrice = Math.round((startPrice + endPrice) / 2);
const grossAmount = Math.floor((mycoAmount * averagePrice) / 1_000_000);
const fee = Math.floor((grossAmount * SELL_FEE_BPS) / 10_000);
const cUSDCOut = grossAmount - fee;
const priceImpact = startPrice > 0
? Number(((startPrice - endPrice) / startPrice * 100).toFixed(2))
: 0;
return { cUSDCOut, grossAmount, fee, averagePrice, priceImpact, endPrice };
}
/** Get current curve configuration (for UI display). */
export function getCurveConfig() {
return {
basePrice: BASE_PRICE,
coefficient: COEFFICIENT,
exponent: EXPONENT,
sellFeeBps: SELL_FEE_BPS,
priceDecimals: 6,
tokenDecimals: 6,
};
}