Files
237-combo/scripts/test-strategy.ts

183 lines
6.6 KiB
TypeScript
Raw Permalink Normal View History

#!/usr/bin/env tsx
/**
* Test script for DeFi strategy testing
*
* This script can be used to test the strategy framework with a real fork
*
* Usage:
* tsx scripts/test-strategy.ts
*
* Environment variables:
* MAINNET_RPC_URL - RPC URL for mainnet fork (required)
* TEST_SCENARIO - Path to scenario file (default: scenarios/aave/leveraged-long.yml)
* TEST_NETWORK - Network name (default: mainnet)
*/
// Load environment variables FIRST, before any other imports that might use them
import dotenv from 'dotenv';
dotenv.config();
import { readFileSync } from 'fs';
import { join } from 'path';
import { ForkOrchestrator } from '../src/strat/core/fork-orchestrator.js';
import { ScenarioRunner } from '../src/strat/core/scenario-runner.js';
import { loadScenario } from '../src/strat/dsl/scenario-loader.js';
import { AaveV3Adapter } from '../src/strat/adapters/aave-v3-adapter.js';
import { UniswapV3Adapter } from '../src/strat/adapters/uniswap-v3-adapter.js';
import { CompoundV3Adapter } from '../src/strat/adapters/compound-v3-adapter.js';
import { Erc20Adapter } from '../src/strat/adapters/erc20-adapter.js';
import { FailureInjector } from '../src/strat/core/failure-injector.js';
import { JsonReporter } from '../src/strat/reporters/json-reporter.js';
import { HtmlReporter } from '../src/strat/reporters/html-reporter.js';
import { getNetwork } from '../src/strat/config/networks.js';
import type { ProtocolAdapter } from '../src/strat/types.js';
async function main() {
const scenarioPath = process.env.TEST_SCENARIO || 'scenarios/aave/leveraged-long.yml';
const networkName = process.env.TEST_NETWORK || 'mainnet';
// Get RPC URL from env - try network-specific first, then MAINNET_RPC_URL
const networkEnvVar = `${networkName.toUpperCase()}_RPC_URL`;
let rpcUrl = process.env[networkEnvVar] || process.env.MAINNET_RPC_URL;
if (!rpcUrl) {
console.error('ERROR: RPC URL not found');
console.error(` Please set ${networkEnvVar} or MAINNET_RPC_URL in your .env file`);
console.error(' Or create .env from .env.example and fill in your RPC URLs');
process.exit(1);
}
if (rpcUrl.includes('YOUR_KEY') || rpcUrl.includes('YOUR_INFURA_KEY')) {
console.error('ERROR: RPC URL contains placeholder');
console.error(' Please set a real RPC URL in your .env file');
console.error(` Current: ${rpcUrl.substring(0, 50)}...`);
process.exit(1);
}
console.log('='.repeat(60));
console.log('DeFi Strategy Testing - Test Script');
console.log('='.repeat(60));
console.log(`Scenario: ${scenarioPath}`);
console.log(`Network: ${networkName}`);
console.log(`RPC: ${rpcUrl.substring(0, 30)}...`);
console.log('');
try {
// Load scenario
console.log('Loading scenario...');
const scenario = loadScenario(scenarioPath);
console.log(`✓ Loaded scenario with ${scenario.steps.length} steps`);
// Setup network
const network = getNetwork(networkName);
network.rpcUrl = rpcUrl;
// Start fork
console.log('Starting fork...');
const fork = new ForkOrchestrator(network, rpcUrl);
await fork.start();
console.log('✓ Fork started');
// Register adapters
console.log('Registering adapters...');
const adapters = new Map<string, ProtocolAdapter>();
adapters.set('erc20', new Erc20Adapter());
adapters.set('aave-v3', new AaveV3Adapter());
adapters.set('uniswap-v3', new UniswapV3Adapter());
adapters.set('compound-v3', new CompoundV3Adapter());
// Register failure injector
const failureInjector = new FailureInjector(fork);
adapters.set('failure', {
name: 'failure',
discover: async () => ({}),
actions: {
oracleShock: (ctx, args) => failureInjector.oracleShock(ctx, args),
timeTravel: (ctx, args) => failureInjector.timeTravel(ctx, args),
setTimestamp: (ctx, args) => failureInjector.setTimestamp(ctx, args),
liquidityShock: (ctx, args) => failureInjector.liquidityShock(ctx, args),
setBaseFee: (ctx, args) => failureInjector.setBaseFee(ctx, args),
pauseReserve: (ctx, args) => failureInjector.pauseReserve(ctx, args),
capExhaustion: (ctx, args) => failureInjector.capExhaustion(ctx, args),
},
views: {},
});
console.log('✓ Adapters registered');
// Create snapshot
console.log('Creating snapshot...');
const snapshotId = await fork.snapshot('test_start');
console.log(`✓ Snapshot created: ${snapshotId}`);
// Run scenario
console.log('');
console.log('Running scenario...');
console.log('-'.repeat(60));
const runner = new ScenarioRunner(fork, adapters, network);
const report = await runner.run(scenario);
console.log('-'.repeat(60));
// Print summary
console.log('');
console.log('='.repeat(60));
console.log('Run Summary');
console.log('='.repeat(60));
console.log(`Status: ${report.passed ? '✓ PASSED' : '✗ FAILED'}`);
console.log(`Steps: ${report.steps.length}`);
console.log(`Duration: ${((report.endTime! - report.startTime) / 1000).toFixed(2)}s`);
console.log(`Total Gas: ${report.metadata.totalGas.toString()}`);
if (report.error) {
console.log(`Error: ${report.error}`);
}
// Generate reports
const outputDir = 'out';
const timestamp = Date.now();
const jsonPath = join(outputDir, `test-run-${timestamp}.json`);
const htmlPath = join(outputDir, `test-report-${timestamp}.html`);
console.log('');
console.log('Generating reports...');
JsonReporter.generate(report, jsonPath);
HtmlReporter.generate(report, htmlPath);
console.log(`✓ JSON report: ${jsonPath}`);
console.log(`✓ HTML report: ${htmlPath}`);
// Print step details
console.log('');
console.log('Step Results:');
for (const step of report.steps) {
const status = step.result.success ? '✓' : '✗';
const duration = (step.duration / 1000).toFixed(2);
console.log(` ${status} ${step.stepName} (${duration}s)`);
if (!step.result.success) {
console.log(` Error: ${step.result.error}`);
}
if (step.assertions && step.assertions.length > 0) {
const passed = step.assertions.filter(a => a.passed).length;
const total = step.assertions.length;
console.log(` Assertions: ${passed}/${total} passed`);
}
}
// Cleanup
await fork.revert(snapshotId);
await fork.stop();
console.log('');
console.log('='.repeat(60));
console.log('Test completed');
process.exit(report.passed ? 0 : 1);
} catch (error: any) {
console.error('');
console.error('ERROR:', error.message);
console.error(error.stack);
process.exit(1);
}
}
main();