Skip to content

Commit

Permalink
feat: Add arbitrage plugin with example character (#2784)
Browse files Browse the repository at this point in the history
* feat: Add arbitrage plugin with example character

* update intialization

* Update .env.example

---------

Co-authored-by: 0xmarf <mitch@integral.link>
Co-authored-by: Sayo <hi@sayo.wtf>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent a3133ed commit 47d4371
Show file tree
Hide file tree
Showing 25 changed files with 3,193 additions and 1,041 deletions.
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -931,3 +931,15 @@ BTC_FUNDRAISING_CAP=100 # Maximum amount for fundraising
# Trikon Plugin Configuration
TRIKON_WALLET_ADDRESS= # Your Trikon wallet address (must be a valid 64-character hex string starting with '0x')
TRIKON_INITIAL_BALANCE= # (Optional) The initial balance for the wallet. Defaults to "0" if not provided.

####################################
#### Arbitrage Plugin Configuration ####
####################################

ARBITRAGE_ETHEREUM_WS_URL= # WebSocket URL for Ethereum node connection
ARBITRAGE_EVM_PROVIDER_URL= # RPC URL for Ethereum node connection (if WS not available)
ARBITRAGE_EVM_PRIVATE_KEY= # Private key for the wallet executing arbitrage transactions
FLASHBOTS_RELAY_SIGNING_KEY= # Signing key for Flashbots relay interactions
BUNDLE_EXECUTOR_ADDRESS= # Address of the bundle executor contract


1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"@elizaos/client-xmtp": "workspace:*",
"@elizaos/plugin-trikon": "workspace:*",
"@elizaos/plugin-zilliqa": "workspace:*",
"@elizaos/plugin-arbitrage": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
12 changes: 9 additions & 3 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ import { MongoClient } from "mongodb";
import { quickIntelPlugin } from "@elizaos/plugin-quick-intel"

import { trikonPlugin } from "@elizaos/plugin-trikon"
import arbitragePlugin from "@elizaos/plugin-arbitrage"
const __filename = fileURLToPath(import.meta.url) // get the resolved path to the file
const __dirname = path.dirname(__filename) // get the name of the directory

Expand Down Expand Up @@ -632,9 +633,9 @@ export async function initializeClients(character: Character, runtime: IAgentRun
}

if (clientTypes.includes(Clients.XMTP)) {
const xmtpClient = await XmtpClientInterface.start(runtime);
if (xmtpClient) clients.xmtp = xmtpClient;
}
const xmtpClient = await XmtpClientInterface.start(runtime);
if (xmtpClient) clients.xmtp = xmtpClient;
}

if (clientTypes.includes(Clients.DISCORD)) {
const discordClient = await DiscordClientInterface.start(runtime)
Expand Down Expand Up @@ -929,6 +930,11 @@ export async function createAgent(character: Character, db: IDatabaseAdapter, ca
getSecret(character, "QUICKINTEL_API_KEY") ? quickIntelPlugin : null,
getSecret(character, "GELATO_RELAY_API_KEY") ? gelatoPlugin : null,
getSecret(character, "TRIKON_WALLET_ADDRESS") ? trikonPlugin : null,
getSecret(character, "ARBITRAGE_EVM_PRIVATE_KEY") &&
(getSecret(character, "ARBITRAGE_EVM_PROVIDER_URL")
|| getSecret(character, "ARBITRAGE_ETHEREUM_WS_URL"))
&& getSecret(character, "ARBITRAGE_FLASHBOTS_RELAY_SIGNING_KEY")
&& getSecret(character, "ARBITRAGE_BUNDLE_EXECUTOR_ADDRESS") ? arbitragePlugin : null,
].flat().filter(Boolean),
providers: [],
managers: [],
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"@ai-sdk/provider": "1.0.6",
"@ai-sdk/provider-utils": "2.1.2",
"cookie": "0.7.0",
"bs58": "5.0.0"
"bs58": "5.0.0",
"@coral-xyz/anchor": "0.28.0"
}
},
"engines": {
Expand Down
115 changes: 115 additions & 0 deletions packages/plugin-arbitrage/examples/trader.character.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"name": "Trader",
"description": "A trading bot that specializes in crypto arbitrage",
"clients": ["direct"],
"modelProvider": "anthropic",
"settings": {
"secrets": {
"EVM_PRIVATE_KEY": "YOUR_PRIVATE_KEY_HERE",
"FLASHBOTS_RELAY_SIGNING_KEY": "YOUR_FLASHBOTS_KEY_HERE",
"BUNDLE_EXECUTOR_ADDRESS": "YOUR_EXECUTOR_ADDRESS_HERE"
},
"arbitrage": {
"ethereumWsUrl": "YOUR_ETH_WSS_URL",
"rpcUrl": "YOUR_ETH_RPC_URL"
}
},
"plugins": [
"@elizaos/plugin-arbitrage",
"@elizaos/plugin-evm"
],
"modelSettings": {
"provider": "anthropic",
"model": "claude-3-sonnet-20240229"
},
"bio": [
"Expert in cryptocurrency trading and arbitrage.",
"Specializes in identifying profitable trading opportunities.",
"Monitors multiple exchanges for price differences.",
"Provides real-time market analysis and insights."
],
"lore": [
"Created to help traders identify and execute profitable arbitrage opportunities.",
"Trained on extensive market data and trading patterns."
],
"knowledge": [
"Understands cryptocurrency market dynamics",
"Knows how to identify arbitrage opportunities",
"Can analyze trading pairs across different exchanges",
"Monitors market conditions in real-time"
],
"messageExamples": [
[
{
"user": "{{user1}}",
"content": { "text": "analyze BTC/ETH pair for arbitrage opportunities" }
},
{
"user": "Trader",
"content": {
"text": "I'll analyze the BTC/ETH trading pair for potential arbitrage opportunities across different exchanges."
}
}
],
[
{
"user": "{{user1}}",
"content": { "text": "check current market conditions" }
},
{
"user": "Trader",
"content": {
"text": "I'll check the current market conditions and look for profitable trading opportunities."
}
}
]
],
"postExamples": [
"Market Analysis: Current arbitrage opportunities in the BTC/ETH market",
"Trading Update: Identified profitable arbitrage paths between exchanges",
"Market Alert: Significant price divergence detected between exchanges",
"Strategy Overview: Best practices for arbitrage trading"
],
"topics": [
"cryptocurrency trading",
"arbitrage opportunities",
"market analysis",
"trading strategies",
"price analysis",
"exchange monitoring",
"risk management",
"trading automation"
],
"adjectives": [
"analytical",
"precise",
"professional",
"strategic",
"vigilant",
"data-driven",
"methodical",
"efficient"
],
"style": {
"all": [
"Keep responses clear and data-driven",
"Focus on market opportunities",
"Provide actionable insights",
"Be professional and precise",
"Use clear market terminology",
"Always consider risk management"
],
"chat": [
"Provide detailed market analysis",
"Be direct and informative",
"Focus on actionable opportunities",
"Maintain professional tone"
],
"post": [
"Share clear market insights",
"Highlight significant opportunities",
"Include relevant market data",
"Maintain analytical perspective"
]
}
}
49 changes: 49 additions & 0 deletions packages/plugin-arbitrage/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@elizaos/plugin-arbitrage",
"version": "0.1.0",
"description": "Arbitrage trading plugin for Eliza",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"default": "./dist/index.js"
}
},
"scripts": {
"clean": "rm -rf dist",
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch"
},
"dependencies": {
"@elizaos/adapter-sqlite": "^0.1.8",
"@elizaos/core": "workspace:*",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/address": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ethersproject/units": "^5.7.0",
"@ethersproject/wallet": "^5.7.0",
"@flashbots/ethers-provider-bundle": "0.6.2",
"dotenv": "^16.4.7",
"ethers": "5.7.2",
"lodash": "^4.17.21",
"ws": "^8.18.0"
},
"devDependencies": {
"@types/lodash": "^4.17.14",
"@types/node": "^22.10.9",
"@types/ws": "^8.5.13",
"rimraf": "^5.0.5",
"typescript": "^5.7.3",
"@types/dotenv": "^8.2.0",
"tsup": "^8.0.2"
},
"peerDependencies": {
"@elizaos/core": "workspace:*"
}
}
40 changes: 40 additions & 0 deletions packages/plugin-arbitrage/src/actions/arbitrageAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Action, IAgentRuntime, Memory, ServiceType } from "@elizaos/core";
import { ArbitrageService } from "../services/ArbitrageService";

export const executeArbitrageAction: Action = {
name: "EXECUTE_ARBITRAGE",
similes: ["TRADE_ARBITRAGE", "RUN_ARBITRAGE"],
description: "Execute arbitrage trades across markets",

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

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

if (markets.length > 0) {
await service.executeArbitrage(markets);
}

return true;
},

examples: [
[
{
user: "{{user1}}",
content: { text: "Find arbitrage opportunities" }
},
{
user: "{{user2}}",
content: {
text: "Scanning for arbitrage trades",
action: "EXECUTE_ARBITRAGE"
}
}
]
]
};
18 changes: 18 additions & 0 deletions packages/plugin-arbitrage/src/config/addresses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const UNISWAP_LOOKUP_CONTRACT_ADDRESS = '0x5EF1009b9FCD4fec3094a5564047e190D72Bd511'
//mainnet ^^ goerli vv
//export const UNISWAP_LOOKUP_CONTRACT_ADDRESS = '0xF52FE911458C6a3279832b764cDF0189e49f073A'
export const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
export const SUSHISWAP_FACTORY_ADDRESS = '0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac';
export const UNISWAP_FACTORY_ADDRESS = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f';

//export const CRO_FACTORY_ADDRESS = "0x9DEB29c9a4c7A88a3C0257393b7f3335338D9A9D";
//export const ZEUS_FACTORY_ADDRESS = "0xbdda21dd8da31d5bee0c9bb886c044ebb9b8906a";
//export const LUA_FACTORY_ADDRESS = "0x0388c1e0f210abae597b7de712b9510c6c36c857";

export const FACTORY_ADDRESSES = [
//CRO_FACTORY_ADDRESS,
//ZEUS_FACTORY_ADDRESS,
//LUA_FACTORY_ADDRESS,
SUSHISWAP_FACTORY_ADDRESS,
UNISWAP_FACTORY_ADDRESS,
]
15 changes: 15 additions & 0 deletions packages/plugin-arbitrage/src/config/thresholds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BigNumber } from "ethers";

export interface MarketThresholds {
minProfitThreshold: BigNumber;
maxTradeSize: BigNumber;
gasLimit: number;
minerRewardPercentage: number;
}

export const DEFAULT_THRESHOLDS: MarketThresholds = {
minProfitThreshold: BigNumber.from("100000000000000"), // 0.0001 ETH
maxTradeSize: BigNumber.from("1000000000000000000"), // 1 ETH
gasLimit: 500000,
minerRewardPercentage: 90
};
Loading

0 comments on commit 47d4371

Please sign in to comment.