Skip to content

About

Official AI agent skills, TypeScript SDK and MCP server (117 tools) for GDEX (gdex.pro), Gemach DAO's multi-chain trading terminal: Solana and EVM spot, Hyperliquid perps, copy trading

Topics

Resources

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

149 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
 β–ˆβ–ˆβ•”β•β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—
 β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—   β•šβ–ˆβ–ˆβ–ˆβ•”β•    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
 β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•   β–ˆβ–ˆβ•”β–ˆβ–ˆβ•—    β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
 β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
  β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•   β•šβ•β•     β•šβ•β•  β•šβ•β• β•šβ•β•β•β•β•β•
               Β· p r o Β·    powered by GEMACH

AI Agent Skill for GDEX Pro β€” the self-custody trading terminal by Gemach
Cross-chain spot Β· HyperLiquid perps Β· Copy trading Β· Portfolio Β· Token discovery Β· Managed custody

npm version TypeScript License: MIT skills.sh Tests


Why agents use GDEX

GDEX is the trading surface. This skill is how AI agents (Claude, Cursor, Codex, and 40+ others) install and trade on it β€” spot, perps, copy-trade, and bridge β€” without building their own exchange stack.

Fastest path for an agent:

npx skills add GemachDAO/gdex-skill --all --agent '*' -g

Then open gdex.pro for the human terminal, or run the MCP server so the agent can execute trades itself.


Table of Contents


πŸ€– Install as an Agent Skill

Install directly into Claude Code, Cursor, Codex, Windsurf, and 40+ other agents using the skills CLI:

# Install all skills (recommended)
npx skills add GemachDAO/gdex-skill --all --agent '*' -g

# Pick skills interactively
npx skills add GemachDAO/gdex-skill

# Install a specific skill
npx skills add GemachDAO/gdex-skill --skill gdex-spot-trading

GDEX uses a multi-skill architecture β€” agents load only the skills they need, keeping context lean and focused.

Available Skills

Skill Description
gdex-agent-trading Start here for agents: status β†’ connect β†’ preview β†’ execute within your authority β†’ follow β†’ exit
gdex-onboarding Platform overview, architecture, supported chains, quickstart
gdex-retailer-onboarding Retailer partner integrations β€” branded onboarding partners on the GDEX stack
gdex-authentication Managed-custody auth, encryption, session keys, API key login
gdex-spot-trading Buy/sell tokens on any chain with DEX routing
gdex-perp-trading HyperLiquid perpetual futures β€” positions, orders, leverage
gdex-perp-funding Deposit/withdraw USDC to/from HyperLiquid
gdex-limit-orders Create, cancel, and list limit orders
gdex-portfolio Cross-chain portfolio, balances, trade history
gdex-token-discovery Token details, trending tokens, OHLCV charts (no auth)
gdex-token-import Import custom tokens into details, balances and portfolio
gdex-livestream-discovery Solana livestream tokens, live status, big-buy alerts
gdex-watchlist-social Watchlists, token comments, sentiment voting
gdex-trending-promotion Book paid trending slots and check booking status
gdex-xstocks Tokenised equities (xStocks) listing
gdex-content-coins Zora content coins and creator coins on Base
gdex-copy-trading Copy trade create/delete, leaderboards, tx history, DEX list (Solana only for writes)
gdex-perp-copy-trading HL perp copy trading β€” top traders, create/manage configs, market data
gdex-hl-outcomes Hyperliquid HIP-4 outcome markets β€” discover, pick a side, limit order, cancel/exit, reconcile (no Polymarket/Kalshi)
gdex-hl-referral HyperLiquid referral info and reward claims
gdex-bridge Cross-chain bridging with quotes
gdex-usdc-wallet Unified USDC wallet β€” one USDC balance across chains, buy/sell in USDC, auto-bridge funding
gdex-transfers Native and ERC20/SPL transfers via managed custody
gdex-wallet-setup Generate EVM wallets, session keys, wallet info (no auth)
gdex-ui-install-setup React/Next.js project setup, SDK context providers, environment variables
gdex-ui-trading-components React component patterns for order forms, position tables, copy trade panels
gdex-ui-portfolio-dashboard Portfolio dashboard components β€” balances, trade history, chain selectors
gdex-ui-wallet-connection Wallet connection UI β€” connect buttons, auth state, chain switching
gdex-ui-theming CSS theming β€” dark/light mode, trading colors, responsive breakpoints, Tailwind
gdex-ui-page-layouts Full page compositions β€” trading, portfolio, copy trading, bridge pages
gdex-sdk-debugging Troubleshoot errors β€” error codes, encryption debugging, chain quirks, HL gotchas

Each skill's description tells the agent when to load it. No API key setup required for trading skills (shared keys are built in).

Risk & Market Data Skills

Deterministic data feeds and events for risk and research. Each ships a standard-library Python script that prints NDJSON: no API key, no install, and every number comes from the script, not a model.

Skill Output
gdex-hl-market-risk ~234 HyperLiquid core perps: funding, open interest, oracle premium, leverage caps, delisting
gdex-hl-anomaly Scored, time-stamped HyperLiquid anomaly events (oracle divergence, funding extremity, liquidity shock) against per-market learned baselines, plus a coverage record per run. Backtest
gdex-token-risk GDEX token screen on 12 chains: price, liquidity, volume, honeypot, taxes, LP lock, holder concentration (missing security data is never "safe")
gvault GVault (GMACL, Enzyme on Ethereum): NAV, share price, holdings, cumulative and annualised return
python3 skills/gdex-hl-market-risk/scripts/hl_market_risk.py > hl.ndjson

Skills Harness (bring your own key)

harness/ runs these skills with Claude on your own Anthropic API key. export produces every feed with no model; ask answers questions with an agent that can only cite figures that skill scripts printed. Every script run is logged with a sha256 of its output.


πŸ”Œ MCP Server

The GDEX MCP server exposes 154 tools β€” full trading execution + SDK documentation β€” as Model Context Protocol tools. Any MCP-compatible AI agent can trade autonomously.

Source code: the server is implemented in this repository, in mcp-server/. The entry point is mcp-server/src/index.ts and the tool handlers are in mcp-server/src/tools/. The npm package @gemachdao/gdex-mcp-server is built from that directory by the release workflow. See mcp-server/README.md to run it from source or with the repo's Dockerfile.

Quick Setup

# Auto-generate config for your AI client
npx @gemachdao/gdex-mcp-server init --client claude   # β†’ .mcp.json
npx @gemachdao/gdex-mcp-server init --client cursor   # β†’ .cursor/mcp.json
npx @gemachdao/gdex-mcp-server init --client vscode   # β†’ .vscode/mcp.json
npx @gemachdao/gdex-mcp-server init --client codex    # β†’ .codex/config.toml
npx @gemachdao/gdex-mcp-server init --client opencode  # β†’ opencode.json ("mcp" section)

init merges into an existing config: other MCP servers and settings are kept, the previous file is saved as <file>.bak, and rerunning is a no-op. If a different gdex-mcp-server entry is already there it is shown and left alone unless you pass --force; a config it can't parse is never modified.

Claude Code plugin

/plugin marketplace add GemachDAO/gdex-skill
/plugin install gdex@gemachdao

Installs every GDEX skill plus the MCP server (npx -y @gemachdao/gdex-mcp-server). The optional GDEX API key is kept in your system credential store.

Requirements

Node.js 22 or newer. The Hyperliquid client libraries need Node 22's built-in WebSocket; on Node 20 the perp tools fail.

Agent trading flow

What is verified live versus offline, per chain and venue, is in docs/AGENT-READINESS.md.

Agents should start with gdex_status, connect once with gdex_connect, then gdex_preview_trade β†’ gdex_execute_trade (user-approved, or within the delegated policy in ~/.gdex/policies/<accountRef>.json) β†’ gdex_operation, and manage exits with gdex_positions / gdex_manage_position. Every tool carries read/write annotations. See the gdex-agent-trading skill.

Connect an account (no secrets in chat)

Ask your agent to run gdex_connect with a key file on your machine, e.g. gdex_connect { keyFile: "~/my-gdex-wallet.json" } (JSON with privateKey), or gdex_connect { create: true } for a new wallet. The server signs in, stores the keys under ~/.gdex/accounts (mode 0600) and returns an accountRef. Pass accountRef to any trading tool instead of sessionPrivateKey/privateKey. Never paste a private key or seed phrase into the chat. gdex_accounts lists connected accounts; gdex_disconnect forgets one.

Codex (OpenAI)

codex plugin marketplace add GemachDAO/gdex-skill
codex plugin add gdex@gemachdao

Installs every GDEX skill plus the MCP server. For the MCP server only: codex mcp add gdex -- npx -y @gemachdao/gdex-mcp-server.

Gemini CLI extension

gemini extensions install https://github.com/GemachDAO/gdex-skill

Manual Config

Add to your client's MCP config:

{
  "mcpServers": {
    "gdex-mcp-server": {
      "command": "npx",
      "args": ["@gemachdao/gdex-mcp-server"],
      "env": {
        "GDEX_API_KEY": "your-api-key"
      }
    }
  }
}

Environment Variables

Variable Description Required
GDEX_API_KEY GDEX API key β€” auto-authenticates on startup Optional
GDEX_API_URL Override API base URL (default: https://trade-api.gemach.io/v1) Optional

MCP Execution Tools (145 tools)

Category Tools Description
Agent flow gdex_status, gdex_preview_trade, gdex_execute_trade, gdex_operation, gdex_positions, gdex_manage_position Readiness, exact previews, execution within user approval or a delegated policy, operation tracking and recovery, positions and exits
Auth gdex_connect, gdex_accounts, gdex_disconnect, auth_login, generate_session_keypair, managed_sign_in, build_sign_in_payload API key login, session keys, managed custody sign-in
Spot Trading buy_token, sell_token Buy/sell on Solana, Sui and 10 EVM chains (Ethereum, Base, Arbitrum, BSC and more)
Perp Trading open_perp_position, place_perp_order, close_perp_position, close_all_positions, cancel_perp_order, cancel_all_perp_orders, set_leverage, perp_deposit, perp_withdraw Full HyperLiquid perpetual futures β€” long/short, TP/SL; leverage up to each market's cap (40x on core BTC)
Perp Data get_account_state, get_perp_positions, get_mark_price, get_all_mid_prices, get_usdc_balance, get_hl_open_orders, get_hl_trade_history, get_hl_spot_state, get_trader_leverage Real-time HyperLiquid account, positions, prices
Direct Execution execute_cross_perp, execute_isolated_perp, execute_spot, direct_cancel_order Private-key execution β€” cross/isolated margin, spot, cancel
Limit Orders limit_buy, limit_sell, update_order, get_limit_orders Limit buy/sell with TP/SL, order management
Copy Trading (Solana) get_copy_trade_wallets, get_copy_trade_custom_wallets, get_copy_trade_gems, get_copy_trade_dexes, get_copy_trade_list, get_copy_trade_tx_list, create_copy_trade, update_copy_trade Auto-mirror top Solana traders
Copy Trading (HL Perp) get_hl_top_traders, get_hl_top_traders_by_pnl, get_hl_user_stats, get_hl_perp_dexes, get_hl_all_assets, get_hl_clearinghouse_state, get_hl_meta_and_asset_ctxs, get_hl_deposit_tokens, get_hl_copy_trade_list, get_hl_copy_trade_tx_list, create_hl_copy_trade, update_hl_copy_trade Copy HyperLiquid perp traders
Portfolio & Data get_portfolio, get_balances, get_trade_history, get_token_details, get_trending_tokens, get_ohlcv, get_top_traders, get_wallet_info, generate_evm_wallet Cross-chain portfolio, market data, OHLCV candles
Bridge estimate_bridge, execute_bridge, get_bridge_orders Cross-chain native token bridging
Unified USDC Wallet usdc_get_balances, usdc_get_trade_quote, usdc_buy, usdc_sell, usdc_buy_with_native, usdc_retry_sell, usdc_get_funding_status, usdc_wait_for_trade, usdc_get_deferred_trade, usdc_get_pending_trades, usdc_get_convert_quote, usdc_convert_native, usdc_estimate_bridge, usdc_execute_bridge, usdc_get_bridge_status, usdc_get_bridge_orders One USDC balance across 7 chains; buy with USDC or native with automatic cross-chain funding and gas, live funding progress, sell to USDC, convert gas to USDC, bridge USDC
Managed Custody managed_purchase, managed_sell, managed_trade_status, build_trade_payload Low-level encrypted trade submission
Transfers transfer_native, transfer_token Send native and ERC20/SPL tokens via managed custody
Social & Watchlist add_comment, get_comments, vote_sentiment, get_watchlist, change_watchlist Token comments, sentiment votes, watchlists
Token Import import_token Add a custom token so it appears in details, balances and portfolio
Market Discovery & Analytics get_newest_tokens, get_top_tokens, get_token_trades, get_token_image, get_native_prices, get_xstocks, get_zora_tokens, get_wallet_performance, get_nof1_analytics, generate_pnl New and top tokens, trades, prices, xStocks, Zora coins, wallet performance, NoF1 analytics, PnL generation
Livestream get_currently_live, get_live_status, get_bigbuys Solana livestream tokens and big-buy alerts
HL Outcome Markets hl_outcomes, get_hl_outcome_volumes, hl_outcome_account, hl_outcome_check_collateral, hl_create_outcome_order, hl_cancel_outcome_order, hl_close_outcome_order Hyperliquid HIP-4 outcome markets
HL Account & Referral hl_enable_trading, hl_swap_collateral, hl_tx_list, hl_list_user_copy_pnl, hl_ref_info, hl_ref_claim, hl_builder_referral One-time HL enablement, HIP-3 collateral swaps, fills/orders, copy-trade PnL, referral rewards
Promotion & Partners trending_list, trending_options, trending_register, trending_booking_status, get_retailers Paid trending slots and retailer partners
Account oauth_login, associate_email Google sign-in and linking an email to a wallet

MCP Documentation Tools (9 tools)

Tool Description
search_gdex_docs Search documentation by keyword
get_sdk_pattern TypeScript code patterns by operation
get_api_info API endpoint details (URL, method, params)
explain_workflow Step-by-step trading workflows
get_chain_info Supported chains and capabilities
get_trading_guide Spot, perp, or limit trading guides
get_copy_trade_guide Copy trading guides (Solana / HL)
get_component_guide React UI component patterns
get_skill_file Read a file a skill ships with (e.g. scripts/token_risk.py)

πŸ“¦ SDK Installation

npm install github:GemachDAO/gdex-skill

Installs the SDK straight from GitHub (it builds on install). Imports stay from '@gemachdao/gdex-skill'. Pin a release with npm install github:GemachDAO/gdex-skill#v4.1.1.

The install script displays a quick-start banner in your terminal. Optional peer dependencies for wallet signing (only needed for user-specific wallet auth):

npm install ethers        # EVM wallet auth
npm install bs58 tweetnacl  # Solana wallet auth

πŸš€ Quick Start

import { GdexSkill, GDEX_API_KEY_PRIMARY } from '@gemachdao/gdex-skill';

// 1. Create skill instance
const skill = new GdexSkill();

// 2. Authenticate with the pre-configured shared key (enough for read-only calls)
skill.loginWithApiKey(GDEX_API_KEY_PRIMARY);

// 3. Trades need the managed-custody session from sign-in (see Managed-Custody Flow below):
//    the control wallet signs once, the session key signs each trade.
const creds = { apiKey: GDEX_API_KEY_PRIMARY, walletAddress: controlAddress, sessionPrivateKey };

// 4. Spot buy on Base (0.01 ETH)
const trade = await skill.buyToken({
  chain: 8453,
  tokenAddress: '0xTokenAddress',
  amount: '0.01',
  slippage: 1,     // 1% max slippage
  ...creds,
});
console.log('Trade submitted:', trade.jobId, 'β€” status:', trade.status);

// 5. Open a BTC 10Γ— long on HyperLiquid (size is in BTC, not USD)
const mark = await skill.getHlMarkPrice('BTC');
const pos = await skill.hlCreateOrder({
  coin: 'BTC',
  isLong: true,
  price: String(mark),
  size: '0.001',
  isMarket: true,
  leverage: 10,
  tpPrice: '110000',
  slPrice: '95000',
  ...creds,
});

// 6. Read-only endpoints need no auth
const trending = await skill.getTrendingTokens({ chain: 'solana', period: '24h', limit: 5 });
console.log('Trending:', trending.map(t => t.symbol).join(', '));

βœ… Verify (offline)

Confirm the SDK is installed and configured correctly β€” no network connection or API key required:

npm run verify

Sample output:

1. SDK import
  βœ“  SDK imported from ../dist/index.js

2. API keys
  βœ“  GDEX_API_KEY_PRIMARY   = 9b4e1c73...
  βœ“  GDEX_API_KEY_SECONDARY = 2c8f0a91...
  βœ“  GDEX_API_KEYS array    = 2 keys

3. GdexSkill instantiation
  βœ“  new GdexSkill() β€” default config
  βœ“  new GdexSkill({ apiUrl, timeout, maxRetries }) β€” custom config

4. Authentication state (offline)
  βœ“  isAuthenticated() = false before login
  βœ“  loginWithApiKey(GDEX_API_KEY_PRIMARY) β†’ isAuthenticated() = true
  βœ“  logout() β†’ isAuthenticated() = false

...

  All 20 checks passed βœ“
  SDK is ready β€” no network token required.

πŸ”‘ Authentication

Shared API Keys (recommended for agents)

Two shared keys are pre-configured in the package β€” agents do not need to sign wallet transactions:

import {
  GdexSkill,
  GDEX_API_KEY_PRIMARY,
  GDEX_API_KEY_SECONDARY,
  GDEX_API_KEYS,
} from '@gemachdao/gdex-skill';

const skill = new GdexSkill();
skill.loginWithApiKey(GDEX_API_KEY_PRIMARY);  // use primary
// or:
skill.loginWithApiKey(GDEX_API_KEY_SECONDARY); // use secondary
// or cycle through them:
skill.loginWithApiKey(GDEX_API_KEYS[0]);

skill.isAuthenticated(); // β†’ true
skill.logout();          // clear session

Note: Read-only endpoints (getTrendingTokens, getTokenDetails, getOHLCV, getTopTraders) do not require authentication.

Wallet-based Auth (advanced)

For user-owned wallets or custom signers (hardware wallets, browser extensions):

// EVM wallet (secp256k1)
await skill.authenticate({
  type: 'evm',
  address: '0xYourAddress',
  privateKey: '0xPrivateKey',
});

// Solana wallet (ed25519)
await skill.authenticate({
  type: 'solana',
  address: 'YourSolanaAddress',
  privateKey: 'base58EncodedPrivateKey',
});

// Custom signer (MetaMask / Phantom)
await skill.authenticate({
  type: 'evm',
  address: accounts[0],
  signer: async (message) =>
    window.ethereum.request({ method: 'personal_sign', params: [message, accounts[0]] }),
});

Configuration

const skill = new GdexSkill({
  apiUrl:     'https://trade-api.gemach.io/v1', // Backend (default)
  timeout:    30000,                          // Request timeout ms
  maxRetries: 3,                              // Retry attempts on 429/503
  debug:      false,                          // Log every request
});

The SDK does not read environment variables directly. If you choose to use env vars, read them in your application (for example via process.env) and pass their values into the GdexSkill constructor as shown above.

Recommended environment variables for your own app:

Env Variable Description Default
GDEX_API_URL Backend base URL to pass as apiUrl https://trade-api.gemach.io/v1
GDEX_API_KEY API key for AES encryption in managed-custody flow β€”
GDEX_TIMEOUT Request timeout (ms) to pass as timeout 30000
GDEX_MAX_RETRIES Retry attempts to pass as maxRetries 3
GDEX_DEBUG Enable debug logging to pass as debug false
GDEX_CONTROL_WALLET Control wallet address (userId) for managed custody β€”
GDEX_SESSION_PRIVATE Session private key (hex, 0x-prefixed) for managed custody β€”
GDEX_MANAGED_CHAIN_ID Chain ID for managed trades (622112261=Solana, 42161=Arbitrum for perps) 622112261
CONFIRM_LIVE_TRADE Set to YES to submit real trades β€”

πŸ”’ Managed-Custody Trading

All trading on GDEX goes through server-side managed wallets. Your control wallet (EVM or Solana) is only used to authenticate (sign-in) β€” actual on-chain execution is handled by GDEX backend trade workers.

How It Works

  1. Generate a session keypair β€” secp256k1 key used to sign trades after auth
  2. Sign-in β€” control wallet signs a message, encrypted as computedData β†’ POST /v1/sign_in
  3. Resolve user β€” GET /v1/user with encrypted session key to see managed wallets
  4. Trade β€” ABI-encode trade data, sign with session key, encrypt as computedData β†’ POST /v1/purchase_v2 or /v1/sell_v2
  5. Poll status β€” GET /v1/trade-status/:requestId until completed/failed

Encryption

All authenticated payloads use AES-256-CBC with a deterministic key/IV derived from the API key (no random IV):

  • Key = first 32 bytes of SHA256(apiKey) hex
  • IV = first 16 bytes of SHA256(SHA256(apiKey)) hex
  • Trade/sign-in payloads: JSON.stringify({ userId, data, signature, apiKey }) β†’ UTF-8 β†’ encrypt β†’ hex
  • Session key (/v1/user): hex-decoded raw bytes β†’ encrypt (not UTF-8 string)
  • The API key is included inside the encrypted JSON payload (not just used for encryption)

WARNING: Do NOT use random IVs or the iv:ciphertext format. The backend uses deterministic AES derived from the API key hash chain.

Signatures

Spot trade signatures (purchase/sell) use raw keccak256 + secp256k1 (no EIP-191 prefix):

  • Message: "<action>-<lowercaseUserId>-<dataHexWithout0x>"
  • Digest: keccak256(utf8Bytes(message))
  • Output: r(64 hex) + s(64 hex) + v(2 hex) = 130 chars, no 0x prefix
  • v = raw recovery parameter (00 or 01), NOT EIP-155 (1b/1c)

HL perp signatures use the same algorithm but with HL-specific action prefixes (hl_deposit, hl_withdraw, hl_create_order, etc.) and different ABI schemas. See the HL Managed-Custody Reference section.

Sign-in signatures use EIP-191 personal_sign with the control wallet β€” this is the ONLY operation that uses EIP-191. All post-sign-in operations use raw keccak256.

Nonce Generation

Nonces are client-generated (not fetched from the server):

const nonce = String(Math.floor(Date.now() / 1000) + Math.floor(Math.random() * 1000));

Quick Example

import {
  GdexSkill,
  GDEX_API_KEY_PRIMARY,
  generateGdexSessionKeyPair,
  buildGdexSignInMessage,
  buildGdexSignInComputedData,
  buildGdexManagedTradeComputedData,
  buildGdexUserSessionData,
} from '@gemachdao/gdex-skill';

const skill = new GdexSkill();
skill.loginWithApiKey(GDEX_API_KEY_PRIMARY);
const apiKey = GDEX_API_KEY_PRIMARY;

// 1. Session keypair
const { sessionPrivateKey, sessionKey } = generateGdexSessionKeyPair();

// 2. Sign-in (control wallet signs this message)
const userId = '0xYourControlWallet';
const nonce = String(Math.floor(Date.now() / 1000) + Math.floor(Math.random() * 1000));
const message = buildGdexSignInMessage(userId, nonce, sessionKey);
const signature = /* wallet.signMessage(message) */;

const signInPayload = buildGdexSignInComputedData({ apiKey, userId, sessionKey, nonce, signature });
await skill.signInWithComputedData({ computedData: signInPayload.computedData, chainId: 622112261 }); // Solana; 1 for EVM/HL

// 3. Trade
const trade = buildGdexManagedTradeComputedData({
  apiKey, action: 'purchase', userId,
  tokenAddress: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
  amount: '100000',
  nonce: String(Math.floor(Date.now() / 1000) + Math.floor(Math.random() * 1000)),
  sessionPrivateKey,
});
const result = await skill.submitManagedPurchase({
  computedData: trade.computedData, chainId: 622112261, slippage: 1, // Solana (NOT 900)
});

// 4. Poll
if (result.requestId) {
  const status = await skill.getManagedTradeStatus(result.requestId);
  console.log(status.status, status.hash);
}

Managed-Custody Helpers

Function Purpose
generateGdexSessionKeyPair() Generate secp256k1 session keypair
buildGdexSignInMessage(userId, nonce, sessionKey) Build the sign-in message for wallet signing
encodeGdexSignInData(sessionKey, nonce, refCode?) ABI-encode sign-in data (['bytes','string','string'])
buildGdexSignInComputedData({...}) Build encrypted sign-in payload
buildGdexUserSessionData(sessionKey, apiKey) Encrypt session key (raw hex bytes) for /v1/user
encodeGdexTradeData(tokenAddr, amount, nonce?) ABI-encode trade data (['string','uint256','string'])
signGdexTradeMessageWithSessionKey(action, userId, data, privKey) Sign trade with session key (v = raw recoveryParam 00/01)
buildGdexManagedTradeComputedData({...}) Build encrypted trade payload
encodeLimitOrderData(action, params) ABI-encode limit order data (buy/sell/update schemas)
signLimitOrderMessage(action, userId, data, privKey) Sign limit order with session key
buildLimitOrderComputedData({...}) Build encrypted limit order payload
createCopyTrade(...) / updateCopyTrade(...) Copy trade payloads are ABI-encoded (create: 12 fields, update: 16 fields, chainId is uint256), signed and encrypted inside these GdexSkill methods; the encoders are not exported
buildEncryptedGdexPayload({...}) Encrypt JSON {userId, data, signature} for computedData
encryptGdexComputedData(plaintext, apiKey) AES-256-CBC encrypt UTF-8 plaintext
encryptGdexHexData(hexData, apiKey) AES-256-CBC encrypt raw hex-decoded bytes
decryptGdexComputedData(cipherHex, apiKey) AES-256-CBC decrypt to UTF-8 plaintext
deriveGdexAesMaterial(apiKey) Get raw AES key/IV from API key

Verify Managed Flow (offline)

npm run verify:managed

This generates all payloads (session keypair, sign-in, user lookup, trade), validates encrypt/decrypt roundtrips, and prints the full execution plan β€” without making any live API calls.


πŸ“š API Reference

Spot Trading

buyToken(params)

Buy a token on any supported chain.

Parameter Type Required Description
chain string | ChainId βœ… Chain name or numeric ID
tokenAddress string βœ… Token contract address
amount string βœ… Native token input amount
slippage number Max slippage % (default: 1)
dex string Force specific DEX
walletAddress string Override wallet address
priorityFee number Solana priority fee (lamports)
const result = await skill.buyToken({
  chain: 'solana',
  tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  amount: '0.1',
  slippage: 1,
});
// result.jobId, result.status, result.txHash, result.outputAmount

sellToken(params)

// Sell absolute amount
await skill.sellToken({ chain: 8453, tokenAddress: '0x...', amount: '100', slippage: 0.5 });

// Sell 50% of holdings
await skill.sellToken({ chain: 'solana', tokenAddress: '...', amount: '50%' });

Perpetual Futures (HyperLiquid)

Critical: HyperLiquid deposits/withdrawals/orders use a different crypto flow than spot trades. See the HL Managed-Custody Reference section below for exact specifications.

Every perp write takes the managed-custody credentials { apiKey, walletAddress, sessionPrivateKey }, where walletAddress is the control wallet that signed in (MCP: pass accountRef from gdex_connect).

hlCreateOrder(params) β€” open (or add to) a position

Parameter Type Default Description
coin string Asset symbol (e.g., 'BTC', 'ETH', builder markets as 'xyz:NVDA')
isLong boolean true long, false short
price string Limit price; for market orders the mark price ('0' resolves to mark)
size string Size in contracts (coin units), not USD. Min order value $11
isMarket boolean true Market or limit
leverage number HL default (20Γ—) 1–50Γ—, sent as a top-level field with the order
tpPrice / slPrice string '' Optional TP / SL price ('' to skip)
reduceOnly boolean false Only reduce an existing position
const pos = await skill.hlCreateOrder({
  coin: 'BTC', isLong: true, price: String(await skill.getHlMarkPrice('BTC')), size: '0.001',
  isMarket: true, leverage: 10, tpPrice: '110000', slPrice: '95000',
  apiKey, walletAddress, sessionPrivateKey,
});

closePerpPosition(params)

Reads the live position and closes it with a reduce-only market order on the opposite side.

const creds = { apiKey, walletAddress, sessionPrivateKey };
await skill.closePerpPosition({ coin: 'BTC', positionAddress: managedEvmAddress, ...creds });                // close 100%
await skill.closePerpPosition({ coin: 'ETH', size: '0.05', positionAddress: managedEvmAddress, ...creds }); // partial close

Other perp methods

// Deposit USDC to HyperLiquid from Arbitrum (human-readable amount, converted internally; min 10 USDC)
await skill.perpDeposit({
  tokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
  amount: '10',
  chainId: 42161,
  apiKey, walletAddress, sessionPrivateKey,
});
await skill.perpWithdraw({ amount: '5', apiKey, walletAddress, sessionPrivateKey });

// Positions live on the managed EVM wallet
const positions = await skill.getPerpPositions({ walletAddress: managedEvmAddress });
// Each: { coin, side, size, entryPrice, markPrice, leverage, unrealizedPnl, liquidationPrice }

HL Deposit notes: Amount is in human-readable USDC (e.g., '10' for 10 USDC). The SDK automatically converts to smallest unit (6 decimals). Minimum deposit is 10 USDC. Your managed wallet must have the deposit amount + 1% fee buffer in USDC on Arbitrum. After the on-chain tx confirms, HyperLiquid takes ~10 minutes to credit the deposit.

Direct HyperLiquid Execution (via @gdexsdk/hyper-liquid-trader)

For direct trading on HyperLiquid L1 without managed custody β€” use your own private key:

// Cross-margin perpetual trade
const result = await skill.hlExecuteCrossPerp(
  process.env.PRIVATE_KEY!,
  { coin: 'BTC', isLong: true, price: '100000', positionSize: '0.001', leverage: 10 },
);

// Isolated-margin perpetual trade
const result2 = await skill.hlExecuteIsolatedPerp(
  process.env.PRIVATE_KEY!,
  { coin: 'ETH', isLong: false, price: '3000', positionSize: '1', leverage: 5 },
);

// Spot trade on HyperLiquid
const result3 = await skill.hlExecuteSpot(
  process.env.PRIVATE_KEY!,
  { coin: 'PURR', isBuy: true, price: '0.50', size: '100' },
);

// Cancel an order directly
await skill.hlDirectCancelOrder(process.env.PRIVATE_KEY!, 'BTC', orderId);

// Get all mid prices
const mids = await skill.getHlAllMids();

// Get trade history
const trades = await skill.getHlTradeHistory('0xYourWallet');

// Get a trader's leverage context (for copy trading)
const leverage = await skill.getHlTraderLeverageContext('0xTraderWallet', 'BTC');

// Create a standalone HyperLiquidTrading instance with custom WS URLs
const trader = await skill.createHlTrader(['wss://custom-ws.example.com']);

Limit Orders

Endpoints: limit_buy / limit_sell / update_order / orders β€” NOT orders/create or orders/cancel.

// Limit buy β€” buy WIF when price drops to $0.50 on Solana
const buyResult = await skill.limitBuy({
  apiKey: GDEX_API_KEY_PRIMARY,
  userId: '0x53D029a671bd1CF61a2fB1F4F6e4bD830BFBb2eD',  // control wallet
  sessionPrivateKey: '<session-key-hex>',
  chainId: 622112261,           // Solana
  tokenAddress: 'EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm',
  amount: '10000000',           // lamports
  triggerPrice: '0.50',
  profitPercent: '50',          // optional: TP at 50% gain
  lossPercent: '25',            // optional: SL at 25% loss
});

// Limit sell β€” sell WIF when price reaches $999.99 (take-profit)
const sellResult = await skill.limitSell({
  apiKey: GDEX_API_KEY_PRIMARY,
  userId: '0x53D029a671bd1CF61a2fB1F4F6e4bD830BFBb2eD',
  sessionPrivateKey: '<session-key-hex>',
  chainId: 622112261,
  tokenAddress: 'EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm',
  amount: '100000',
  triggerPrice: '999.99',
});

// Delete/cancel an order
await skill.updateOrder({
  apiKey: GDEX_API_KEY_PRIMARY,
  userId: '0x53D029a671bd1CF61a2fB1F4F6e4bD830BFBb2eD',
  sessionPrivateKey: '<session-key-hex>',
  chainId: 622112261,
  orderId: '<64-char-hex-order-id>',
  isDelete: true,
});

// List active orders (uses session-key auth, not full computedData)
const { count, orders } = await skill.getLimitOrders({
  userId: '0x53D029a671bd1CF61a2fB1F4F6e4bD830BFBb2eD',
  data: encryptedSessionKey,  // from buildGdexUserSessionData()
  chainId: 622112261,
});

Copy Trading

Solana-only for write operations. Sign-in must use chainId: 622112261. The ABI encodes chainId as uint256.

Discovery (no auth)

// Top 300 wallets by total PnL (cached 2 min)
const topWallets = await skill.getCopyTradeWallets();
// [{ address, totalPnl, receivedMinusSpent, spent, unrealizedValue, chainId }]

// Top 300 by net received
const customWallets = await skill.getCopyTradeCustomWallets();

// Hot new tokens from top wallets (cached 20s)
const gems = await skill.getCopyTradeGems();

// Supported DEXes for Solana
const dexes = await skill.getCopyTradeDexes(622112261);
// [{ dexName: 'pumpfun', dexNumber: 0, programId: '...' }, ...]

Read (session-key auth)

import { buildGdexUserSessionData } from '@gemachdao/gdex-skill';

const data = buildGdexUserSessionData(sessionKey, apiKey);

// List copy trade configs (cached 20s per user)
const { allCopyTrades, dexes } = await skill.getCopyTradeList({ userId, data });
// allCopyTrades: [{ copyTradeId, copyTradeName, traderWallet, isActive, lossPercent, profitPercent, ... }]

// Transaction history with PnL
const { txes } = await skill.getCopyTradeTxList({ userId, data });

Create (computedData auth)

import {
  GDEX_API_KEY_PRIMARY,
  buildCopyTradeComputedData,
  generateGdexSessionKeyPair,
  buildGdexSignInMessage,
  buildGdexSignInComputedData,
} from '@gemachdao/gdex-skill';

// Sign-in MUST use chainId: 622112261 for copy trade operations
const result = await skill.createCopyTrade({
  apiKey: GDEX_API_KEY_PRIMARY,
  userId: controlWalletAddress,
  sessionPrivateKey,
  chainId: 622112261,                // Solana only
  traderWallet: 'SolanaTraderAddress',
  copyTradeName: 'Alpha Trader',
  buyMode: 1,                        // 1 = fixed SOL, 2 = percentage
  copyBuyAmount: '0.001',            // SOL amount (mode 1) or percentage (mode 2)
  lossPercent: '50',                 // stop-loss at 50%
  profitPercent: '100',              // take-profit at 100%
  copySell: true,                    // also copy sell trades
  isBuyExistingToken: false,         // skip tokens already held
  excludedDexNumbers: [],            // no DEX exclusions
});
// { isSuccess: true, message: 'created new copy trade successfully', allCopyTrades: [...] }

Update / Pause / Resume / Delete (computedData auth)

const ids = { apiKey: GDEX_API_KEY_PRIMARY, userId: controlWalletAddress, sessionPrivateKey, copyTradeId: '<64-char-hex-id>' };

await skill.updateCopyTradeSettings({ ...ids, lossPercent: '40' }); // never deletes or pauses
await skill.pauseCopyTrade(ids);   // isActive=false; no-op if already paused
await skill.resumeCopyTrade(ids);  // isActive=true; no-op if already active
await skill.deleteCopyTrade(ids);  // permanent

Each reads the current config from /copy_trade/list first, because the backend rewrites every field on update and isChangeStatus is a relative toggle (isActive = !existing.isActive). Deleting removes the configuration only: positions it opened are not sold and their TP/SL limit orders are not cancelled. While paused the copier mirrors neither buys nor sells.

Copy Trade ABI Reference

Typed exactly as the backend decodes them (src/shared/parser/evm/decoder.ts):

Action Fields ABI Types
create_copy_trade 12 string, string, uint64, string, uint64, string, bool, string, string, string, bool, uint64[]
update_copy_trade 15 string, string, uint64, string, uint64, string, bool, string, string, string, bool, bool, string, bool, uint64[]

Create field order: [traderWallet, copyTradeName, chainId, gasPrice, buyMode, copyBuyAmount, isBuyExistingToken, lossPercent, profitPercent, nonce, copySell, excludedDexNumbers]

Update field order: [traderWallet, copyTradeName, chainId, gasPrice, buyMode, copyBuyAmount, isBuyExistingToken, lossPercent, profitPercent, nonce, isDelete, isChangeStatus, copyTradeId, copySell, excludedDexNumbers]

SDK versions up to 4.12 encoded every field as a string; the backend then decoded isDelete=true, so every update deleted. Nonce is auto-generated by the SDK.


HL Perp Copy Trading

Completely separate from Solana copy trading above. This copies perpetual futures positions (long/short) on HyperLiquid. Sign-in uses chainId: 1. All ABI fields are strings.

Discovery (no auth)

// Top traders by volume/tradeCount/deposit (cached 15 min)
const topByVolume = await skill.getHlTopTraders('volume');
const topByPnl = await skill.getHlTopTradersByPnl();

// Detailed trader stats (cached 1 hr)
// NOTE: Requires MANAGED wallet address, not control wallet
const stats = await skill.getHlUserStats('0xManagedWalletAddress');
// { userStats: { '24h', '7d', '30d', dailyPnls, volumes, tradesCount, allTime } }

// Market data
const dexes = await skill.getHlPerpDexes();
const assets = await skill.getHlAllAssets();
const tokens = await skill.getHlDepositTokens();

// Account state
const state = await skill.getHlClearinghouseState('0xAddress');
const orders = await skill.getHlOpenOrdersForCopy('0xAddress');
const balance = await skill.getHlUsdcBalanceForCopy('0xAddress');

Read (session-key auth)

import { buildGdexUserSessionData } from '@gemachdao/gdex-skill';

const data = buildGdexUserSessionData(sessionKey, apiKey);

// List HL copy trade configs
const { allCopyTrades } = await skill.getHlCopyTradeList({ userId, data });
// [{ copyTradeId, copyTradeName, copyMode, traderWallet, isActive, oppositeCopy, totalPnl, ... }]

// Fill history (cached 15s, max 100 per page)
const { txes, totalCount } = await skill.getHlCopyTradeTxList({ userId, data, page: '1', limit: '20' });
// [{ coin, px, sz, side, dir, closedPnl, copyTradeName, traderWallet, ... }]

Create (computedData auth)

await skill.createHlCopyTrade({
  apiKey,
  userId: controlWalletAddress,
  sessionPrivateKey,
  traderWallet: '0xTraderEvmAddress',
  copyTradeName: 'BTC Whale',
  copyMode: 1,                    // 1 = fixed USD, 2 = proportion
  fixedAmountCostPerOrder: '50000000', // copyMode 1: raw USDC, 6 decimals ($50 per copied trade)
  lossPercent: '25',              // mandatory, > 0 and < 100
  profitPercent: '100',           // mandatory, > 0
  oppositeCopy: false,            // true = copy opposite direction
});

Update / Pause / Resume / Delete (computedData auth)

const ids = { apiKey, userId: controlWalletAddress, sessionPrivateKey, copyTradeId: 'abc123...' };

await skill.updateHlCopyTradeSettings({ ...ids, copyMode: 2, fixedAmountCostPerOrder: '0.5', oppositeCopy: false });
await skill.pauseHlCopyTrade(ids);   // isActive=false; copied positions stay open and stop following the trader's exits
await skill.resumeHlCopyTrade(ids);
await skill.deleteHlCopyTrade(ids);  // permanent; does NOT close positions or cancel their TP/SL

HL Perp Copy Trade ABI Reference

Action Fields ABI Types
hl_create 8 string, string, uint64, string, string, string, string, bool
hl_update 11 string, string, uint64, string, string, string, string, bool, bool, string, bool

Create field order: [traderWallet, copyTradeName, copyMode, fixedAmountCostPerOrder, lossPercent, profitPercent, nonce, oppositeCopy]

Update field order: [traderWallet, copyTradeName, copyMode, fixedAmountCostPerOrder, lossPercent, profitPercent, nonce, isDelete, isChangeStatus, copyTradeId, oppositeCopy]

Configs written by SDK <= 4.12 (string-encoded ABI) hold copyMode 416/480 and oppositeCopy: true; repair them with updateHlCopyTradeSettings and explicit values. isChangeStatus toggles isActive; only isDelete deletes.


Portfolio

// Portfolio on one chain (managed-account read: also pass the session, see gdex-portfolio)
const portfolio = await skill.getPortfolio({ walletAddress: '0x...', chain: ChainId.BASE });
// { totalValueUsd, balances, perpPositions?, realizedPnl, unrealizedPnl }

// Chain-specific balances
const balances = await skill.getBalances({ walletAddress: '0x...', chain: ChainId.ETHEREUM });

// Paginated trade history
const history = await skill.getTradeHistory({
  walletAddress: '0x...', chain: ChainId.BASE, page: 1, limit: 20,
  startTime: 1700000000, endTime: 1700086400,
});

Token Information

πŸ”“ No authentication required for these endpoints.

// Token details
const token = await skill.getTokenDetails({
  tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  chain: 'solana',
});
// { symbol, name, priceUsd, priceChange24h, marketCap, fdv, volume24h, liquidity }

// Trending tokens
const trending = await skill.getTrendingTokens({
  chain: 'solana',
  period: '24h',     // '1h' | '6h' | '24h' | '7d'
  limit: 20,
  minLiquidity: 50000,
});

// OHLCV candles
const ohlcv = await skill.getOHLCV({
  tokenAddress: 'So11111111111111111111111111111111111111112',
  chain: 'solana',
  resolution: '60',  // '1'|'5'|'15'|'30'|'60'|'240'|'D'|'W'
  from: Math.floor(Date.now() / 1000) - 86400,
  to: Math.floor(Date.now() / 1000),
});

Top Traders

πŸ”“ No authentication required.

const traders = await skill.getTopTraders({
  chain: 'solana',
  period: '7d',    // '1d' | '7d' | '30d' | 'all'
  limit: 10,
  sortBy: 'pnl',   // 'pnl' | 'winRate' | 'volume' | 'tradeCount'
});
// [{ address, totalPnlUsd, winRate, tradeCount, totalVolumeUsd }]

Bridge

Native-token bridging (amounts in raw units: wei / lamports). For USDC see the Unified USDC Wallet.

// Get quote first (no auth)
const quote = await skill.estimateBridge({
  fromChainId: ChainId.ETHEREUM,
  toChainId: ChainId.BASE,
  amount: '100000000000000000', // 0.1 ETH in wei
});
console.log('Output:', quote.estimateAmount, 'β€” ETA (s):', quote.minEstimateTime, '-', quote.maxEstimateTime);

// Execute bridge (managed custody; sign in with chainId 1)
const result = await skill.requestBridge({
  fromChainId: ChainId.ETHEREUM,
  toChainId: ChainId.BASE,
  amount: '100000000000000000',
  userId: controlAddress,
  sessionPrivateKey,
  apiKey,
});
console.log(result.isSuccess, result.hash);

Wallet Info

// Managed-account read: control walletAddress plus the session (sessionPrivateKey + apiKey, or data)
const info = await skill.getWalletInfo({ walletAddress: controlWalletAddress, sessionPrivateKey, apiKey, chain: 'solana' });
// { address, nativeBalance, nativeSymbol, totalValueUsd, tokenCount }

Wallet Generation

πŸ”“ No authentication required. Keys are generated locally and never transmitted.

When a user doesn't have a wallet yet, generate an EVM control wallet for them. Then use the managed-custody sign-in flow to authenticate, and the Gbot backend automatically provisions a full trading wallet β€” including a Solana address and other chain-specific keys β€” server-side. No separate Solana wallet generation is needed.

generateEvmWallet() β€” your control wallet

Generates a new EVM-compatible wallet (Ethereum, Base, Arbitrum, BSC, etc.) using ethers.Wallet.createRandom().

import {
  GdexSkill,
  generateEvmWallet,
  generateGdexSessionKeyPair,
  buildGdexSignInMessage,
  GDEX_API_KEY_PRIMARY,
} from '@gemachdao/gdex-skill';

// Step 1: generate your EVM control wallet (one-time setup)
const wallet = generateEvmWallet();
console.log(wallet.address);  // '0xAbCd...' (checksummed) β€” safe to share
// ⚠️  Store wallet.privateKey and wallet.mnemonic securely

// Step 2: generate a session keypair for managed-custody trading
const { sessionPrivateKey, sessionKey } = generateGdexSessionKeyPair();

// Step 3: build the sign-in message and sign with your control wallet
const message = buildGdexSignInMessage(wallet.address, String(Date.now()), sessionKey);
// Sign with: new ethers.Wallet(wallet.privateKey).signMessage(message)

// Step 4: submit sign_in computedData (see Managed-Custody Trading section above)
// The backend provisions Solana + all other trading wallets server-side

🌐 Supported Chains

Chain ChainId Native Token DEXes
Ethereum 1 ETH Uniswap V2/V3, Odos
Optimism 10 ETH Uniswap V3, Odos
BNB Smart Chain 56 BNB PancakeSwap, Odos
Sonic 146 S β€”
Fraxtal 252 frxETH Uniswap V3
Nibiru 6900 NIBI β€”
Base 8453 ETH Uniswap V3, Odos, Arcadia
Arbitrum One 42161 ETH Uniswap V3, Odos
Berachain 80094 BERA β€”
Solana 622112261 SOL Raydium, Raydium V2, Orca
Sui 1313131213 SUI Cetus, Bluefin
HyperLiquid perps only USDC Native perp engine

The ChainId enum is wider than this table. It also defines Avalanche (43114), Polygon (137), zkSync Era (324), Linea (59144), Blast (81457) and Scroll (534352). The backend's supportedChainIds does not include them, so calls against those ids will not route. Trade only the chains listed above.

import { ChainId } from '@gemachdao/gdex-skill';

ChainId.ETHEREUM   // 1
ChainId.OPTIMISM   // 10
ChainId.BSC        // 56
ChainId.SONIC      // 146
ChainId.FRAXTAL    // 252
ChainId.NIBIRU     // 6900
ChainId.BASE       // 8453
ChainId.ARBITRUM   // 42161
ChainId.BERACHAIN  // 80094
ChainId.SOLANA     // 622112261
ChainId.SUI        // 1313131213

⚠️ Error Handling

import {
  GdexAuthError,       // 401/403 β€” re-authenticate
  GdexValidationError, // invalid input params
  GdexApiError,        // 4xx/5xx backend errors
  GdexNetworkError,    // connection failures, timeouts
  GdexRateLimitError,  // 429 β€” check err.retryAfter
} from '@gemachdao/gdex-skill';

try {
  await skill.buyToken({ ... });
} catch (err) {
  if (err instanceof GdexRateLimitError) {
    console.log(`Rate limited β€” retry after ${err.retryAfter}s`);
    await new Promise(r => setTimeout(r, err.retryAfter * 1000));
    // retry…
  } else if (err instanceof GdexAuthError) {
    skill.loginWithApiKey(GDEX_API_KEY_PRIMARY); // re-auth
  } else if (err instanceof GdexValidationError) {
    console.error(`Bad param "${err.field}": ${err.message}`);
  } else if (err instanceof GdexApiError) {
    console.error(`API ${err.statusCode}: ${err.message}`);
  } else if (err instanceof GdexNetworkError) {
    console.error(`Network (${err.code}): ${err.message}`);
  }
}
Class When thrown
GdexAuthError 401/403, invalid credentials
GdexValidationError Invalid address, amount, chain, slippage
GdexApiError Non-success HTTP (4xx/5xx)
GdexNetworkError Connection refused, ECONNABORTED, timeout
GdexRateLimitError HTTP 429 (has .retryAfter in seconds)

πŸ›  Utility Functions

import {
  getChainName,         // getChainName(8453)        β†’ "Base"
  getNativeToken,       // getNativeToken('solana')   β†’ "SOL"
  formatTokenAmount,    // formatTokenAmount('1000000', 6, 'USDC') β†’ "1 USDC"
  formatUsd,            // formatUsd('1234.5')         β†’ "$1,234.50"
  formatPercentChange,  // formatPercentChange('5.23') β†’ "+5.23%"
  shortenAddress,       // shortenAddress('0x1234...') β†’ "0x1234...5678"
  validateAddress,      // throws GdexValidationError if invalid
  validateAmount,       // throws GdexValidationError if invalid
  validateChain,        // throws GdexValidationError if unsupported
} from '@gemachdao/gdex-skill';

πŸ§ͺ Testing

All 103 tests run with mocked HTTP β€” no real API key or network connection required:

npm test              # run all 103 tests
npm run test:coverage # with coverage report
npm run verify        # offline SDK smoke-test (20 checks)
npm run verify:managed # managed-custody payload validation (dry-run)

Test suites:

  • tests/client/auth.test.ts β€” API key auth, EVM/Solana wallet signing
  • tests/actions/spotTrade.test.ts β€” buy/sell, slippage, validation
  • tests/actions/perpTrade.test.ts β€” open/close positions, leverage, deposits
  • tests/actions/portfolio.test.ts β€” balances, history, wallet info
  • tests/actions/tokenInfo.test.ts β€” trending, OHLCV, token details, top traders
  • tests/utils/walletGeneration.test.ts β€” EVM control wallet generation (offline)
  • tests/utils/gdexManagedCrypto.test.ts β€” managed-custody crypto helpers (AES, signing, ABI encoding)

πŸ— Architecture

AI Agent (Claude Code / Cursor / Codex / ...)
   β”‚
   β”‚  npx skills add GemachDAO/gdex-skill
   β”‚  ──────────────────────────────────
   β”‚  SKILL.md β†’ agent skill directory
   β”‚
   β–Ό
@gemachdao/gdex-skill  (this package)
   β”‚  TypeScript methods with full type safety
   β”‚  @gdexsdk/hyper-liquid-trader for HyperLiquid L1 queries & direct execution
   β”‚  Managed-custody: AES-256-CBC encryption + secp256k1 session signing
   β”‚  computedData payloads for all trade operations
   β”‚  Auto-retry with exponential backoff
   β”‚
   β”‚  Control Wallet (EVM / Solana)
   β”‚  └─ signs once for /v1/sign_in β†’ session keypair
   β”‚
   β–Ό
Gbot Backend API  (https://trade-api.gemach.io/v1)
   β”‚  Decrypt computedData β†’ verify signature β†’ resolve nonce (gRPC)
   β”‚  NATS JetStream trade queue
   β”‚  Server-side managed wallets (custody)
   β”‚  DEX aggregation engine
   β–Ό
Blockchains  (Solana Β· Sui Β· Ethereum Β· Base Β· Arbitrum Β· …)

HL Managed-Custody Reference

HyperLiquid perp operations use a distinct crypto pipeline from spot trades. Getting any detail wrong produces a 400 Unauthorized (code 103) error. This section documents the exact specification.

HL Deposit Flow

The backend custodially executes the on-chain deposit: it loads the user's server-side private key, constructs an Arbitrum transaction, and sends USDC to the HyperLiquid bridge receiver. The agent only provides an authorization signature.

Agent SDK                              Backend
────────                               ───────
1. ABI-encode deposit params     ──►   2. AES-decrypt computedData
   (uint64 chainId, address,           3. ABI-decode data
    uint256 amount, string nonce)       4. Verify chainId == 42161
2. Sign with session key          ──►  5. Verify signature vs stored sessionKey
3. AES-encrypt as computedData    ──►  6. Validate token, balance, min deposit
4. POST /v1/hl/deposit                 7. Execute ERC-20 transfer on Arbitrum

HL ABI Schemas (CRITICAL)

Action ABI Types Fields
hl_deposit ['uint64', 'address', 'uint256', 'string'] [chainId, tokenAddress, amount, nonce]
hl_withdraw ['string', 'string'] [amount, nonce]
hl_create_order ['string', 'bool', 'string', 'string', 'bool', 'string', 'string', 'string', 'bool'] [coin, isLong, price, size, reduceOnly, nonce, tpPrice, slPrice, isMarket]
hl_place_order ['string', 'bool', 'string', 'string', 'bool', 'string'] [coin, isLong, price, size, reduceOnly, nonce]
hl_close_all ['string'] [nonce]
hl_cancel_order ['string', 'string', 'string'] [nonce, coin, orderId]
hl_cancel_all_orders ['string'] [nonce]
hl_update_leverage ['string', 'uint32', 'bool', 'string'] [coin, leverage, isCross, nonce]

WARNING: The hl_deposit chainId uses uint64, NOT uint256. This is the single most common cause of "Unauthorized" errors. The backend re-encodes with uint64 for signature verification β€” if you encode with uint256, the hex differs, signature recovery fails, and you get code 103.

HL Signature Format

All HL write operations sign with the session private key (from sign-in), NOT the control wallet key:

// Message format (no EIP-191 prefix):
const msg = `${action}-${userId.toLowerCase()}-${dataHex}`;
// e.g.: "hl_deposit-0x53d029a6...-00000000000000000000000000000000000000000000000000000000..."

const digest = keccak256(toUtf8Bytes(msg));
const sig = new SigningKey(sessionPrivateKey).sign(digest);
// Output: r(64hex) + s(64hex) + v(2hex) = 130 chars, no 0x prefix
// v = raw recovery parameter (00 or 01), NOT EIP-155 (1b/1c)

HL Deposit Constraints

Constraint Value
Chain Arbitrum only (chainId 42161)
Token USDC only (0xaf88d065e77c8cC2239327C5EDb3A432268e5831)
Amount In smallest unit (6 decimals): 10 USDC = 10000000
Min deposit 10 USDC
Fee buffer Balance must cover amount Γ— 1.01
Delivery time ~10 minutes after Arbitrum tx confirms
Bridge receiver 0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7
userId Control wallet address (from sign-in), NOT managed wallet

HL Error Codes

Code Error Cause
101 Missing params computedData not in request body
102 Invalid chainId chainId is not 42161
102 Invalid params Nonce already used, or token not supported
103 Unauthorized Signature verification failed β€” check ABI types (uint64!), userId, and that you're signing with the session key registered during sign-in
β€” Insufficient balance Managed wallet doesn't have enough USDC + fee on Arbitrum
β€” Too low amount Amount < 10 USDC

HTTP Headers (Required)

The backend sits behind Cloudflare. Requests MUST include browser-like headers:

{
  'Content-Type': 'application/json',
  'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
  'Accept': 'application/json',
  'Accept-Language': 'en-US,en;q=0.9',
  'Accept-Encoding': 'gzip, deflate, br',
  'Connection': 'keep-alive',
  'Authorization': 'Bearer <apiKey>',  // NOT X-API-Key
}

WARNING: Using a non-browser User-Agent (e.g., axios/1.x or any string containing bot identifiers) will result in Cloudflare 403 "Access denied". The SDK handles this automatically.

Complete HL Deposit Example

import {
  GdexSkill,
  GDEX_API_KEY_PRIMARY,
  generateGdexSessionKeyPair,
  buildGdexSignInMessage,
  buildGdexSignInComputedData,
  buildHlComputedData,
} from '@gemachdao/gdex-skill';
import { ethers } from 'ethers';

const apiKey = GDEX_API_KEY_PRIMARY;
const skill = new GdexSkill();
skill.loginWithApiKey(apiKey);

// 1. Generate session keypair
const { sessionPrivateKey, sessionKey } = generateGdexSessionKeyPair();

// 2. Sign in (control wallet signs the Terms message)
const controlWallet = new ethers.Wallet('0xYourPrivateKey');
const nonce = String(Date.now());
const message = buildGdexSignInMessage(controlWallet.address, nonce, sessionKey);
const signature = await controlWallet.signMessage(message);

const signInPayload = buildGdexSignInComputedData({
  apiKey, userId: controlWallet.address, sessionKey, nonce,
  signature: signature.replace(/^0x/, ''),
});
await skill.signInWithComputedData({
  computedData: signInPayload.computedData, chainId: 42161,
});

// 3. Deposit USDC (amount in smallest unit, uint64 chainId is handled by SDK)
const computedData = buildHlComputedData({
  action: 'hl_deposit',
  apiKey,
  walletAddress: controlWallet.address,  // userId = control wallet
  sessionPrivateKey,
  actionParams: {
    chainId: 42161,
    tokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
    amount: '10000000',  // 10 USDC in smallest unit (6 decimals)
  },
});

const result = await skill.client.post('/v1/hl/deposit', { computedData });
// { hash: '0x...', isSuccess: true, amount: '10000000', message: 'Deposit successfully...' }
// Wait ~10 minutes for HyperLiquid to credit the deposit

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Run npm test && npm run build && npm run verify
  5. Submit a pull request

License

MIT Β© GemachDAO

About

Official AI agent skills, TypeScript SDK and MCP server (117 tools) for GDEX (gdex.pro), Gemach DAO's multi-chain trading terminal: Solana and EVM spot, Hyperliquid perps, copy trading

Topics

Resources

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages