Skip to content

Commit a51327c

Browse files
committed
make getprice work just like our usual price calc incl decimals
1 parent 41e2407 commit a51327c

File tree

3 files changed

+168
-20
lines changed

3 files changed

+168
-20
lines changed

packages/cli/src/menus/pool.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import type { State } from "../types";
2323
import { getPoolData, prettyAssetId } from "../utils.js";
2424
import { ensureDeployment, getAssetAmount, printHeader } from "./shared.js";
2525
import { transactionDialog } from "./transaction.js";
26-
import { StableSwapsPool } from "@sundaeswap/math";
2726

2827
export async function swapMenu(state: State): Promise<State> {
2928
await printHeader(state);
@@ -37,14 +36,7 @@ export async function swapMenu(state: State): Promise<State> {
3736
assetId: swapFrom.id,
3837
} as IPoolByAssetQuery)) as IPoolData[];
3938
let choices = pools!.map((pool) => {
40-
const price =
41-
pool.version === "Stableswaps"
42-
? StableSwapsPool.getPrice(
43-
pool.liquidity.aReserve,
44-
pool.liquidity.bReserve,
45-
pool.linearAmplificationFactor!,
46-
).toNumber()
47-
: Number(pool.liquidity.aReserve) / Number(pool.liquidity.bReserve);
39+
const price = SundaeUtils.getPrice(pool);
4840
return {
4941
name: `${prettyAssetId(pool.assetA.assetId.toString())} / ${prettyAssetId(
5042
pool.assetB.assetId.toString(),

packages/core/src/Utilities/SundaeUtils.class.ts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -583,19 +583,44 @@ export class SundaeUtils {
583583

584584
/**
585585
* Calculates the price ratio between assets in a pool.
586-
* For Stableswaps pools, uses the amplification factor for accurate pricing.
587-
* For other pool types, returns the simple ratio of reserves.
586+
* - Accounts for decimal differences between assets
587+
* - For ADA pairs: returns ADA per token (assetA / assetB since ADA is always assetA)
588+
* - For exotic pairs: returns inverted ratio (assetB / assetA)
589+
* - For Stableswaps pools, uses the amplification factor for accurate pricing
588590
*
589-
* @param {IPoolData} pool - The pool data containing reserves and version information.
590-
* @returns {number} The price as a number representing the ratio of asset A to asset B.
591+
* @param {IPoolData} pool - The pool data containing reserves, decimals, and version information.
592+
* @returns {number} The price ratio adjusted for decimals.
591593
*/
592594
static getPrice(pool: IPoolData): number {
593-
return pool.version === "Stableswaps"
594-
? StableSwapsPool.getPrice(
595-
pool.liquidity.aReserve,
596-
pool.liquidity.bReserve,
597-
pool.linearAmplificationFactor!,
598-
).toNumber()
599-
: Number(pool.liquidity.aReserve) / Number(pool.liquidity.bReserve);
595+
// Create AssetAmounts to handle decimal normalization
596+
const assetAmountA = new AssetAmount<IAssetAmountMetadata>(
597+
pool.liquidity.aReserve,
598+
pool.assetA,
599+
);
600+
const assetAmountB = new AssetAmount<IAssetAmountMetadata>(
601+
pool.liquidity.bReserve,
602+
pool.assetB,
603+
);
604+
605+
const isAdaPair = SundaeUtils.isAdaAsset(pool.assetA);
606+
607+
if (pool.version === EContractVersion.Stableswaps) {
608+
// For stableswaps, both assets always have the same decimals
609+
const price = StableSwapsPool.getPrice(
610+
pool.liquidity.aReserve,
611+
pool.liquidity.bReserve,
612+
pool.linearAmplificationFactor!,
613+
).toNumber();
614+
615+
// For exotic pairs, invert the price
616+
return isAdaPair ? price : 1 / price;
617+
}
618+
619+
// For constant product pools, use the decimal-aware AssetAmount values
620+
// ADA pairs: assetA (ADA) / assetB
621+
// Exotic pairs: assetB / assetA (inverted)
622+
return isAdaPair
623+
? assetAmountA.value.divide(assetAmountB.value).toNumber()
624+
: assetAmountB.value.divide(assetAmountA.value).toNumber();
600625
}
601626
}

packages/core/src/Utilities/__tests__/SundaeUtils.class.test.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,4 +523,135 @@ describe("SundaeUtils class", () => {
523523
);
524524
});
525525
});
526+
527+
describe("getPrice", () => {
528+
it("should return price with decimal adjustment for ADA pairs (v1 pool)", () => {
529+
// v1 pool: ADA (6 decimals) / TINDY (0 decimals)
530+
// aReserve: 500000000n (500 ADA), bReserve: 250000000n (250M TINDY)
531+
// Price should be: (500 ADA) / (250M TINDY) = 0.000002 ADA per TINDY
532+
const price = SundaeUtils.getPrice(PREVIEW_DATA.pools.v1);
533+
// Raw ratio without decimals would be: 500000000 / 250000000 = 2
534+
// With decimal adjustment (ADA has 6 decimals, TINDY has 0):
535+
// 2 * 10^(0-6) = 2 * 0.000001 = 0.000002
536+
expect(price).toBeCloseTo(0.000002, 8);
537+
});
538+
539+
it("should return price with decimal adjustment for ADA pairs (v3 pool)", () => {
540+
// v3 pool: ADA (6 decimals) / TINDY (0 decimals)
541+
// aReserve: 1018800000n (~1018.8 ADA), bReserve: 992067448n (~992M TINDY)
542+
const price = SundaeUtils.getPrice(PREVIEW_DATA.pools.v3);
543+
// Raw ratio: 1018800000 / 992067448 ≈ 1.0269
544+
// With decimal adjustment: 1.0269 * 10^(0-6) ≈ 0.0000010269
545+
expect(price).toBeCloseTo(0.0000010269, 8);
546+
});
547+
548+
it("should handle exotic pairs (neither asset is ADA)", () => {
549+
// Create a mock exotic pair pool
550+
const exoticPool: IPoolData = {
551+
...PREVIEW_DATA.pools.v1,
552+
assetA: {
553+
assetId:
554+
"99b071ce8580d6a3a11b4902145adb8bfd0d2a03935af8cf66403e15.55534443",
555+
decimals: 6, // USDC
556+
},
557+
assetB: {
558+
assetId:
559+
"fa3eff2047fdf9293c5feef4dc85ce58097ea1c6da4845a351535183.74494e4459",
560+
decimals: 0, // TINDY
561+
},
562+
liquidity: {
563+
aReserve: 1000000000n, // 1000 USDC
564+
bReserve: 500000000n, // 500M TINDY
565+
lpTotal: 353553390n,
566+
},
567+
};
568+
569+
const price = SundaeUtils.getPrice(exoticPool);
570+
// For exotic pairs, returns B/A (inverted) with decimal adjustment
571+
// 500M TINDY / 1000 USDC = 500000 TINDY per USDC
572+
expect(price).toBeCloseTo(500000, 0);
573+
});
574+
575+
it("should handle same decimal assets", () => {
576+
// Two assets with same decimals (e.g., two 6-decimal stablecoins)
577+
const sameDecimalPool: IPoolData = {
578+
...PREVIEW_DATA.pools.v1,
579+
assetA: {
580+
assetId: "ada.lovelace",
581+
decimals: 6,
582+
},
583+
assetB: {
584+
assetId:
585+
"99b071ce8580d6a3a11b4902145adb8bfd0d2a03935af8cf66403e15.55534443",
586+
decimals: 6, // USDC
587+
},
588+
liquidity: {
589+
aReserve: 1000000000n, // 1000 ADA
590+
bReserve: 1000000000n, // 1000 USDC
591+
lpTotal: 1000000000n,
592+
},
593+
};
594+
595+
const price = SundaeUtils.getPrice(sameDecimalPool);
596+
// 1000 ADA / 1000 USDC = 1.0 ADA per USDC
597+
expect(price).toBeCloseTo(1.0, 6);
598+
});
599+
600+
it("should handle stableswap pools", () => {
601+
// Create a stableswap pool mock
602+
const stableswapPool: IPoolData = {
603+
...PREVIEW_DATA.pools.v1,
604+
version: EContractVersion.Stableswaps,
605+
assetA: {
606+
assetId: "ada.lovelace",
607+
decimals: 6,
608+
},
609+
assetB: {
610+
assetId:
611+
"99b071ce8580d6a3a11b4902145adb8bfd0d2a03935af8cf66403e15.55534443",
612+
decimals: 6, // USDC
613+
},
614+
liquidity: {
615+
aReserve: 20_000_000n, // 20 ADA
616+
bReserve: 20_000_000n, // 20 USDC
617+
lpTotal: 40_000_000n,
618+
},
619+
linearAmplificationFactor: 200n,
620+
};
621+
622+
const price = SundaeUtils.getPrice(stableswapPool);
623+
// For balanced stableswap pool, price should be close to 1.0
624+
expect(price).toBeCloseTo(1.0, 4);
625+
});
626+
627+
it("should handle unbalanced stableswap pools", () => {
628+
// Stableswap with slightly unbalanced reserves (exotic pair)
629+
const stableswapPool: IPoolData = {
630+
...PREVIEW_DATA.pools.v1,
631+
version: EContractVersion.Stableswaps,
632+
assetA: {
633+
assetId:
634+
"99b071ce8580d6a3a11b4902145adb8bfd0d2a03935af8cf66403e15.55534443",
635+
decimals: 6, // USDC
636+
},
637+
assetB: {
638+
assetId:
639+
"99b071ce8580d6a3a11b4902145adb8bfd0d2a03935af8cf66403e15.55534454",
640+
decimals: 6, // USDT
641+
},
642+
liquidity: {
643+
aReserve: 1010000000n, // 1010 USDC
644+
bReserve: 1000000000n, // 1000 USDT
645+
lpTotal: 2000000000n,
646+
},
647+
linearAmplificationFactor: 200n,
648+
};
649+
650+
const price = SundaeUtils.getPrice(stableswapPool);
651+
// For exotic pair stableswap, price is inverted (1/rawPrice)
652+
// Raw price is slightly > 1.0, so inverted is slightly < 1.0
653+
expect(price).toBeGreaterThan(0.98);
654+
expect(price).toBeLessThan(1.0);
655+
});
656+
});
526657
});

0 commit comments

Comments
 (0)