235 lines
8.7 KiB
TypeScript
235 lines
8.7 KiB
TypeScript
import { describe, it, expect, beforeAll } from "vitest";
|
|
import { StrategyCompiler } from "../../src/planner/compiler.js";
|
|
import { loadStrategy, substituteBlinds } from "../../src/strategy.js";
|
|
import { getChainConfig } from "../../src/config/chains.js";
|
|
import { JsonRpcProvider, Contract, getAddress } from "ethers";
|
|
import { join } from "path";
|
|
import * as dotenv from "dotenv";
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
describe("Protocol Integration Tests - Recursive Leverage Strategy", () => {
|
|
const RPC_MAINNET = process.env.RPC_MAINNET;
|
|
const RPC_POLYGON = process.env.RPC_POLYGON;
|
|
const RPC_BASE = process.env.RPC_BASE;
|
|
const RPC_OPTIMISM = process.env.RPC_OPTIMISM;
|
|
|
|
// Aave v3 Pool contract ABI (minimal for testing)
|
|
const AAVE_POOL_ABI = [
|
|
"function getReserveData(address asset) external view returns (tuple(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint40))",
|
|
"function getReservesList() external view returns (address[])",
|
|
];
|
|
|
|
describe("Mainnet Protocol Contracts", () => {
|
|
it.skipIf(!RPC_MAINNET)("should connect to Aave v3 Pool on mainnet", async () => {
|
|
const provider = new JsonRpcProvider(RPC_MAINNET);
|
|
const chainConfig = getChainConfig("mainnet");
|
|
|
|
if (!chainConfig.protocols.aaveV3?.pool) {
|
|
throw new Error("Aave v3 pool address not configured");
|
|
}
|
|
|
|
const poolAddress = getAddress(chainConfig.protocols.aaveV3.pool);
|
|
const poolContract = new Contract(poolAddress, AAVE_POOL_ABI, provider);
|
|
|
|
// Test connection by calling getReservesList
|
|
const reserves = await poolContract.getReservesList();
|
|
expect(reserves).toBeDefined();
|
|
expect(Array.isArray(reserves)).toBe(true);
|
|
expect(reserves.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it.skipIf(!RPC_MAINNET)("should verify Aave v3 Pool address is correct", async () => {
|
|
const provider = new JsonRpcProvider(RPC_MAINNET);
|
|
const chainConfig = getChainConfig("mainnet");
|
|
|
|
if (!chainConfig.protocols.aaveV3?.pool) {
|
|
throw new Error("Aave v3 pool address not configured");
|
|
}
|
|
|
|
const poolAddress = getAddress(chainConfig.protocols.aaveV3.pool);
|
|
|
|
// Verify contract exists by checking code
|
|
const code = await provider.getCode(poolAddress);
|
|
expect(code).not.toBe("0x");
|
|
expect(code.length).toBeGreaterThan(2);
|
|
});
|
|
|
|
it.skipIf(!RPC_MAINNET)("should compile recursive leverage strategy with real protocol addresses", async () => {
|
|
const strategyPath = join(
|
|
process.cwd(),
|
|
"strategies",
|
|
"sample.recursive.json"
|
|
);
|
|
|
|
if (!require("fs").existsSync(strategyPath)) {
|
|
return;
|
|
}
|
|
|
|
const strategy = loadStrategy(strategyPath);
|
|
|
|
// Substitute blind values for testing
|
|
const blindValues = {
|
|
collateralAmount: "1000000", // 1 USDC (6 decimals)
|
|
leverageFactor: "500000", // 0.5 USDC
|
|
};
|
|
const resolvedStrategy = substituteBlinds(strategy, blindValues);
|
|
|
|
const compiler = new StrategyCompiler(resolvedStrategy.chain);
|
|
const plan = await compiler.compile(resolvedStrategy);
|
|
|
|
expect(plan.calls.length).toBeGreaterThan(0);
|
|
|
|
// Verify the compiled calls target the correct Aave pool address
|
|
const chainConfig = getChainConfig("mainnet");
|
|
const aavePoolAddress = chainConfig.protocols.aaveV3?.pool;
|
|
|
|
if (aavePoolAddress) {
|
|
const checksummedPoolAddress = getAddress(aavePoolAddress);
|
|
const supplyCall = plan.calls.find(call =>
|
|
getAddress(call.to).toLowerCase() === checksummedPoolAddress.toLowerCase()
|
|
);
|
|
expect(supplyCall).toBeDefined();
|
|
expect(supplyCall?.description).toContain("Aave");
|
|
}
|
|
});
|
|
|
|
it.skipIf(!RPC_MAINNET)("should verify USDC is a valid reserve in Aave v3", async () => {
|
|
const provider = new JsonRpcProvider(RPC_MAINNET);
|
|
const chainConfig = getChainConfig("mainnet");
|
|
|
|
if (!chainConfig.protocols.aaveV3?.pool) {
|
|
throw new Error("Aave v3 pool address not configured");
|
|
}
|
|
|
|
const poolAddress = getAddress(chainConfig.protocols.aaveV3.pool);
|
|
const poolContract = new Contract(poolAddress, AAVE_POOL_ABI, provider);
|
|
|
|
// USDC address on mainnet
|
|
const USDC_ADDRESS = getAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
|
|
|
|
// Get reserve data for USDC
|
|
const reserveData = await poolContract.getReserveData(USDC_ADDRESS);
|
|
expect(reserveData).toBeDefined();
|
|
|
|
// Verify it's a valid reserve (liquidityIndex should be > 0)
|
|
const liquidityIndex = reserveData[0];
|
|
expect(liquidityIndex).toBeDefined();
|
|
expect(Number(liquidityIndex)).toBeGreaterThan(0);
|
|
});
|
|
|
|
it.skipIf(!RPC_MAINNET)("should verify Uniswap V3 Router address", async () => {
|
|
const provider = new JsonRpcProvider(RPC_MAINNET);
|
|
const chainConfig = getChainConfig("mainnet");
|
|
|
|
if (!chainConfig.protocols.uniswapV3?.router) {
|
|
throw new Error("Uniswap V3 router address not configured");
|
|
}
|
|
|
|
const routerAddress = getAddress(chainConfig.protocols.uniswapV3.router);
|
|
|
|
// Verify contract exists
|
|
const code = await provider.getCode(routerAddress);
|
|
expect(code).not.toBe("0x");
|
|
expect(code.length).toBeGreaterThan(2);
|
|
});
|
|
});
|
|
|
|
describe("Polygon Protocol Contracts", () => {
|
|
it.skipIf(!RPC_POLYGON)("should connect to Polygon RPC and verify chain config", async () => {
|
|
const provider = new JsonRpcProvider(RPC_POLYGON);
|
|
// Note: Polygon chain config may not be implemented yet
|
|
// This test verifies RPC connectivity only
|
|
|
|
// Verify we can connect
|
|
const blockNumber = await provider.getBlockNumber();
|
|
expect(blockNumber).toBeGreaterThan(0);
|
|
|
|
// Verify chain ID matches Polygon
|
|
const network = await provider.getNetwork();
|
|
expect(network.chainId).toBe(BigInt(137));
|
|
});
|
|
});
|
|
|
|
describe("Base Protocol Contracts", () => {
|
|
it.skipIf(!RPC_BASE)("should connect to Base RPC and verify chain config", async () => {
|
|
const provider = new JsonRpcProvider(RPC_BASE);
|
|
const chainConfig = getChainConfig("base");
|
|
|
|
// Verify we can connect
|
|
const blockNumber = await provider.getBlockNumber();
|
|
expect(blockNumber).toBeGreaterThan(0);
|
|
|
|
// Verify chain ID matches
|
|
const network = await provider.getNetwork();
|
|
expect(network.chainId).toBe(BigInt(8453));
|
|
});
|
|
});
|
|
|
|
describe("Optimism Protocol Contracts", () => {
|
|
it.skipIf(!RPC_OPTIMISM)("should connect to Optimism RPC and verify chain config", async () => {
|
|
const provider = new JsonRpcProvider(RPC_OPTIMISM);
|
|
const chainConfig = getChainConfig("optimism");
|
|
|
|
// Verify we can connect
|
|
const blockNumber = await provider.getBlockNumber();
|
|
expect(blockNumber).toBeGreaterThan(0);
|
|
|
|
// Verify chain ID matches
|
|
const network = await provider.getNetwork();
|
|
expect(network.chainId).toBe(BigInt(10));
|
|
});
|
|
});
|
|
|
|
describe("Recursive Leverage Strategy - Full Integration", () => {
|
|
it.skipIf(!RPC_MAINNET)("should compile and validate recursive leverage strategy against real contracts", async () => {
|
|
const strategyPath = join(
|
|
process.cwd(),
|
|
"strategies",
|
|
"sample.recursive.json"
|
|
);
|
|
|
|
if (!require("fs").existsSync(strategyPath)) {
|
|
return;
|
|
}
|
|
|
|
const provider = new JsonRpcProvider(RPC_MAINNET);
|
|
const strategy = loadStrategy(strategyPath);
|
|
const chainConfig = getChainConfig(strategy.chain);
|
|
|
|
// Substitute blind values
|
|
const blindValues = {
|
|
collateralAmount: "1000000", // 1 USDC
|
|
leverageFactor: "500000", // 0.5 USDC
|
|
};
|
|
const resolvedStrategy = substituteBlinds(strategy, blindValues);
|
|
|
|
// Compile strategy
|
|
const compiler = new StrategyCompiler(resolvedStrategy.chain);
|
|
const plan = await compiler.compile(resolvedStrategy);
|
|
|
|
expect(plan.calls.length).toBeGreaterThan(0);
|
|
|
|
// Verify all calls target valid contract addresses
|
|
for (const call of plan.calls) {
|
|
const callAddress = getAddress(call.to);
|
|
const code = await provider.getCode(callAddress);
|
|
expect(code).not.toBe("0x");
|
|
expect(code.length).toBeGreaterThan(2);
|
|
}
|
|
|
|
// Verify Aave pool address matches configuration
|
|
const aavePoolAddress = chainConfig.protocols.aaveV3?.pool;
|
|
if (aavePoolAddress) {
|
|
const checksummedPoolAddress = getAddress(aavePoolAddress);
|
|
const aaveCalls = plan.calls.filter(call =>
|
|
getAddress(call.to).toLowerCase() === checksummedPoolAddress.toLowerCase()
|
|
);
|
|
expect(aaveCalls.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|