Skip to content

Commit

Permalink
Merge branch 'develop' into evm-check
Browse files Browse the repository at this point in the history
  • Loading branch information
wtfsayo authored Jan 28, 2025
2 parents 71a8a45 + fe8f5f7 commit 2833663
Show file tree
Hide file tree
Showing 68 changed files with 1,371 additions and 975 deletions.
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Shaw Walters, aka Moon aka @lalalune
Copyright (c) 2025 Shaw Walters, aka Moon aka @lalalune

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
"cookie": "0.7.0",
"bs58": "5.0.0",
"@coral-xyz/anchor": "0.28.0"
},
"patchedDependencies": {
"@solana-developers/helpers": "patches/@solana-developers__helpers.patch"
}
},
"engines": {
Expand All @@ -71,15 +74,15 @@
"@injectivelabs/sdk-ts": "^1.14.33",
"@vitest/eslint-plugin": "1.0.1",
"amqplib": "0.10.5",
"bs58": "4.0.0",
"csv-parse": "5.6.0",
"langdetect": "^0.2.1",
"ollama-ai-provider": "0.16.1",
"optional": "0.1.4",
"pnpm": "9.14.4",
"sharp": "0.33.5",
"bs58": "4.0.0"
"pnpm": "9.15.0",
"sharp": "0.33.5"
},
"packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee",
"packageManager": "pnpm@9.15.0",
"workspaces": [
"packages/*"
]
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-arbitrage/src/actions/arbitrageAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ export const executeArbitrageAction: Action = {
similes: ["TRADE_ARBITRAGE", "RUN_ARBITRAGE"],
description: "Execute arbitrage trades across markets",

validate: async (runtime: IAgentRuntime, message: Memory) => {
validate: async (runtime: IAgentRuntime, _message: Memory) => {
// Validate settings are present
return runtime.getSetting("arbitrage.walletPrivateKey") !== undefined;
},

handler: async (runtime: IAgentRuntime, message: Memory) => {
handler: async (runtime: IAgentRuntime, _message: Memory) => {
const service = runtime.getService(ServiceType.ARBITRAGE) as ArbitrageService;
const markets = await service.evaluateMarkets();

Expand Down
226 changes: 120 additions & 106 deletions packages/plugin-arbitrage/src/core/Arbitrage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { Contract } from "@ethersproject/contracts";
import { Wallet } from "@ethersproject/wallet";
import { Provider } from "@ethersproject/providers";
import { FlashbotsBundleProvider } from "@flashbots/ethers-provider-bundle";
import { WETH_ADDRESS } from "././addresses";
// import { WETH_ADDRESS } from "././addresses";
import { EthMarket } from "././EthMarket";
import { ETHER, bigNumberToDecimal } from "./utils";
// import { ETHER, bigNumberToDecimal } from "./utils";
import { EthMarket as EthMarketType, CrossedMarketDetails, MarketsByToken, MarketType, BuyCalls } from "././types";
import { TransactionResponse } from "@ethersproject/providers";
import { DEFAULT_THRESHOLDS, MarketThresholds } from '../config/thresholds';
import { MarketsByToken as ImportedMarketsByToken, CrossedMarketDetails as ImportedCrossedMarketDetails } from "../type";
// import { TransactionResponse } from "@ethersproject/providers";
// import { DEFAULT_THRESHOLDS, MarketThresholds } from '../config/thresholds';
// import { MarketsByToken as ImportedMarketsByToken, CrossedMarketDetails as ImportedCrossedMarketDetails } from "../type";
export { MarketsByToken, EthMarket, CrossedMarketDetails } from '././types';

export interface BundleEntry {
Expand All @@ -24,12 +24,17 @@ export interface BundleEntry {
signer: string,
}

let CFMM = {
interface ArbitrageTransaction {
hash: string;
wait: () => Promise<{ blockNumber: number }>;
}

const CFMM = {
reserves: {
x: BigNumber.from(0),
y: BigNumber.from(0),
},
getOutputAmount: function (inputAmount: BigNumber, inputReserve: BigNumber, outputReserve: BigNumber) {
getOutputAmount: (inputAmount: BigNumber, inputReserve: BigNumber, outputReserve: BigNumber) => {
const inputAmountWithFee = inputAmount.mul(997);
const numerator = inputAmountWithFee.mul(outputReserve);
const denominator = inputReserve.mul(1000).add(inputAmountWithFee);
Expand All @@ -38,17 +43,17 @@ let CFMM = {
tradingFee: BigNumber.from("3000"),
};

let acceptTrade = (R: BigNumber, deltaPlus: number, deltaMinus: number) => {
let tradingFunctionResult = CFMM.getOutputAmount(R.sub(CFMM.tradingFee.mul(deltaMinus)).sub(deltaPlus), CFMM.reserves.x, CFMM.reserves.y);
let tradingFunctionResult2 = CFMM.getOutputAmount(R, CFMM.reserves.x, CFMM.reserves.y);
const acceptTrade = (R: BigNumber, deltaPlus: number, deltaMinus: number) => {
const tradingFunctionResult = CFMM.getOutputAmount(R.sub(CFMM.tradingFee.mul(deltaMinus)).sub(deltaPlus), CFMM.reserves.x, CFMM.reserves.y);
const tradingFunctionResult2 = CFMM.getOutputAmount(R, CFMM.reserves.x, CFMM.reserves.y);
console.log(`acceptTrade: tradingFunctionResult = ${tradingFunctionResult}, tradingFunctionResult2 = ${tradingFunctionResult2}`);
return tradingFunctionResult.gte(tradingFunctionResult2) && R.sub(CFMM.tradingFee.mul(deltaMinus)).sub(deltaPlus).gte(0);
};


export const dualDecomposition = (referencePrices: string | any[], objectiveFunction: (arg0: any) => any, penaltyVector: number[]) => {
console.log("Entering dualDecomposition");
let T = [];
const T = [];
for (let i = 0; i < referencePrices.length; i++) {
let deltaPlus = referencePrices[i].cumulativePrice;
let deltaMinus = Math.min(referencePrices[i].cumulativePrice, 0);
Expand Down Expand Up @@ -130,60 +135,60 @@ export async function calculateOptimalVolume(

return optimalVolume;
}
// Define the bisection search
// Define the bisection search
let bisectionSearch = (referencePrices: Array<{cumulativePrice: number, marketCount: number}>, objectiveFunction: (arg0: number) => number, penaltyVector: number[]) => {
console.log("Entering bisectionSearch");
let left = 0;
let right = referencePrices.length - 1;
let tolerance = 1e-6;
let psi;

while (right - left > tolerance) {
let mid = Math.floor((left + right) / 2);
let midValue = objectiveFunction(mid);
let penaltyResult = penaltyVector[mid] * mid;

if (midValue > penaltyResult) {
left = mid;
psi = mid;
} else {
right = mid;
}
}
console.log(`bisectionSearch: final psi = ${psi}`);

return psi;
};

let swapMarketArbitrage = (referencePrices: Array<{cumulativePrice: number, marketCount: number}> = [], objectiveFunction: (price: number) => number, penaltyVector: number[]) => {
// Initialize the dual variable ν
console.log("Entering swapMarketArbitrage");

let nu = 0;

// Use bisection or ternary search to solve for the vector Ψ
// Assuming that bisectionSearch accepts a number, not an array
let psi = bisectionSearch(referencePrices, objectiveFunction, penaltyVector);

// Iterate through the ∆i with i = 1, . . . , m
for (let i = 0; i < referencePrices.length; i++) {
// Compute the objective function U(Ψ)
// Ensure psi is used correctly as an index
if (psi !== undefined && psi >= 0 && psi < referencePrices.length) {
const objectiveFunctionResult = objectiveFunction(referencePrices[psi].cumulativePrice);

// Compute the linear penalty in the objective
let penaltyResult = penaltyVector[i] * nu;

// Update the dual variable ν
nu = Math.max(nu, (objectiveFunctionResult - penaltyResult));
}
}
// Return the dual variable ν
console.log(`swapMarketArbitrage: final nu = ${nu}`);
return nu;
};
// // Define the bisection search
// // Define the bisection search
// let bisectionSearch = (referencePrices: Array<{cumulativePrice: number, marketCount: number}>, objectiveFunction: (arg0: number) => number, penaltyVector: number[]) => {
// console.log("Entering bisectionSearch");
// let left = 0;
// let right = referencePrices.length - 1;
// let tolerance = 1e-6;
// let psi;

// while (right - left > tolerance) {
// let mid = Math.floor((left + right) / 2);
// let midValue = objectiveFunction(mid);
// let penaltyResult = penaltyVector[mid] * mid;

// if (midValue > penaltyResult) {
// left = mid;
// psi = mid;
// } else {
// right = mid;
// }
// }
// console.log(`bisectionSearch: final psi = ${psi}`);

// return psi;
// };

// let swapMarketArbitrage = (referencePrices: Array<{cumulativePrice: number, marketCount: number}> = [], objectiveFunction: (price: number) => number, penaltyVector: number[]) => {
// // Initialize the dual variable ν
// console.log("Entering swapMarketArbitrage");

// let nu = 0;

// // Use bisection or ternary search to solve for the vector Ψ
// // Assuming that bisectionSearch accepts a number, not an array
// let psi = bisectionSearch(referencePrices, objectiveFunction, penaltyVector);

// // Iterate through the ∆i with i = 1, . . . , m
// for (let i = 0; i < referencePrices.length; i++) {
// // Compute the objective function U(Ψ)
// // Ensure psi is used correctly as an index
// if (psi !== undefined && psi >= 0 && psi < referencePrices.length) {
// const objectiveFunctionResult = objectiveFunction(referencePrices[psi].cumulativePrice);

// // Compute the linear penalty in the objective
// let penaltyResult = penaltyVector[i] * nu;

// // Update the dual variable ν
// nu = Math.max(nu, (objectiveFunctionResult - penaltyResult));
// }
// }
// // Return the dual variable ν
// console.log(`swapMarketArbitrage: final nu = ${nu}`);
// return nu;
// };

export async function getGasPriceInfo(provider: Provider): Promise<{ currentGasPrice: BigNumber, avgGasPrice: BigNumber }> {
console.log("Entering getGasPriceInfo");
Expand All @@ -205,7 +210,7 @@ export async function getGasPriceInfo(provider: Provider): Promise<{ currentGasP
let transactionCountInBlock = 0;
for (const txHash of transactions) {
const tx = await provider.getTransaction(txHash);
if (tx && tx.gasPrice) {
if (tx?.gasPrice) {
totalGasPriceInBlock = totalGasPriceInBlock.add(tx.gasPrice);
transactionCountInBlock++;
}
Expand All @@ -231,31 +236,40 @@ export async function ensureHigherEffectiveGasPrice(transactionGasPrice: BigNumb
console.log(`ensureHigherEffectiveGasPrice: transactionGasPrice = ${transactionGasPrice}, tailTransactionGasPrice = ${tailTransactionGasPrice}, effectiveGasPrice = ${effectiveGasPrice}`);
return effectiveGasPrice;
}
async function checkBundleGas(bundleGas: BigNumber): Promise<boolean> {
const isValid = bundleGas.gte(42000);
console.log(`checkBundleGas: bundleGas = ${bundleGas}, isValid = ${isValid}`);
return isValid;

// async function checkBundleGas(bundleGas: BigNumber): Promise<boolean> {
// const isValid = bundleGas.gte(42000);
// console.log(`checkBundleGas: bundleGas = ${bundleGas}, isValid = ${isValid}`);
// return isValid;
// }
interface Block {
bundleGasPrice: BigNumber;
}

interface BlocksApi {
getRecentBlocks: () => Promise<Block[]>;
}
export async function monitorCompetingBundlesGasPrices(blocksApi: { getRecentBlocks: () => any; }): Promise<Array<BigNumber>> {

export async function monitorCompetingBundlesGasPrices(blocksApi: BlocksApi): Promise<Array<BigNumber>> {
console.log("Entering monitorCompetingBundlesGasPrices");
const recentBlocks = await blocksApi.getRecentBlocks();
const competingBundlesGasPrices = recentBlocks.map((block: { bundleGasPrice: any; }) => block.bundleGasPrice);
const competingBundlesGasPrices = recentBlocks.map((block: Block) => block.bundleGasPrice);
console.log(`monitorCompetingBundlesGasPrices: competingBundlesGasPrices = ${competingBundlesGasPrices}`);
return competingBundlesGasPrices;
}
export class Arbitrage {
constructor(
private wallet: any,
private flashbotsProvider: any,
private bundleExecutorContract: any
) {}
constructor(
private wallet: Wallet,
private flashbotsProvider: FlashbotsBundleProvider,
private bundleExecutorContract: Contract
) {}

async evaluateMarkets(marketsByToken: MarketsByToken): Promise<CrossedMarketDetails[]> {
async evaluateMarkets(_marketsByToken: MarketsByToken): Promise<CrossedMarketDetails[]> {
// Implement market evaluation logic
return [];
}

async takeCrossedMarkets(markets: CrossedMarketDetails[], currentBlock: number, maxAttempts: number): Promise<void> {
async takeCrossedMarkets(_markets: CrossedMarketDetails[], _currentBlock: number, _maxAttempts: number): Promise<void> {
// Implement arbitrage execution logic
}

Expand All @@ -279,9 +293,9 @@ export class Arbitrage {
sourceRouter: string,
targetRouter: string,
tokenIn: string,
tokenOut: string,
_tokenOut: string,
amountIn: BigNumber
): Promise<any> {
): Promise<ArbitrageTransaction> {
// Create the arbitrage transaction
const markets: CrossedMarketDetails[] = [{
marketPairs: [{
Expand All @@ -301,12 +315,12 @@ export class Arbitrage {
// Return a mock transaction response for now
// In a real implementation, this should return the actual transaction
return {
hash: "0x" + Math.random().toString(16).slice(2),
hash: `0x${Math.random().toString(16).slice(2)}`,
wait: async () => ({ blockNumber: await this.getCurrentBlock() })
};
}

private async getMarketForDex(dexAddress: string): Promise<EthMarket | null> {
private async getMarketForDex(_dexAddress: string): Promise<EthMarket | null> {
// This should be implemented to return the appropriate market instance
// based on the DEX address
return null;
Expand Down Expand Up @@ -341,26 +355,26 @@ export class Arbitrage {
}
}

async function fetchWETHBalance(address: string, provider: Provider, retries = 5, delayMs = 500): Promise<BigNumber | null> {
const WETH_CONTRACT_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const ABI = [
"function balanceOf(address owner) view returns (uint256)"
];
const contract = new Contract(WETH_CONTRACT_ADDRESS, ABI, provider);

for (let attempt = 1; attempt <= retries; attempt++) {
try {
const balance: BigNumber = await contract.balanceOf(address);
return balance;
} catch (error: any) {
console.error(`Attempt ${attempt} - Failed to fetch WETH balance for address ${address}:`, error.message);
if (attempt < retries) {
await new Promise(res => setTimeout(res, delayMs * attempt));
} else {
console.error(`All ${retries} attempts failed for address ${address}`);
return null;
}
}
}
return null;
}
// async function fetchWETHBalance(address: string, provider: Provider, retries = 5, delayMs = 500): Promise<BigNumber | null> {
// const WETH_CONTRACT_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
// const ABI = [
// "function balanceOf(address owner) view returns (uint256)"
// ];
// const contract = new Contract(WETH_CONTRACT_ADDRESS, ABI, provider);

// for (let attempt = 1; attempt <= retries; attempt++) {
// try {
// const balance: BigNumber = await contract.balanceOf(address);
// return balance;
// } catch (error: any) {
// console.error(`Attempt ${attempt} - Failed to fetch WETH balance for address ${address}:`, error.message);
// if (attempt < retries) {
// await new Promise(res => setTimeout(res, delayMs * attempt));
// } else {
// console.error(`All ${retries} attempts failed for address ${address}`);
// return null;
// }
// }
// }
// return null;
// }
Loading

0 comments on commit 2833663

Please sign in to comment.