Skip to content

Commit d9d9ec3

Browse files
Merge pull request #1 from HMXOrg/feature/plugin-desk-exchange
feat: plugin-desk-exchange
2 parents 681e38d + c6b92df commit d9d9ec3

17 files changed

+919
-0
lines changed

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -952,4 +952,7 @@ ARBITRAGE_EVM_PRIVATE_KEY= # Private key for the wallet executi
952952
FLASHBOTS_RELAY_SIGNING_KEY= # Signing key for Flashbots relay interactions
953953
BUNDLE_EXECUTOR_ADDRESS= # Address of the bundle executor contract
954954

955+
# DESK Exchange Plugin Configration
956+
DESK_EXCHANGE_PRIVATE_KEY= # Required for trading and cancelling orders
957+
DESK_EXCHANGE_NETWORK= # "mainnet" or "testnet
955958

agent/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@elizaos/plugin-coinmarketcap": "workspace:*",
5656
"@elizaos/plugin-conflux": "workspace:*",
5757
"@elizaos/plugin-cosmos": "workspace:*",
58+
"@elizaos/plugin-desk-exchange": "workspace:*",
5859
"@elizaos/plugin-echochambers": "workspace:*",
5960
"@elizaos/plugin-evm": "workspace:*",
6061
"@elizaos/plugin-flow": "workspace:*",

agent/src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import { coinmarketcapPlugin } from "@elizaos/plugin-coinmarketcap";
8484
import { confluxPlugin } from "@elizaos/plugin-conflux";
8585
import { createCosmosPlugin } from "@elizaos/plugin-cosmos";
8686
import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm";
87+
import { deskExchangePlugin } from "@elizaos/plugin-desk-exchange";
8788
import { evmPlugin } from "@elizaos/plugin-evm";
8889
import { flowPlugin } from "@elizaos/plugin-flow";
8990
import { fuelPlugin } from "@elizaos/plugin-fuel";
@@ -1296,6 +1297,10 @@ export async function createAgent(
12961297
getSecret(character, "ARBITRAGE_BUNDLE_EXECUTOR_ADDRESS")
12971298
? arbitragePlugin
12981299
: null,
1300+
getSecret(character, "DESK_EXCHANGE_PRIVATE_KEY") ||
1301+
getSecret(character, "DESK_EXCHANGE_NETWORK")
1302+
? deskExchangePlugin
1303+
: null,
12991304
]
13001305
.flat()
13011306
.filter(Boolean),
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*
2+
3+
!dist/**
4+
!package.json
5+
!readme.md
6+
!tsup.config.ts
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# DESK Exchange Plugin for Eliza
2+
3+
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.
4+
## Features
5+
- 💱 Perpetual Trading
6+
- Market orders (immediate execution)
7+
- Limit orders (price-specific)
8+
- 🔄 Order Management
9+
- Cancel all open orders
10+
- 🏦 Account summary
11+
- View open orders
12+
- View active positions
13+
- View collateral balances
14+
15+
## Installation
16+
17+
Add the plugin to your Eliza configuration:
18+
19+
```json
20+
{
21+
"plugins": ["@elizaos/plugin-desk-exchange"]
22+
}
23+
```
24+
25+
## Configuration
26+
27+
Set the following environment variables:
28+
29+
```env
30+
DESK_EXCHANGE_PRIVATE_KEY=your_private_key # Required for trading and cancelling orders
31+
DESK_EXCHANGE_NETWORK= # "mainnet" or "testnet
32+
```
33+
34+
## Available Actions
35+
36+
### 1. PERP_TRADE
37+
38+
Place perp market or limit orders.
39+
40+
Examples:
41+
42+
```
43+
# Market Orders
44+
"long 1 BTC" -> Place buy order of 1 BTC at market price
45+
"sell 2 ETH" -> Sells 2 ETH at market price
46+
"market buy 1 ETH" -> Buys 1 ETH at market price
47+
48+
# Limit Orders
49+
"buy 1 SOL at 20 USDC" -> Places buy order for 1 SOL at 20 USDC
50+
"sell 0.5 BASE at 21 USDC" -> Places sell order for 0.5 BASE at 21 USDC
51+
```
52+
53+
### 2. CANCEL_ORDERS
54+
55+
Cancel all your open orders.
56+
57+
Examples:
58+
59+
```
60+
"Cancel all orders"
61+
"Cancel my orders"
62+
```
63+
64+
### 3. GET_PERP_ACCOUNT_SUMMARY
65+
66+
Display the summary of your current account with details on open orders, active position and collateral tokens.
67+
68+
Examples:
69+
70+
```
71+
"Check my account please"
72+
73+
"Here is the summary of your account 0xxxxxxxx
74+
Your positions:
75+
- Long 1.0039 BTCUSD
76+
- Short 10.01 ETHUSD
77+
- Long 135808.80 SOLUSD
78+
Your orders:
79+
- Sell 0/0.0001 BTCUSD @200000.00
80+
Your collaterals:
81+
- 1382295.125325162 USDC
82+
- 2000000.00 CREDIT"
83+
```
84+
85+
## Security Notes
86+
87+
- Store your private key securely using environment variables
88+
- Test with small amounts first
89+
- Use testnet for initial testing
90+
- Monitor your orders regularly
91+
- Double-check prices before confirming trades
92+
93+
## License
94+
95+
MIT
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@elizaos/plugin-desk-exchange",
3+
"version": "0.1.0",
4+
"main": "dist/index.js",
5+
"type": "module",
6+
"types": "dist/index.d.ts",
7+
"dependencies": {
8+
"@elizaos/core": "workspace:*",
9+
"zod": "^3.23.8",
10+
"ethers": "^6.13.5",
11+
"axios": "^1.7.9"
12+
},
13+
"devDependencies": {
14+
"@types/node": "^20.0.0",
15+
"tsup": "8.3.5"
16+
},
17+
"scripts": {
18+
"build": "tsup --format esm --dts",
19+
"dev": "tsup --format esm --dts --watch"
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import {
2+
type Action,
3+
type ActionExample,
4+
type IAgentRuntime,
5+
type Memory,
6+
type State,
7+
type HandlerCallback,
8+
composeContext,
9+
elizaLogger,
10+
} from "@elizaos/core";
11+
import { accountSummaryTemplate } from "../templates";
12+
import { ethers } from "ethers";
13+
import {
14+
generateNonce,
15+
generateJwt,
16+
getSubaccount,
17+
getEndpoint,
18+
formatNumber,
19+
} from "../services/utils";
20+
import { getSubaccountSummary } from "../services/account";
21+
22+
export const accountSummary: Action = {
23+
name: "GET_PERP_ACCOUNT_SUMMARY",
24+
similes: [
25+
"CHECK_ACCOUNT",
26+
"CHECK_PERP_ACCOUNT",
27+
"ACCOUNT_SUMMARY",
28+
"PERP_ACCOUNT_SUMMARY",
29+
],
30+
description: "Get the current account summary",
31+
validate: async (runtime: IAgentRuntime) => {
32+
return !!(
33+
runtime.getSetting("DESK_EXCHANGE_PRIVATE_KEY") &&
34+
runtime.getSetting("DESK_EXCHANGE_NETWORK")
35+
);
36+
},
37+
handler: async (
38+
runtime: IAgentRuntime,
39+
message: Memory,
40+
state: State,
41+
options: Record<string, unknown>,
42+
callback?: HandlerCallback
43+
) => {
44+
// Initialize or update state
45+
state = !state
46+
? await runtime.composeState(message)
47+
: await runtime.updateRecentMessageState(state);
48+
49+
const context = composeContext({
50+
state,
51+
template: accountSummaryTemplate,
52+
});
53+
54+
try {
55+
const endpoint = getEndpoint(runtime);
56+
const wallet = new ethers.Wallet(
57+
runtime.getSetting("DESK_EXCHANGE_PRIVATE_KEY")
58+
);
59+
const jwt = await generateJwt(endpoint, wallet, 0, generateNonce());
60+
61+
const response = await getSubaccountSummary(
62+
endpoint,
63+
jwt,
64+
getSubaccount(wallet.address, 0)
65+
);
66+
elizaLogger.info(response.data);
67+
68+
const subaccountSummaryData = response.data.data;
69+
const positionSummary =
70+
subaccountSummaryData.positions.length > 0
71+
? subaccountSummaryData.positions
72+
.map((p) => {
73+
return `- ${p.side} ${formatNumber(p.quantity)} ${
74+
p.symbol
75+
}`;
76+
})
77+
.join("\n")
78+
: "- No active position";
79+
const orderSummary =
80+
subaccountSummaryData.open_orders.length > 0
81+
? subaccountSummaryData.open_orders
82+
.map((o) => {
83+
return `- ${
84+
o.side === "Long" ? "Buy" : "Sell"
85+
} ${formatNumber(
86+
Number(o.original_quantity) -
87+
Number(o.remaining_quantity)
88+
)}/${formatNumber(o.original_quantity)} ${
89+
o.symbol
90+
} @${
91+
Number(o.price) > 0
92+
? formatNumber(o.price)
93+
: formatNumber(o.trigger_price)
94+
}`;
95+
})
96+
.join("\n")
97+
: "- No orders";
98+
const collateralSummary =
99+
subaccountSummaryData.collaterals.length > 0
100+
? subaccountSummaryData.collaterals
101+
.map((c) => {
102+
return `- ${formatNumber(c.amount, 4)} ${
103+
c.asset
104+
}`;
105+
})
106+
.join("\n")
107+
: "- No collateral";
108+
callback({
109+
text:
110+
`Here is the summary of your account ${wallet.address}\n` +
111+
`Your positions:\n` +
112+
positionSummary +
113+
`\n` +
114+
`Your orders:\n` +
115+
orderSummary +
116+
`\n` +
117+
`Your collaterals:\n` +
118+
collateralSummary,
119+
content: subaccountSummaryData,
120+
});
121+
122+
return true;
123+
} catch (error) {
124+
elizaLogger.error("Error getting account summary:", {
125+
message: error.message,
126+
code: error.code,
127+
data: error.response?.data,
128+
});
129+
if (callback) {
130+
callback({
131+
text: `Error getting account summary: ${error.message} ${error.response?.data?.errors}`,
132+
content: { error: error.message },
133+
});
134+
}
135+
return false;
136+
}
137+
},
138+
examples: [
139+
[
140+
{
141+
user: "{{user1}}",
142+
content: {
143+
text: "Check my account please",
144+
},
145+
},
146+
{
147+
user: "{{agent}}",
148+
content: {
149+
text: "Here is the summary of your account",
150+
action: "GET_PERP_ACCOUNT_SUMMARY",
151+
},
152+
},
153+
],
154+
[
155+
{
156+
user: "{{user1}}",
157+
content: {
158+
text: "How is my account doing?",
159+
},
160+
},
161+
{
162+
user: "{{agent}}",
163+
content: {
164+
text: "Here is the summary of your account",
165+
action: "GET_PERP_ACCOUNT_SUMMARY",
166+
},
167+
},
168+
],
169+
[
170+
{
171+
user: "{{user1}}",
172+
content: {
173+
text: "Account summary",
174+
},
175+
},
176+
{
177+
user: "{{agent}}",
178+
content: {
179+
text: "Here is the summary of your account",
180+
action: "GET_PERP_ACCOUNT_SUMMARY",
181+
},
182+
},
183+
],
184+
] as ActionExample[][],
185+
};
186+
187+
export default accountSummary;

0 commit comments

Comments
 (0)