Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugin desk exchange #3096

Merged
merged 36 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
98b46f6
feat: can call to backend
john-xina-p88 Jan 28, 2025
a718f33
feat: trade successful with callback
john-xina-p88 Jan 28, 2025
265c231
feat: gen nonce and jwt
john-xina-p88 Jan 28, 2025
003a061
feat: change to PERP_TRADE
john-xina-p88 Jan 28, 2025
d6cbb70
feat: add order side in callback message
john-xina-p88 Jan 28, 2025
718516f
feat: refactor to use axios
john-xina-p88 Jan 28, 2025
c78d068
feat: add content validation
john-xina-p88 Jan 29, 2025
7b86f4b
feat: limit trade and better error handling
john-xina-p88 Jan 29, 2025
fb81403
feat: refactor common functions in to services
john-xina-p88 Jan 29, 2025
40dd70f
feat: account summary
john-xina-p88 Jan 29, 2025
31916c2
feat: cancel orders
john-xina-p88 Jan 29, 2025
f5fac7f
feat: make example inconsistent
john-xina-p88 Jan 29, 2025
b4d2508
feat: prevent duplicate plugin initialization
john-xina-p88 Jan 29, 2025
1f4bcac
chore: readme and housekeeping
john-xina-p88 Jan 29, 2025
45cbc0a
chore: add env in example
john-xina-p88 Jan 29, 2025
be8299c
feat: add validation and timeout
john-xina-p88 Jan 29, 2025
a151be2
feat: refrain from storing jwt for now
john-xina-p88 Jan 29, 2025
dcc3131
feat: edit wrong template
john-xina-p88 Jan 29, 2025
c690e8b
feat: update nonce generation to be crypto secured
john-xina-p88 Jan 29, 2025
f740f51
feat: add timeout and validation
john-xina-p88 Jan 29, 2025
08857ab
feat: proper env validation
john-xina-p88 Jan 29, 2025
a182c8c
feat: revert pnpm-lock
john-xina-p88 Jan 29, 2025
229e179
Merge branch 'develop' into feature/plugin-desk-exchange
john-xina-p88 Jan 29, 2025
8c874cf
feat: edit prod endpoints
john-xina-p88 Jan 29, 2025
ab6ed36
chore: typo
john-xina-p88 Jan 30, 2025
79b4238
chore: typo
john-xina-p88 Jan 30, 2025
768b0d8
feat: format number
john-xina-p88 Jan 30, 2025
632365d
chore: readme
john-xina-p88 Jan 31, 2025
cb99f44
feat: sequentially cancel orders
john-xina-p88 Jan 31, 2025
c6b92df
Merge branch 'develop' into feature/plugin-desk-exchange
john-xina-p88 Jan 31, 2025
d9d9ec3
Merge pull request #1 from HMXOrg/feature/plugin-desk-exchange
john-xina-p88 Jan 31, 2025
64ed7f7
feat: use bigint to prevent overflow
john-xina-p88 Jan 31, 2025
f433cd1
Merge branch 'develop' into develop
john-xina-p88 Jan 31, 2025
56c8845
Merge branch 'develop' into develop
john-xina-p88 Jan 31, 2025
199e1ba
Merge branch 'develop' into develop
john-xina-p88 Feb 1, 2025
b25a962
Merge branch 'develop' into develop
john-xina-p88 Feb 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-flow": "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 { flowPlugin } from "@elizaos/plugin-flow";
import { fuelPlugin } from "@elizaos/plugin-fuel";
Expand Down Expand Up @@ -1295,6 +1296,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
Loading