89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { evaluateTWAPSanity } from "../../../src/guards/twapSanity.js";
|
|
import { UniswapV3Adapter } from "../../../src/adapters/uniswapV3.js";
|
|
import { Guard } from "../../../src/strategy.schema.js";
|
|
|
|
describe("TWAP Sanity Guard", () => {
|
|
let mockUniswap: UniswapV3Adapter;
|
|
let mockProvider: any;
|
|
|
|
beforeEach(() => {
|
|
mockProvider = {
|
|
getNetwork: vi.fn().mockResolvedValue({ chainId: 1n }),
|
|
call: vi.fn(),
|
|
};
|
|
|
|
mockUniswap = new UniswapV3Adapter("mainnet");
|
|
// @ts-ignore - access private property for testing
|
|
mockUniswap.provider = mockProvider;
|
|
});
|
|
|
|
it("should pass when TWAP is within deviation", async () => {
|
|
const guard: Guard = {
|
|
type: "twapSanity",
|
|
params: {
|
|
tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
tokenOut: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
fee: 3000,
|
|
maxDeviationBps: 500, // 5%
|
|
},
|
|
};
|
|
|
|
const context = {
|
|
amountIn: 1000000n,
|
|
expectedAmountOut: 1000000n,
|
|
};
|
|
|
|
// Mock quote that's within deviation
|
|
vi.spyOn(mockUniswap, "quoteExactInput").mockResolvedValue(1010000n); // 1% deviation
|
|
|
|
const result = await evaluateTWAPSanity(guard, mockUniswap, context);
|
|
expect(result.passed).toBe(true);
|
|
});
|
|
|
|
it("should fail when TWAP deviation is too high", async () => {
|
|
const guard: Guard = {
|
|
type: "twapSanity",
|
|
params: {
|
|
tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
tokenOut: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
fee: 3000,
|
|
maxDeviationBps: 100, // 1%
|
|
},
|
|
};
|
|
|
|
const context = {
|
|
amountIn: 1000000n,
|
|
expectedAmountOut: 1000000n,
|
|
};
|
|
|
|
// Mock quote that's 2% off
|
|
vi.spyOn(mockUniswap, "quoteExactInput").mockResolvedValue(980000n); // 2% deviation
|
|
|
|
const result = await evaluateTWAPSanity(guard, mockUniswap, context);
|
|
expect(result.passed).toBe(false);
|
|
expect(result.reason).toContain("deviation");
|
|
});
|
|
|
|
it("should handle missing pool gracefully", async () => {
|
|
const guard: Guard = {
|
|
type: "twapSanity",
|
|
params: {
|
|
tokenIn: "0xInvalid",
|
|
tokenOut: "0xInvalid",
|
|
fee: 3000,
|
|
maxDeviationBps: 500,
|
|
},
|
|
};
|
|
|
|
vi.spyOn(mockUniswap, "quoteExactInput").mockRejectedValue(
|
|
new Error("Pool not found")
|
|
);
|
|
|
|
const result = await evaluateTWAPSanity(guard, mockUniswap, {});
|
|
expect(result.passed).toBe(false);
|
|
expect(result.reason).toBeDefined();
|
|
});
|
|
});
|
|
|