Consolidate webapp structure by merging nested components into the main repository
This commit is contained in:
685
docs/Simulation_Engine_Spec.md
Normal file
685
docs/Simulation_Engine_Spec.md
Normal file
@@ -0,0 +1,685 @@
|
||||
# Simulation Engine Specification
|
||||
|
||||
## Overview
|
||||
This document specifies the optional simulation engine for the ISO-20022 Combo Flow system. The simulation engine provides dry-run execution logic, gas estimation, slippage calculation, liquidity checks, failure prediction, and result presentation. It is toggleable for advanced users per requirement 2b.
|
||||
|
||||
---
|
||||
|
||||
## 1. Simulation Engine Architecture
|
||||
|
||||
### High-Level Design
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Combo Builder UI │
|
||||
│ [Simulation Toggle: ON/OFF] │
|
||||
└────────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Simulation Engine API │
|
||||
│ POST /api/plans/{planId}/simulate │
|
||||
└──────────────┬──────────────────────────────┬───────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ DLT Simulator │ │ Fiat Simulator │
|
||||
│ │ │ │
|
||||
│ • Gas Estimation│ │ • Bank Routing │
|
||||
│ • Slippage Calc │ │ • Fee Calculation│
|
||||
│ • Liquidity Check│ │ • Settlement Time│
|
||||
└──────────────────┘ └──────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Price Oracles │ │ Bank APIs │
|
||||
│ (On-Chain) │ │ (Off-Chain) │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. API Specification
|
||||
|
||||
### Endpoint: `POST /api/plans/{planId}/simulate`
|
||||
|
||||
```typescript
|
||||
interface SimulationRequest {
|
||||
planId: string;
|
||||
options?: {
|
||||
includeGasEstimate?: boolean; // Default: true
|
||||
includeSlippageAnalysis?: boolean; // Default: true
|
||||
includeLiquidityCheck?: boolean; // Default: true
|
||||
includeBankRouting?: boolean; // Default: true (for fiat steps)
|
||||
chainId?: number; // Default: current chain
|
||||
};
|
||||
}
|
||||
|
||||
interface SimulationResponse {
|
||||
planId: string;
|
||||
status: 'SUCCESS' | 'FAILURE' | 'PARTIAL';
|
||||
steps: SimulationStepResult[];
|
||||
summary: {
|
||||
gasEstimate: number;
|
||||
estimatedCost: number; // USD
|
||||
totalSlippage: number; // Percentage
|
||||
executionTime: number; // Seconds
|
||||
};
|
||||
slippageAnalysis: SlippageAnalysis;
|
||||
liquidityCheck: LiquidityCheck;
|
||||
warnings: string[];
|
||||
errors: string[];
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Response Structure
|
||||
|
||||
```typescript
|
||||
interface SimulationStepResult {
|
||||
stepIndex: number;
|
||||
stepType: 'borrow' | 'swap' | 'repay' | 'pay';
|
||||
status: 'SUCCESS' | 'FAILURE' | 'WARNING';
|
||||
message: string;
|
||||
estimatedOutput?: {
|
||||
token: string;
|
||||
amount: number;
|
||||
};
|
||||
gasEstimate?: number;
|
||||
slippage?: number;
|
||||
liquidityStatus?: 'SUFFICIENT' | 'INSUFFICIENT' | 'LOW';
|
||||
bankRouting?: {
|
||||
estimatedTime: number; // Minutes
|
||||
fee: number;
|
||||
currency: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface SlippageAnalysis {
|
||||
expectedSlippage: number; // Percentage
|
||||
riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
|
||||
liquidityDepth: number; // Total liquidity in pool
|
||||
priceImpact: number; // Percentage
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
interface LiquidityCheck {
|
||||
sufficient: boolean;
|
||||
poolDepth: number;
|
||||
requiredAmount: number;
|
||||
availableAmount: number;
|
||||
warnings: string[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Dry-Run Execution Logic
|
||||
|
||||
### Step-by-Step Simulation
|
||||
|
||||
```typescript
|
||||
class SimulationEngine {
|
||||
async simulatePlan(plan: Plan, options: SimulationOptions): Promise<SimulationResponse> {
|
||||
const results: SimulationStepResult[] = [];
|
||||
let cumulativeGas = 0;
|
||||
let totalSlippage = 0;
|
||||
const warnings: string[] = [];
|
||||
const errors: string[] = [];
|
||||
|
||||
// Simulate each step sequentially
|
||||
for (let i = 0; i < plan.steps.length; i++) {
|
||||
const step = plan.steps[i];
|
||||
const stepResult = await this.simulateStep(step, i, plan, options);
|
||||
|
||||
results.push(stepResult);
|
||||
|
||||
if (stepResult.status === 'FAILURE') {
|
||||
errors.push(`Step ${i + 1} failed: ${stepResult.message}`);
|
||||
return {
|
||||
status: 'FAILURE',
|
||||
steps: results,
|
||||
errors,
|
||||
warnings
|
||||
};
|
||||
}
|
||||
|
||||
if (stepResult.status === 'WARNING') {
|
||||
warnings.push(`Step ${i + 1}: ${stepResult.message}`);
|
||||
}
|
||||
|
||||
cumulativeGas += stepResult.gasEstimate || 0;
|
||||
totalSlippage += stepResult.slippage || 0;
|
||||
}
|
||||
|
||||
// Aggregate results
|
||||
return {
|
||||
status: 'SUCCESS',
|
||||
steps: results,
|
||||
summary: {
|
||||
gasEstimate: cumulativeGas,
|
||||
estimatedCost: this.calculateCost(cumulativeGas),
|
||||
totalSlippage,
|
||||
executionTime: this.estimateExecutionTime(plan)
|
||||
},
|
||||
slippageAnalysis: this.analyzeSlippage(results),
|
||||
liquidityCheck: this.checkLiquidity(results),
|
||||
warnings,
|
||||
errors: []
|
||||
};
|
||||
}
|
||||
|
||||
async simulateStep(
|
||||
step: PlanStep,
|
||||
index: number,
|
||||
plan: Plan,
|
||||
options: SimulationOptions
|
||||
): Promise<SimulationStepResult> {
|
||||
switch (step.type) {
|
||||
case 'borrow':
|
||||
return await this.simulateBorrow(step, index);
|
||||
case 'swap':
|
||||
return await this.simulateSwap(step, index, options);
|
||||
case 'repay':
|
||||
return await this.simulateRepay(step, index);
|
||||
case 'pay':
|
||||
return await this.simulatePay(step, index, options);
|
||||
default:
|
||||
return {
|
||||
stepIndex: index,
|
||||
stepType: step.type,
|
||||
status: 'FAILURE',
|
||||
message: 'Unknown step type'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DeFi Step Simulation
|
||||
|
||||
```typescript
|
||||
async simulateSwap(
|
||||
step: SwapStep,
|
||||
index: number,
|
||||
options: SimulationOptions
|
||||
): Promise<SimulationStepResult> {
|
||||
// 1. Get current price from oracle
|
||||
const currentPrice = await this.priceOracle.getPrice(step.from, step.to);
|
||||
|
||||
// 2. Calculate slippage
|
||||
const slippage = await this.calculateSlippage(step.from, step.to, step.amount);
|
||||
|
||||
// 3. Check liquidity
|
||||
const liquidity = await this.liquidityChecker.check(step.from, step.to, step.amount);
|
||||
|
||||
// 4. Estimate gas
|
||||
const gasEstimate = await this.gasEstimator.estimateSwap(step.from, step.to, step.amount);
|
||||
|
||||
// 5. Calculate expected output
|
||||
const expectedOutput = step.amount * currentPrice * (1 - slippage / 100);
|
||||
|
||||
// 6. Validate minimum receive
|
||||
if (step.minRecv && expectedOutput < step.minRecv) {
|
||||
return {
|
||||
stepIndex: index,
|
||||
stepType: 'swap',
|
||||
status: 'FAILURE',
|
||||
message: `Expected output ${expectedOutput} is below minimum ${step.minRecv}`,
|
||||
estimatedOutput: { token: step.to, amount: expectedOutput },
|
||||
slippage,
|
||||
liquidityStatus: liquidity.status
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
stepIndex: index,
|
||||
stepType: 'swap',
|
||||
status: liquidity.sufficient ? 'SUCCESS' : 'WARNING',
|
||||
message: liquidity.sufficient ? 'Swap would succeed' : 'Low liquidity warning',
|
||||
estimatedOutput: { token: step.to, amount: expectedOutput },
|
||||
gasEstimate,
|
||||
slippage,
|
||||
liquidityStatus: liquidity.status
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Fiat Step Simulation
|
||||
|
||||
```typescript
|
||||
async simulatePay(
|
||||
step: PayStep,
|
||||
index: number,
|
||||
options: SimulationOptions
|
||||
): Promise<SimulationStepResult> {
|
||||
// 1. Validate IBAN
|
||||
if (!this.validateIBAN(step.beneficiary.IBAN)) {
|
||||
return {
|
||||
stepIndex: index,
|
||||
stepType: 'pay',
|
||||
status: 'FAILURE',
|
||||
message: 'Invalid IBAN format'
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Get bank routing info
|
||||
const routing = await this.bankRouter.getRouting(step.beneficiary.IBAN, step.asset);
|
||||
|
||||
// 3. Calculate fees
|
||||
const fee = await this.feeCalculator.calculateFiatFee(step.amount, step.asset, routing);
|
||||
|
||||
// 4. Estimate settlement time
|
||||
const settlementTime = await this.settlementEstimator.estimate(step.asset, routing);
|
||||
|
||||
return {
|
||||
stepIndex: index,
|
||||
stepType: 'pay',
|
||||
status: 'SUCCESS',
|
||||
message: 'Payment would be processed',
|
||||
bankRouting: {
|
||||
estimatedTime: settlementTime,
|
||||
fee,
|
||||
currency: step.asset
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Gas Estimation
|
||||
|
||||
### Gas Estimation Strategy
|
||||
|
||||
```typescript
|
||||
class GasEstimator {
|
||||
async estimateSwap(tokenIn: string, tokenOut: string, amount: number): Promise<number> {
|
||||
// Base gas for swap
|
||||
const baseGas = 150000;
|
||||
|
||||
// Additional gas for complex routing
|
||||
const routingGas = await this.estimateRoutingGas(tokenIn, tokenOut);
|
||||
|
||||
// Gas for token approvals (if needed)
|
||||
const approvalGas = await this.estimateApprovalGas(tokenIn);
|
||||
|
||||
return baseGas + routingGas + approvalGas;
|
||||
}
|
||||
|
||||
async estimateBorrow(asset: string, amount: number): Promise<number> {
|
||||
// Base gas for borrow
|
||||
const baseGas = 200000;
|
||||
|
||||
// Gas for collateral check
|
||||
const collateralGas = 50000;
|
||||
|
||||
// Gas for LTV calculation
|
||||
const ltvGas = 30000;
|
||||
|
||||
return baseGas + collateralGas + ltvGas;
|
||||
}
|
||||
|
||||
async estimateFullPlan(plan: Plan): Promise<number> {
|
||||
let totalGas = 21000; // Base transaction gas
|
||||
|
||||
for (const step of plan.steps) {
|
||||
switch (step.type) {
|
||||
case 'borrow':
|
||||
totalGas += await this.estimateBorrow(step.asset, step.amount);
|
||||
break;
|
||||
case 'swap':
|
||||
totalGas += await this.estimateSwap(step.from, step.to, step.amount);
|
||||
break;
|
||||
case 'repay':
|
||||
totalGas += 100000; // Standard repay gas
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add handler overhead
|
||||
totalGas += 50000;
|
||||
|
||||
return totalGas;
|
||||
}
|
||||
|
||||
calculateCost(gas: number, gasPrice: number): number {
|
||||
// gasPrice in gwei, convert to ETH then USD
|
||||
const ethCost = (gas * gasPrice * 1e9) / 1e18;
|
||||
const usdCost = ethCost * await this.getETHPrice();
|
||||
return usdCost;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Slippage Calculation
|
||||
|
||||
### Slippage Calculation Logic
|
||||
|
||||
```typescript
|
||||
class SlippageCalculator {
|
||||
async calculateSlippage(
|
||||
tokenIn: string,
|
||||
tokenOut: string,
|
||||
amountIn: number
|
||||
): Promise<number> {
|
||||
// Get current pool reserves
|
||||
const reserves = await this.getPoolReserves(tokenIn, tokenOut);
|
||||
|
||||
// Calculate price impact using constant product formula (x * y = k)
|
||||
const priceImpact = this.calculatePriceImpact(
|
||||
reserves.tokenIn,
|
||||
reserves.tokenOut,
|
||||
amountIn
|
||||
);
|
||||
|
||||
// Add fixed fee (e.g., 0.3% for Uniswap)
|
||||
const protocolFee = 0.3;
|
||||
|
||||
// Total slippage = price impact + protocol fee
|
||||
const totalSlippage = priceImpact + protocolFee;
|
||||
|
||||
return totalSlippage;
|
||||
}
|
||||
|
||||
calculatePriceImpact(
|
||||
reserveIn: number,
|
||||
reserveOut: number,
|
||||
amountIn: number
|
||||
): number {
|
||||
// Constant product formula: (x + Δx) * (y - Δy) = x * y
|
||||
// Solving for Δy: Δy = (y * Δx) / (x + Δx)
|
||||
const amountOut = (reserveOut * amountIn) / (reserveIn + amountIn);
|
||||
const priceBefore = reserveOut / reserveIn;
|
||||
const priceAfter = (reserveOut - amountOut) / (reserveIn + amountIn);
|
||||
const priceImpact = ((priceBefore - priceAfter) / priceBefore) * 100;
|
||||
|
||||
return priceImpact;
|
||||
}
|
||||
|
||||
analyzeSlippage(results: SimulationStepResult[]): SlippageAnalysis {
|
||||
const swapSteps = results.filter(r => r.stepType === 'swap');
|
||||
const totalSlippage = swapSteps.reduce((sum, r) => sum + (r.slippage || 0), 0);
|
||||
const avgSlippage = totalSlippage / swapSteps.length;
|
||||
|
||||
let riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
|
||||
if (avgSlippage < 0.5) {
|
||||
riskLevel = 'LOW';
|
||||
} else if (avgSlippage < 2.0) {
|
||||
riskLevel = 'MEDIUM';
|
||||
} else {
|
||||
riskLevel = 'HIGH';
|
||||
}
|
||||
|
||||
const warnings: string[] = [];
|
||||
if (avgSlippage > 1.0) {
|
||||
warnings.push(`High slippage expected: ${avgSlippage.toFixed(2)}%`);
|
||||
}
|
||||
|
||||
return {
|
||||
expectedSlippage: avgSlippage,
|
||||
riskLevel,
|
||||
liquidityDepth: 0, // Aggregate from steps
|
||||
priceImpact: avgSlippage,
|
||||
warnings
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Liquidity Checks
|
||||
|
||||
### Liquidity Check Logic
|
||||
|
||||
```typescript
|
||||
class LiquidityChecker {
|
||||
async check(
|
||||
tokenIn: string,
|
||||
tokenOut: string,
|
||||
amountIn: number
|
||||
): Promise<LiquidityCheck> {
|
||||
// Get pool liquidity
|
||||
const pool = await this.getPool(tokenIn, tokenOut);
|
||||
const availableLiquidity = pool.reserveOut;
|
||||
|
||||
// Calculate required output
|
||||
const price = await this.getPrice(tokenIn, tokenOut);
|
||||
const requiredOutput = amountIn * price;
|
||||
|
||||
// Check if sufficient
|
||||
const sufficient = availableLiquidity >= requiredOutput * 1.1; // 10% buffer
|
||||
|
||||
const warnings: string[] = [];
|
||||
if (!sufficient) {
|
||||
warnings.push(`Insufficient liquidity: need ${requiredOutput}, have ${availableLiquidity}`);
|
||||
} else if (availableLiquidity < requiredOutput * 1.5) {
|
||||
warnings.push(`Low liquidity: ${((availableLiquidity / requiredOutput) * 100).toFixed(1)}% buffer`);
|
||||
}
|
||||
|
||||
return {
|
||||
sufficient,
|
||||
poolDepth: availableLiquidity,
|
||||
requiredAmount: requiredOutput,
|
||||
availableAmount: availableLiquidity,
|
||||
warnings
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Failure Prediction
|
||||
|
||||
### Failure Prediction Logic
|
||||
|
||||
```typescript
|
||||
class FailurePredictor {
|
||||
async predictFailures(plan: Plan): Promise<string[]> {
|
||||
const failures: string[] = [];
|
||||
|
||||
// Check step dependencies
|
||||
for (let i = 0; i < plan.steps.length; i++) {
|
||||
const step = plan.steps[i];
|
||||
|
||||
// Check if previous step outputs are sufficient
|
||||
if (i > 0) {
|
||||
const prevStep = plan.steps[i - 1];
|
||||
const prevOutput = await this.getStepOutput(prevStep);
|
||||
|
||||
if (step.type === 'swap' && step.amount > prevOutput.amount) {
|
||||
failures.push(`Step ${i + 1}: Insufficient input from previous step`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check step-specific validations
|
||||
if (step.type === 'borrow') {
|
||||
const canBorrow = await this.checkBorrowCapacity(step.asset, step.amount);
|
||||
if (!canBorrow) {
|
||||
failures.push(`Step ${i + 1}: Cannot borrow ${step.amount} ${step.asset}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (step.type === 'pay') {
|
||||
const isValidIBAN = this.validateIBAN(step.beneficiary.IBAN);
|
||||
if (!isValidIBAN) {
|
||||
failures.push(`Step ${i + 1}: Invalid IBAN`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check recursion depth
|
||||
const borrowCount = plan.steps.filter(s => s.type === 'borrow').length;
|
||||
if (borrowCount - 1 > plan.maxRecursion) {
|
||||
failures.push(`Recursion depth ${borrowCount - 1} exceeds maximum ${plan.maxRecursion}`);
|
||||
}
|
||||
|
||||
// Check LTV
|
||||
const totalBorrowed = plan.steps
|
||||
.filter(s => s.type === 'borrow')
|
||||
.reduce((sum, s) => sum + (s as BorrowStep).amount, 0);
|
||||
const totalCollateral = await this.getTotalCollateral();
|
||||
const ltv = totalBorrowed / totalCollateral;
|
||||
|
||||
if (ltv > plan.maxLTV) {
|
||||
failures.push(`LTV ${ltv} exceeds maximum ${plan.maxLTV}`);
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Result Presentation Format
|
||||
|
||||
### UI Presentation
|
||||
|
||||
```typescript
|
||||
// Simulation Results Component
|
||||
const SimulationResults = ({ results }: { results: SimulationResponse }) => {
|
||||
return (
|
||||
<div className="simulation-results">
|
||||
<h2>Simulation Results</h2>
|
||||
|
||||
{/* Status */}
|
||||
<StatusBadge status={results.status} />
|
||||
|
||||
{/* Summary */}
|
||||
<div className="summary">
|
||||
<div>Gas Estimate: {results.summary.gasEstimate.toLocaleString()}</div>
|
||||
<div>Estimated Cost: ${results.summary.estimatedCost.toFixed(2)}</div>
|
||||
<div>Total Slippage: {results.summary.totalSlippage.toFixed(2)}%</div>
|
||||
<div>Execution Time: ~{results.summary.executionTime}s</div>
|
||||
</div>
|
||||
|
||||
{/* Step-by-Step Results */}
|
||||
<div className="steps">
|
||||
{results.steps.map((step, i) => (
|
||||
<StepResultCard key={i} step={step} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Warnings */}
|
||||
{results.warnings.length > 0 && (
|
||||
<WarningPanel warnings={results.warnings} />
|
||||
)}
|
||||
|
||||
{/* Errors */}
|
||||
{results.errors.length > 0 && (
|
||||
<ErrorPanel errors={results.errors} />
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="actions">
|
||||
<Button onClick={onRunAgain}>Run Simulation Again</Button>
|
||||
<Button onClick={onProceed} disabled={results.status === 'FAILURE'}>
|
||||
Proceed to Sign
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Optional Toggle Implementation
|
||||
|
||||
### Frontend Toggle
|
||||
|
||||
```typescript
|
||||
// Builder UI with optional simulation toggle
|
||||
const BuilderPage = () => {
|
||||
const [simulationEnabled, setSimulationEnabled] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Summary Panel */}
|
||||
<SummaryPanel>
|
||||
<Checkbox
|
||||
checked={simulationEnabled}
|
||||
onChange={(e) => setSimulationEnabled(e.target.checked)}
|
||||
label="Enable Simulation (Advanced)"
|
||||
/>
|
||||
|
||||
{simulationEnabled && (
|
||||
<Button onClick={handleSimulate}>Simulate</Button>
|
||||
)}
|
||||
</SummaryPanel>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Backend Handling
|
||||
|
||||
```typescript
|
||||
// Backend respects simulation toggle
|
||||
if (simulationEnabled && user.isAdvanced) {
|
||||
// Show simulation button
|
||||
// Allow simulation requests
|
||||
} else {
|
||||
// Hide simulation button
|
||||
// Simulation still available via API for advanced users
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Performance Requirements
|
||||
|
||||
### Response Time
|
||||
- **Simulation Time**: < 5 seconds for typical workflows
|
||||
- **Gas Estimation**: < 1 second per step
|
||||
- **Slippage Calculation**: < 500ms per swap
|
||||
- **Liquidity Check**: < 1 second per check
|
||||
|
||||
### Caching
|
||||
- Cache price oracle data for 30 seconds
|
||||
- Cache liquidity data for 10 seconds
|
||||
- Cache gas estimates for 60 seconds
|
||||
|
||||
---
|
||||
|
||||
## 11. Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```typescript
|
||||
describe('SimulationEngine', () => {
|
||||
it('should simulate swap step', async () => {
|
||||
const result = await engine.simulateStep(swapStep, 0);
|
||||
expect(result.status).toBe('SUCCESS');
|
||||
expect(result.slippage).toBeLessThan(1.0);
|
||||
});
|
||||
|
||||
it('should predict failures', async () => {
|
||||
const failures = await predictor.predictFailures(invalidPlan);
|
||||
expect(failures.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```typescript
|
||||
describe('Simulation API', () => {
|
||||
it('should return simulation results', async () => {
|
||||
const response = await api.simulatePlan(planId);
|
||||
expect(response.status).toBe('SUCCESS');
|
||||
expect(response.steps.length).toBe(plan.steps.length);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Last Updated**: 2025-01-15
|
||||
**Author**: Engineering Team
|
||||
|
||||
Reference in New Issue
Block a user