Initial commit
This commit is contained in:
23
mev-bot/package.json
Normal file
23
mev-bot/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "dbis-mev-bot",
|
||||
"version": "1.0.0",
|
||||
"description": "DBIS MEV Bot for protective arbitrage and atomic cycles",
|
||||
"main": "src/bot.ts",
|
||||
"scripts": {
|
||||
"start": "tsx src/bot.ts",
|
||||
"build": "tsc",
|
||||
"dev": "tsx watch src/bot.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethers": "^6.9.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"@flashbots/ethers-provider-bundle": "^1.0.1",
|
||||
"winston": "^3.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
161
mev-bot/src/bot.ts
Normal file
161
mev-bot/src/bot.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { ethers } from "ethers";
|
||||
import * as dotenv from "dotenv";
|
||||
import { AmortizationStrategy } from "./strategy/amortization";
|
||||
import { ArbitrageStrategy } from "./strategy/arbitrage";
|
||||
import { DeleverageStrategy } from "./strategy/deleverage";
|
||||
import { InvariantChecker } from "./utils/invariant-checker";
|
||||
import { PositionCalculator } from "./utils/position-calculator";
|
||||
import { BundleBuilder } from "./utils/bundle-builder";
|
||||
import { FlashRouterClient } from "./providers/flash-router";
|
||||
import { AaveClient } from "./providers/aave-client";
|
||||
import { UniswapClient } from "./providers/uniswap-client";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* DBIS MEV Bot
|
||||
* Monitors positions and executes profitable atomic cycles
|
||||
*/
|
||||
export class DBISMEVBot {
|
||||
private provider: ethers.Provider;
|
||||
private wallet: ethers.Wallet;
|
||||
private flashRouterClient: FlashRouterClient;
|
||||
private aaveClient: AaveClient;
|
||||
private uniswapClient: UniswapClient;
|
||||
private invariantChecker: InvariantChecker;
|
||||
private positionCalculator: PositionCalculator;
|
||||
private bundleBuilder: BundleBuilder;
|
||||
|
||||
private amortizationStrategy: AmortizationStrategy;
|
||||
private arbitrageStrategy: ArbitrageStrategy;
|
||||
private deleverageStrategy: DeleverageStrategy;
|
||||
|
||||
private running = false;
|
||||
private pollInterval = 5000; // 5 seconds
|
||||
|
||||
constructor(
|
||||
rpcUrl: string,
|
||||
privateKey: string,
|
||||
config: BotConfig
|
||||
) {
|
||||
this.provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
this.wallet = new ethers.Wallet(privateKey, this.provider);
|
||||
|
||||
// Initialize clients
|
||||
this.flashRouterClient = new FlashRouterClient(config.flashRouterAddress, this.wallet);
|
||||
this.aaveClient = new AaveClient(config.aavePoolAddress, this.provider);
|
||||
this.uniswapClient = new UniswapClient(config.uniswapRouterAddress, this.wallet);
|
||||
|
||||
// Initialize utilities
|
||||
this.invariantChecker = new InvariantChecker(config.vaultAddress, this.provider);
|
||||
this.positionCalculator = new PositionCalculator(config.vaultAddress, this.aaveClient, this.provider);
|
||||
this.bundleBuilder = new BundleBuilder(this.provider);
|
||||
|
||||
// Initialize strategies
|
||||
this.amortizationStrategy = new AmortizationStrategy(
|
||||
this.flashRouterClient,
|
||||
this.positionCalculator,
|
||||
this.invariantChecker
|
||||
);
|
||||
this.arbitrageStrategy = new ArbitrageStrategy(
|
||||
this.uniswapClient,
|
||||
this.positionCalculator
|
||||
);
|
||||
this.deleverageStrategy = new DeleverageStrategy(
|
||||
this.positionCalculator,
|
||||
this.invariantChecker
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the bot
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
this.running = true;
|
||||
console.log("🚀 DBIS MEV Bot started");
|
||||
|
||||
while (this.running) {
|
||||
try {
|
||||
await this.tick();
|
||||
} catch (error) {
|
||||
console.error("Error in bot tick:", error);
|
||||
}
|
||||
|
||||
await this.sleep(this.pollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the bot
|
||||
*/
|
||||
stop(): void {
|
||||
this.running = false;
|
||||
console.log("🛑 DBIS MEV Bot stopped");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main bot loop
|
||||
*/
|
||||
private async tick(): Promise<void> {
|
||||
// Check position health
|
||||
const position = await this.positionCalculator.getPosition();
|
||||
|
||||
// Health factor protection
|
||||
if (position.healthFactor < 1.1e18) {
|
||||
console.warn("⚠️ Low health factor detected:", position.healthFactor.toString());
|
||||
await this.deleverageStrategy.executeIfProfitable();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for profitable amortization cycles
|
||||
const amortizationOpportunity = await this.amortizationStrategy.detectOpportunity();
|
||||
if (amortizationOpportunity.profitable) {
|
||||
console.log("💰 Amortization opportunity detected");
|
||||
await this.amortizationStrategy.execute(amortizationOpportunity);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for arbitrage opportunities
|
||||
const arbitrageOpportunity = await this.arbitrageStrategy.detectOpportunity();
|
||||
if (arbitrageOpportunity.profitable) {
|
||||
console.log("💎 Arbitrage opportunity detected");
|
||||
await this.arbitrageStrategy.execute(arbitrageOpportunity);
|
||||
return;
|
||||
}
|
||||
|
||||
// No opportunities found
|
||||
console.log("⏳ No opportunities found, waiting...");
|
||||
}
|
||||
|
||||
private sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
interface BotConfig {
|
||||
flashRouterAddress: string;
|
||||
vaultAddress: string;
|
||||
aavePoolAddress: string;
|
||||
uniswapRouterAddress: string;
|
||||
}
|
||||
|
||||
// Main entry point
|
||||
if (require.main === module) {
|
||||
const rpcUrl = process.env.RPC_URL || "";
|
||||
const privateKey = process.env.PRIVATE_KEY || "";
|
||||
const config: BotConfig = {
|
||||
flashRouterAddress: process.env.FLASH_ROUTER_ADDRESS || "",
|
||||
vaultAddress: process.env.VAULT_ADDRESS || "",
|
||||
aavePoolAddress: process.env.AAVE_POOL_ADDRESS || "",
|
||||
uniswapRouterAddress: process.env.UNISWAP_ROUTER_ADDRESS || ""
|
||||
};
|
||||
|
||||
if (!rpcUrl || !privateKey) {
|
||||
console.error("Missing required environment variables");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const bot = new DBISMEVBot(rpcUrl, privateKey, config);
|
||||
bot.start().catch(console.error);
|
||||
}
|
||||
|
||||
28
mev-bot/src/providers/aave-client.ts
Normal file
28
mev-bot/src/providers/aave-client.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
* Aave Client
|
||||
* Interacts with Aave v3 Pool
|
||||
*/
|
||||
export class AaveClient {
|
||||
private poolInterface: ethers.Interface;
|
||||
|
||||
constructor(
|
||||
private poolAddress: string,
|
||||
private provider: ethers.Provider
|
||||
) {
|
||||
this.poolInterface = new ethers.Interface([
|
||||
"function getUserAccountData(address user) view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)"
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user account data
|
||||
*/
|
||||
async getUserAccountData(userAddress: string): Promise<any> {
|
||||
const data = this.poolInterface.encodeFunctionData("getUserAccountData", [userAddress]);
|
||||
const result = await this.provider.call({ to: this.poolAddress, data });
|
||||
return this.poolInterface.decodeFunctionResult("getUserAccountData", result);
|
||||
}
|
||||
}
|
||||
|
||||
30
mev-bot/src/providers/flash-router.ts
Normal file
30
mev-bot/src/providers/flash-router.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
* Flash Router Client
|
||||
* Interacts with FlashLoanRouter contract
|
||||
*/
|
||||
export class FlashRouterClient {
|
||||
private routerInterface: ethers.Interface;
|
||||
|
||||
constructor(
|
||||
private routerAddress: string,
|
||||
private wallet: ethers.Wallet
|
||||
) {
|
||||
this.routerInterface = new ethers.Interface([
|
||||
"function flashLoan((address asset, uint256 amount, uint8 provider) params, bytes callbackData)",
|
||||
"function getFlashLoanFee(address asset, uint256 amount, uint8 provider) view returns (uint256)"
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flash loan fee
|
||||
*/
|
||||
async getFlashLoanFee(asset: string, amount: bigint, provider: number): Promise<bigint> {
|
||||
const data = this.routerInterface.encodeFunctionData("getFlashLoanFee", [asset, amount, provider]);
|
||||
const result = await this.wallet.provider.call({ to: this.routerAddress, data });
|
||||
const decoded = this.routerInterface.decodeFunctionResult("getFlashLoanFee", result);
|
||||
return decoded[0] as bigint;
|
||||
}
|
||||
}
|
||||
|
||||
21
mev-bot/src/providers/uniswap-client.ts
Normal file
21
mev-bot/src/providers/uniswap-client.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
* Uniswap Client
|
||||
* Interacts with Uniswap V3 Router
|
||||
*/
|
||||
export class UniswapClient {
|
||||
constructor(
|
||||
private routerAddress: string,
|
||||
private wallet: ethers.Wallet
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get quote for swap
|
||||
*/
|
||||
async getQuote(tokenIn: string, tokenOut: string, amountIn: bigint): Promise<bigint> {
|
||||
// Would query Uniswap router for quote
|
||||
return 0n;
|
||||
}
|
||||
}
|
||||
|
||||
82
mev-bot/src/strategy/amortization.ts
Normal file
82
mev-bot/src/strategy/amortization.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { ethers } from "ethers";
|
||||
import { FlashRouterClient } from "../providers/flash-router";
|
||||
import { PositionCalculator } from "../utils/position-calculator";
|
||||
import { InvariantChecker } from "../utils/invariant-checker";
|
||||
|
||||
/**
|
||||
* Amortization Strategy
|
||||
* Detects and executes profitable amortization cycles
|
||||
*/
|
||||
export class AmortizationStrategy {
|
||||
constructor(
|
||||
private flashRouterClient: FlashRouterClient,
|
||||
private positionCalculator: PositionCalculator,
|
||||
private invariantChecker: InvariantChecker
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Detect amortization opportunity
|
||||
*/
|
||||
async detectOpportunity(): Promise<AmortizationOpportunity> {
|
||||
const position = await this.positionCalculator.getPosition();
|
||||
|
||||
// Calculate if amortization would be profitable
|
||||
const estimatedGasCost = ethers.parseEther("0.01"); // Simplified
|
||||
const estimatedImprovement = await this.estimateImprovement(position);
|
||||
|
||||
const profitable = estimatedImprovement.value > estimatedGasCost * 2n; // 2x gas as buffer
|
||||
|
||||
return {
|
||||
profitable,
|
||||
targetAsset: "0x0000000000000000000000000000000000000000", // Would determine optimal asset
|
||||
maxLoops: 5,
|
||||
estimatedImprovement
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute amortization cycle
|
||||
*/
|
||||
async execute(opportunity: AmortizationOpportunity): Promise<void> {
|
||||
// Verify invariants before execution
|
||||
const valid = await this.invariantChecker.verifyBeforeExecution();
|
||||
if (!valid) {
|
||||
throw new Error("Invariants would be violated");
|
||||
}
|
||||
|
||||
// Build transaction
|
||||
const kernelAddress = process.env.KERNEL_ADDRESS || "";
|
||||
const kernelInterface = new ethers.Interface([
|
||||
"function executeAmortizingCycle((address targetAsset, uint256 maxLoops, uint256 minHFImprovement) params) returns (bool success, uint256 cyclesExecuted)"
|
||||
]);
|
||||
|
||||
const params = {
|
||||
targetAsset: opportunity.targetAsset,
|
||||
maxLoops: opportunity.maxLoops,
|
||||
minHFImprovement: ethers.parseEther("0.01") // 1% minimum improvement
|
||||
};
|
||||
|
||||
const data = kernelInterface.encodeFunctionData("executeAmortizingCycle", [params]);
|
||||
|
||||
// Send transaction
|
||||
// Would use Flashbots bundle in production
|
||||
console.log("Executing amortization cycle...");
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate improvement from amortization
|
||||
*/
|
||||
private async estimateImprovement(position: any): Promise<{ value: bigint }> {
|
||||
// Simplified calculation
|
||||
// Would use simulation to estimate improvement
|
||||
return { value: ethers.parseEther("0.1") };
|
||||
}
|
||||
}
|
||||
|
||||
interface AmortizationOpportunity {
|
||||
profitable: boolean;
|
||||
targetAsset: string;
|
||||
maxLoops: number;
|
||||
estimatedImprovement: { value: bigint };
|
||||
}
|
||||
|
||||
43
mev-bot/src/strategy/arbitrage.ts
Normal file
43
mev-bot/src/strategy/arbitrage.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { ethers } from "ethers";
|
||||
import { UniswapClient } from "../providers/uniswap-client";
|
||||
import { PositionCalculator } from "../utils/position-calculator";
|
||||
|
||||
/**
|
||||
* Arbitrage Strategy
|
||||
* Detects and executes arbitrage opportunities
|
||||
*/
|
||||
export class ArbitrageStrategy {
|
||||
constructor(
|
||||
private uniswapClient: UniswapClient,
|
||||
private positionCalculator: PositionCalculator
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Detect arbitrage opportunity
|
||||
*/
|
||||
async detectOpportunity(): Promise<ArbitrageOpportunity> {
|
||||
// Simplified: would compare prices across DEXs
|
||||
return {
|
||||
profitable: false,
|
||||
path: [],
|
||||
amountIn: 0n,
|
||||
expectedProfit: 0n
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute arbitrage
|
||||
*/
|
||||
async execute(opportunity: ArbitrageOpportunity): Promise<void> {
|
||||
// Execute arbitrage trade
|
||||
console.log("Executing arbitrage...");
|
||||
}
|
||||
}
|
||||
|
||||
interface ArbitrageOpportunity {
|
||||
profitable: boolean;
|
||||
path: string[];
|
||||
amountIn: bigint;
|
||||
expectedProfit: bigint;
|
||||
}
|
||||
|
||||
26
mev-bot/src/strategy/deleverage.ts
Normal file
26
mev-bot/src/strategy/deleverage.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { PositionCalculator } from "../utils/position-calculator";
|
||||
import { InvariantChecker } from "../utils/invariant-checker";
|
||||
|
||||
/**
|
||||
* Deleverage Strategy
|
||||
* Protects position by reducing leverage when HF is low
|
||||
*/
|
||||
export class DeleverageStrategy {
|
||||
constructor(
|
||||
private positionCalculator: PositionCalculator,
|
||||
private invariantChecker: InvariantChecker
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Execute deleverage if profitable/necessary
|
||||
*/
|
||||
async executeIfProfitable(): Promise<void> {
|
||||
const position = await this.positionCalculator.getPosition();
|
||||
|
||||
if (position.healthFactor < 1.1e18) {
|
||||
console.log("Executing deleverage...");
|
||||
// Would execute deleverage transaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
mev-bot/src/utils/bundle-builder.ts
Normal file
26
mev-bot/src/utils/bundle-builder.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
* Bundle Builder
|
||||
* Builds Flashbots bundles for private transaction submission
|
||||
*/
|
||||
export class BundleBuilder {
|
||||
constructor(private provider: ethers.Provider) {}
|
||||
|
||||
/**
|
||||
* Build a Flashbots bundle
|
||||
*/
|
||||
async buildBundle(transactions: ethers.TransactionRequest[]): Promise<Bundle> {
|
||||
// Would build Flashbots bundle
|
||||
return {
|
||||
transactions,
|
||||
blockNumber: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface Bundle {
|
||||
transactions: ethers.TransactionRequest[];
|
||||
blockNumber: number;
|
||||
}
|
||||
|
||||
75
mev-bot/src/utils/invariant-checker.ts
Normal file
75
mev-bot/src/utils/invariant-checker.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
* Invariant Checker
|
||||
* Verifies position invariants before execution
|
||||
*/
|
||||
export class InvariantChecker {
|
||||
private vaultInterface: ethers.Interface;
|
||||
|
||||
constructor(
|
||||
private vaultAddress: string,
|
||||
private provider: ethers.Provider
|
||||
) {
|
||||
this.vaultInterface = new ethers.Interface([
|
||||
"function verifyPositionImproved(uint256 collateralBefore, uint256 debtBefore, uint256 healthFactorBefore) view returns (bool success)"
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify invariants before execution
|
||||
*/
|
||||
async verifyBeforeExecution(): Promise<boolean> {
|
||||
// Take snapshot
|
||||
const snapshot = await this.takeSnapshot();
|
||||
|
||||
// Verify position would improve
|
||||
try {
|
||||
const data = this.vaultInterface.encodeFunctionData("verifyPositionImproved", [
|
||||
snapshot.collateral,
|
||||
snapshot.debt,
|
||||
snapshot.healthFactor
|
||||
]);
|
||||
|
||||
const result = await this.provider.call({
|
||||
to: this.vaultAddress,
|
||||
data
|
||||
});
|
||||
|
||||
const decoded = this.vaultInterface.decodeFunctionResult("verifyPositionImproved", result);
|
||||
return decoded[0] as boolean;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take position snapshot
|
||||
*/
|
||||
private async takeSnapshot(): Promise<PositionSnapshot> {
|
||||
const vaultInterface = new ethers.Interface([
|
||||
"function snapshotPosition() returns (uint256 collateralBefore, uint256 debtBefore, uint256 healthFactorBefore)"
|
||||
]);
|
||||
|
||||
const data = vaultInterface.encodeFunctionData("snapshotPosition");
|
||||
const result = await this.provider.call({
|
||||
to: this.vaultAddress,
|
||||
data
|
||||
});
|
||||
|
||||
const decoded = vaultInterface.decodeFunctionResult("snapshotPosition", result);
|
||||
|
||||
return {
|
||||
collateral: decoded[0] as bigint,
|
||||
debt: decoded[1] as bigint,
|
||||
healthFactor: decoded[2] as bigint
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface PositionSnapshot {
|
||||
collateral: bigint;
|
||||
debt: bigint;
|
||||
healthFactor: bigint;
|
||||
}
|
||||
|
||||
78
mev-bot/src/utils/position-calculator.ts
Normal file
78
mev-bot/src/utils/position-calculator.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { ethers } from "ethers";
|
||||
import { AaveClient } from "../providers/aave-client";
|
||||
|
||||
/**
|
||||
* Position Calculator
|
||||
* Calculates current position metrics
|
||||
*/
|
||||
export class PositionCalculator {
|
||||
private vaultInterface: ethers.Interface;
|
||||
|
||||
constructor(
|
||||
private vaultAddress: string,
|
||||
private aaveClient: AaveClient,
|
||||
private provider: ethers.Provider
|
||||
) {
|
||||
this.vaultInterface = new ethers.Interface([
|
||||
"function getTotalCollateralValue() view returns (uint256)",
|
||||
"function getTotalDebtValue() view returns (uint256)",
|
||||
"function getHealthFactor() view returns (uint256)",
|
||||
"function getLTV() view returns (uint256)"
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position
|
||||
*/
|
||||
async getPosition(): Promise<Position> {
|
||||
const [collateral, debt, healthFactor, ltv] = await Promise.all([
|
||||
this.getTotalCollateralValue(),
|
||||
this.getTotalDebtValue(),
|
||||
this.getHealthFactor(),
|
||||
this.getLTV()
|
||||
]);
|
||||
|
||||
return {
|
||||
collateral,
|
||||
debt,
|
||||
healthFactor,
|
||||
ltv
|
||||
};
|
||||
}
|
||||
|
||||
private async getTotalCollateralValue(): Promise<bigint> {
|
||||
const data = this.vaultInterface.encodeFunctionData("getTotalCollateralValue");
|
||||
const result = await this.provider.call({ to: this.vaultAddress, data });
|
||||
const decoded = this.vaultInterface.decodeFunctionResult("getTotalCollateralValue", result);
|
||||
return decoded[0] as bigint;
|
||||
}
|
||||
|
||||
private async getTotalDebtValue(): Promise<bigint> {
|
||||
const data = this.vaultInterface.encodeFunctionData("getTotalDebtValue");
|
||||
const result = await this.provider.call({ to: this.vaultAddress, data });
|
||||
const decoded = this.vaultInterface.decodeFunctionResult("getTotalDebtValue", result);
|
||||
return decoded[0] as bigint;
|
||||
}
|
||||
|
||||
private async getHealthFactor(): Promise<bigint> {
|
||||
const data = this.vaultInterface.encodeFunctionData("getHealthFactor");
|
||||
const result = await this.provider.call({ to: this.vaultAddress, data });
|
||||
const decoded = this.vaultInterface.decodeFunctionResult("getHealthFactor", result);
|
||||
return decoded[0] as bigint;
|
||||
}
|
||||
|
||||
private async getLTV(): Promise<bigint> {
|
||||
const data = this.vaultInterface.encodeFunctionData("getLTV");
|
||||
const result = await this.provider.call({ to: this.vaultAddress, data });
|
||||
const decoded = this.vaultInterface.decodeFunctionResult("getLTV", result);
|
||||
return decoded[0] as bigint;
|
||||
}
|
||||
}
|
||||
|
||||
interface Position {
|
||||
collateral: bigint;
|
||||
debt: bigint;
|
||||
healthFactor: bigint;
|
||||
ltv: bigint;
|
||||
}
|
||||
|
||||
9
mev-bot/tsconfig.json
Normal file
9
mev-bot/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user