82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
import { DLMMBot } from "../../src/bot";
|
|
import { MeteoraWrapper } from "../../src/meteora";
|
|
import logger from "../../src/utils/logger";
|
|
import { BN } from "@coral-xyz/anchor";
|
|
|
|
jest.mock("../../src/utils/config", () => ({
|
|
connection: { getActiveBin: jest.fn() },
|
|
wallet: { publicKey: { toBase58: () => "wallet1" } },
|
|
REBALANCE_THRESHOLD_PERCENT: 0.15,
|
|
poolAddress: { toBase58: () => "pool1" },
|
|
DRY_RUN: false,
|
|
SLIPPAGE_BPS: 100,
|
|
MAX_RETRIES: 3,
|
|
JLP_USD_ESTIMATE: 3.0,
|
|
SOL_USD_ESTIMATE: 100.0,
|
|
COMPOUND_THRESHOLD_USD: 1.0,
|
|
}));
|
|
jest.mock("../../src/utils/logger", () => ({
|
|
info: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
warn: jest.fn(),
|
|
}));
|
|
jest.mock("../../src/meteora");
|
|
|
|
describe("DLMMBot Rebalance Logic", () => {
|
|
let bot: DLMMBot;
|
|
let mockMeteora: jest.Mocked<MeteoraWrapper>;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
bot = new DLMMBot();
|
|
mockMeteora = (bot as any).meteora;
|
|
mockMeteora.getBalances.mockResolvedValue({ balanceX: new BN(0), balanceY: new BN(0) });
|
|
mockMeteora.getTokenPrice.mockResolvedValue(1.0);
|
|
mockMeteora.dlmmPool = {
|
|
tokenX: { mint: { decimals: 9, address: { toBase58: () => "So11111111111111111111111111111111111111112" } } },
|
|
tokenY: { mint: { decimals: 6, address: { toBase58: () => "JLP" } } }
|
|
} as any;
|
|
});
|
|
|
|
it("should not rebalance if within range", async () => {
|
|
mockMeteora.getActiveBin.mockResolvedValue({ binId: 100 } as any);
|
|
mockMeteora.getPositions.mockResolvedValue([
|
|
{
|
|
publicKey: { toBase58: () => "pos1" },
|
|
positionData: { lowerBinId: 90, upperBinId: 110 },
|
|
},
|
|
] as any);
|
|
|
|
await bot.rebalance();
|
|
|
|
expect(logger.info).toHaveBeenCalledWith(
|
|
expect.stringContaining("Portfolio healthy")
|
|
);
|
|
expect(mockMeteora.withdrawAll).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should trigger rebalance if out of range", async () => {
|
|
mockMeteora.getActiveBin.mockResolvedValue({ binId: 120 } as any);
|
|
mockMeteora.getPositions.mockResolvedValue([
|
|
{
|
|
publicKey: { toBase58: () => "pos1" },
|
|
positionData: { lowerBinId: 90, upperBinId: 110 },
|
|
},
|
|
] as any);
|
|
mockMeteora.getBalances.mockResolvedValue({ balanceX: new BN(100), balanceY: new BN(100) });
|
|
mockMeteora.getTokenPrice.mockResolvedValue(1.0);
|
|
// dlmmPool already mocked in beforeEach
|
|
|
|
await bot.rebalance();
|
|
|
|
expect(mockMeteora.withdrawAll).toHaveBeenCalled();
|
|
expect(mockMeteora.getBalances).toHaveBeenCalled();
|
|
expect(mockMeteora.deposit).toHaveBeenCalledWith(
|
|
new BN(100),
|
|
new BN(100),
|
|
120
|
|
);
|
|
});
|
|
});
|