Skip to content

Commit

Permalink
Merge pull request elizaOS#3096 from HMXOrg/develop
Browse files Browse the repository at this point in the history
feat: plugin desk exchange
  • Loading branch information
samarth30 authored Feb 3, 2025
2 parents 9ab8f5a + b25a962 commit 085e970
Show file tree
Hide file tree
Showing 17 changed files with 919 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -952,4 +952,7 @@ ARBITRAGE_EVM_PRIVATE_KEY= # Private key for the wallet executi
FLASHBOTS_RELAY_SIGNING_KEY= # Signing key for Flashbots relay interactions
BUNDLE_EXECUTOR_ADDRESS= # Address of the bundle executor contract

# DESK Exchange Plugin Configration
DESK_EXCHANGE_PRIVATE_KEY= # Required for trading and cancelling orders
DESK_EXCHANGE_NETWORK= # "mainnet" or "testnet

1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@elizaos/plugin-coinmarketcap": "workspace:*",
"@elizaos/plugin-conflux": "workspace:*",
"@elizaos/plugin-cosmos": "workspace:*",
"@elizaos/plugin-desk-exchange": "workspace:*",
"@elizaos/plugin-echochambers": "workspace:*",
"@elizaos/plugin-evm": "workspace:*",
"@elizaos/plugin-edwin": "workspace:*",
Expand Down
5 changes: 5 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { coinmarketcapPlugin } from "@elizaos/plugin-coinmarketcap";
import { confluxPlugin } from "@elizaos/plugin-conflux";
import { createCosmosPlugin } from "@elizaos/plugin-cosmos";
import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm";
import { deskExchangePlugin } from "@elizaos/plugin-desk-exchange";
import { evmPlugin } from "@elizaos/plugin-evm";
import { edwinPlugin } from "@elizaos/plugin-edwin";
import { flowPlugin } from "@elizaos/plugin-flow";
Expand Down Expand Up @@ -1300,6 +1301,10 @@ export async function createAgent(
getSecret(character, "ARBITRAGE_BUNDLE_EXECUTOR_ADDRESS")
? arbitragePlugin
: null,
getSecret(character, "DESK_EXCHANGE_PRIVATE_KEY") ||
getSecret(character, "DESK_EXCHANGE_NETWORK")
? deskExchangePlugin
: null,
]
.flat()
.filter(Boolean),
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-desk-exchange/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
95 changes: 95 additions & 0 deletions packages/plugin-desk-exchange/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# DESK Exchange Plugin for Eliza

This plugin enables interaction with the DESK Perpetual DEX through Eliza, providing perpetual futures trading capabilities. Visit [DESK Exchange](https://desk.exchange/) for more details.
## Features
- 💱 Perpetual Trading
- Market orders (immediate execution)
- Limit orders (price-specific)
- 🔄 Order Management
- Cancel all open orders
- 🏦 Account summary
- View open orders
- View active positions
- View collateral balances

## Installation

Add the plugin to your Eliza configuration:

```json
{
"plugins": ["@elizaos/plugin-desk-exchange"]
}
```

## Configuration

Set the following environment variables:

```env
DESK_EXCHANGE_PRIVATE_KEY=your_private_key # Required for trading and cancelling orders
DESK_EXCHANGE_NETWORK= # "mainnet" or "testnet
```

## Available Actions

### 1. PERP_TRADE

Place perp market or limit orders.

Examples:

```
# Market Orders
"long 1 BTC" -> Place buy order of 1 BTC at market price
"sell 2 ETH" -> Sells 2 ETH at market price
"market buy 1 ETH" -> Buys 1 ETH at market price
# Limit Orders
"buy 1 SOL at 20 USDC" -> Places buy order for 1 SOL at 20 USDC
"sell 0.5 BASE at 21 USDC" -> Places sell order for 0.5 BASE at 21 USDC
```

### 2. CANCEL_ORDERS

Cancel all your open orders.

Examples:

```
"Cancel all orders"
"Cancel my orders"
```

### 3. GET_PERP_ACCOUNT_SUMMARY

Display the summary of your current account with details on open orders, active position and collateral tokens.

Examples:

```
"Check my account please"
"Here is the summary of your account 0xxxxxxxx
Your positions:
- Long 1.0039 BTCUSD
- Short 10.01 ETHUSD
- Long 135808.80 SOLUSD
Your orders:
- Sell 0/0.0001 BTCUSD @200000.00
Your collaterals:
- 1382295.125325162 USDC
- 2000000.00 CREDIT"
```

## Security Notes

- Store your private key securely using environment variables
- Test with small amounts first
- Use testnet for initial testing
- Monitor your orders regularly
- Double-check prices before confirming trades

## License

MIT
21 changes: 21 additions & 0 deletions packages/plugin-desk-exchange/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@elizaos/plugin-desk-exchange",
"version": "0.1.0",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"zod": "^3.23.8",
"ethers": "^6.13.5",
"axios": "^1.7.9"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch"
}
}
187 changes: 187 additions & 0 deletions packages/plugin-desk-exchange/src/actions/accountSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import {
type Action,
type ActionExample,
type IAgentRuntime,
type Memory,
type State,
type HandlerCallback,
composeContext,
elizaLogger,
} from "@elizaos/core";
import { accountSummaryTemplate } from "../templates";
import { ethers } from "ethers";
import {
generateNonce,
generateJwt,
getSubaccount,
getEndpoint,
formatNumber,
} from "../services/utils";
import { getSubaccountSummary } from "../services/account";

export const accountSummary: Action = {
name: "GET_PERP_ACCOUNT_SUMMARY",
similes: [
"CHECK_ACCOUNT",
"CHECK_PERP_ACCOUNT",
"ACCOUNT_SUMMARY",
"PERP_ACCOUNT_SUMMARY",
],
description: "Get the current account summary",
validate: async (runtime: IAgentRuntime) => {
return !!(
runtime.getSetting("DESK_EXCHANGE_PRIVATE_KEY") &&
runtime.getSetting("DESK_EXCHANGE_NETWORK")
);
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
options: Record<string, unknown>,
callback?: HandlerCallback
) => {
// Initialize or update state
state = !state
? await runtime.composeState(message)
: await runtime.updateRecentMessageState(state);

const context = composeContext({
state,
template: accountSummaryTemplate,
});

try {
const endpoint = getEndpoint(runtime);
const wallet = new ethers.Wallet(
runtime.getSetting("DESK_EXCHANGE_PRIVATE_KEY")
);
const jwt = await generateJwt(endpoint, wallet, 0, generateNonce());

const response = await getSubaccountSummary(
endpoint,
jwt,
getSubaccount(wallet.address, 0)
);
elizaLogger.info(response.data);

const subaccountSummaryData = response.data.data;
const positionSummary =
subaccountSummaryData.positions.length > 0
? subaccountSummaryData.positions
.map((p) => {
return `- ${p.side} ${formatNumber(p.quantity)} ${
p.symbol
}`;
})
.join("\n")
: "- No active position";
const orderSummary =
subaccountSummaryData.open_orders.length > 0
? subaccountSummaryData.open_orders
.map((o) => {
return `- ${
o.side === "Long" ? "Buy" : "Sell"
} ${formatNumber(
Number(o.original_quantity) -
Number(o.remaining_quantity)
)}/${formatNumber(o.original_quantity)} ${
o.symbol
} @${
Number(o.price) > 0
? formatNumber(o.price)
: formatNumber(o.trigger_price)
}`;
})
.join("\n")
: "- No orders";
const collateralSummary =
subaccountSummaryData.collaterals.length > 0
? subaccountSummaryData.collaterals
.map((c) => {
return `- ${formatNumber(c.amount, 4)} ${
c.asset
}`;
})
.join("\n")
: "- No collateral";
callback({
text:
`Here is the summary of your account ${wallet.address}\n` +
`Your positions:\n` +
positionSummary +
`\n` +
`Your orders:\n` +
orderSummary +
`\n` +
`Your collaterals:\n` +
collateralSummary,
content: subaccountSummaryData,
});

return true;
} catch (error) {
elizaLogger.error("Error getting account summary:", {
message: error.message,
code: error.code,
data: error.response?.data,
});
if (callback) {
callback({
text: `Error getting account summary: ${error.message} ${error.response?.data?.errors}`,
content: { error: error.message },
});
}
return false;
}
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "Check my account please",
},
},
{
user: "{{agent}}",
content: {
text: "Here is the summary of your account",
action: "GET_PERP_ACCOUNT_SUMMARY",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "How is my account doing?",
},
},
{
user: "{{agent}}",
content: {
text: "Here is the summary of your account",
action: "GET_PERP_ACCOUNT_SUMMARY",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Account summary",
},
},
{
user: "{{agent}}",
content: {
text: "Here is the summary of your account",
action: "GET_PERP_ACCOUNT_SUMMARY",
},
},
],
] as ActionExample[][],
};

export default accountSummary;
Loading

0 comments on commit 085e970

Please sign in to comment.