GDEX (gdex.pro) trading for AI agents: spot, perps and HyperLiquid on Solana, EVM and more chains
██████╗ ██████╗ ███████╗██╗ ██╗ ██████╗ ██████╗ ██████╗
██╔════╝ ██╔══██╗██╔════╝╚██╗██╔╝ ██╔══██╗██╔══██╗██╔═══██╗
██║ ███╗██║ ██║█████╗ ╚███╔╝ ██████╔╝██████╔╝██║ ██║
██║ ██║██║ ██║██╔══╝ ██╔██╗ ██╔═══╝ ██╔══██╗██║ ██║
╚██████╔╝██████╔╝███████╗██╔╝ ██╗ ██║ ██║ ██║╚██████╔╝
╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
· 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
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.
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.
| Skill | Description |
|---|---|
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 outcome (event) markets — list, order, manage positions |
gdex-hl-referral | HyperLiquid referral info and reward claims |
gdex-bridge | Cross-chain bridging with quotes |
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).
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
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.
The GDEX MCP server exposes 117 tools — full trading execution + SDK documentation — as Model Context Protocol tools. Any MCP-compatible AI agent can trade autonomously.
# 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/mcp.json
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"
}
}
}
}
| 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 |
| Category | Tools | Description |
|---|---|---|
| Auth | 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, Ethereum, Base, Arbitrum, BSC, and 10+ chains |
| 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 |
| 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_create_outcome_order, hl_cancel_outcome_order, hl_close_outcome_order | HyperLiquid outcome (event) 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 |
| 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 |
npm install github:GemachDAO/gdex-skill
Installs the SDK straight from GitHub (it builds on install). Imports stay
from '@gdexsdk/gdex-skill'. Pin a release withnpm 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
import { GdexSkill, GDEX_API_KEY_PRIMARY } from '@gdexsdk/gdex-skill';
// 1. Create skill instance
const skill = new GdexSkill();
// 2. Authenticate with pre-configured shared key — no wallet needed
skill.loginWithApiKey(GDEX_API_KEY_PRIMARY);
// 3. Spot buy on Solana
const trade = await skill.buyToken({
chain: 'solana',
tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: '0.1', // 0.1 SOL
slippage: 1, // 1% max slippage
});
console.log('Trade submitted:', trade.jobId, '— status:', trade.status);
// 4. Open a BTC 10× long on HyperLiquid
const pos = await skill.openPerpPosition({
coin: 'BTC',
side: 'long',
sizeUsd: '1000',
leverage: 10,
takeProfitPrice: '110000',
stopLossPrice: '95000',
});
// 5. 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(', '));
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.
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 '@gdexsdk/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.
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]] }),
});
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 | — |
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.
computedData → POST /v1/sign_in/v1/user with encrypted session key to see managed walletscomputedData → POST /v1/purchase_v2 or /v1/sell_v2/v1/trade-status/:requestId until completed/failedAll authenticated payloads use AES-256-CBC with a deterministic key/IV derived from the API key (no random IV):
SHA256(apiKey) hexSHA256(SHA256(apiKey)) hexJSON.stringify({ userId, data, signature, apiKey }) → UTF-8 → encrypt → hex/v1/user): hex-decoded raw bytes → encrypt (not UTF-8 string)WARNING: Do NOT use random IVs or the
iv:ciphertextformat. The backend uses deterministic AES derived from the API key hash chain.
Spot trade signatures (purchase/sell) use raw keccak256 + secp256k1 (no EIP-191 prefix):
"<action>-<lowercaseUserId>-<dataHexWithout0x>"keccak256(utf8Bytes(message))r(64 hex) + s(64 hex) + v(2 hex) = 130 chars, no 0x prefix00 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.
Nonces are client-generated (not fetched from the server):
const nonce = String(Math.floor(Date.now() / 1000) + Math.floor(Math.random() * 1000));
import {
GdexSkill,
GDEX_API_KEY_PRIMARY,
generateGdexSessionKeyPair,
buildGdexSignInMessage,
buildGdexSignInComputedData,
buildGdexManagedTradeComputedData,
buildGdexUserSessionData,
} from '@gdexsdk/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: 900 });
// 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: 900, slippage: 1,
});
// 4. Poll
if (result.requestId) {
const status = await skill.getManagedTradeStatus(result.requestId);
console.log(status.status, status.hash);
}
| 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 |
encodeCopyTradeData(action, params) | ABI-encode copy trade data (create: 12 fields, update: 16 fields, chainId is uint256) |
signCopyTradeMessage(action, userId, data, privKey) | Sign copy trade with session key |
buildCopyTradeComputedData({...}) | Build encrypted copy trade payload |
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 |
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.
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%' });
Critical: HyperLiquid deposits/withdrawals/orders use a different crypto flow than spot trades. See the HL Managed-Custody Reference section below for exact specifications.
openPerpPosition(params)| Parameter | Type | Default | Description |
|---|---|---|---|
coin | string | Asset symbol (e.g., 'BTC', 'ETH') | |
side | 'long' | 'short' | Direction | |
sizeUsd | string | Collateral in USD | |
leverage | number | 5 | 1–50× |
takeProfitPrice | string | Optional TP price | |
stopLossPrice | string | Optional SL price | |
marginMode | 'cross' | 'isolated' | 'cross' | Margin mode |
const pos = await skill.openPerpPosition({
coin: 'BTC', side: 'long', sizeUsd: '1000', leverage: 10,
takeProfitPrice: '110000', stopLossPrice: '95000',
});
closePerpPosition(params)await skill.closePerpPosition({ coin: 'BTC' }); // close 100%
await skill.closePerpPosition({ coin: 'ETH', closePercent: 50 }); // close 50%
await skill.setPerpLeverage({ coin: 'BTC', leverage: 20 });
// Update leverage via HL managed-custody (explicit session-key signing)
await skill.hlUpdateLeverage({
coin: 'BTC',
leverage: 40,
isCross: true, // true = cross margin, false = isolated (default: true)
apiKey,
walletAddress,
sessionPrivateKey,
});
// Deposit USDC to HyperLiquid (human-readable amount, converted internally)
await skill.perpDeposit({ amount: '10' }); // deposit 10 USDC (minimum)
await skill.perpWithdraw({ amount: '5' }); // withdraw 5 USDC
const positions = await skill.getPerpPositions({ walletAddress: '0x...' });
// 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.
@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']);
Endpoints:
limit_buy/limit_sell/update_order/orders— NOTorders/createororders/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,
});
Solana-only for write operations. Sign-in must use
chainId: 622112261. The ABI encodeschainIdasuint256.
// 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: '...' }, ...]
import { buildGdexUserSessionData } from '@gdexsdk/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 });
import {
GDEX_API_KEY_PRIMARY,
buildCopyTradeComputedData,
generateGdexSessionKeyPair,
buildGdexSignInMessage,
buildGdexSignInComputedData,
} from '@gdexsdk/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: [...] }
// Delete a copy trade (isDelete: true)
const deleteResult = await skill.updateCopyTrade({
apiKey: GDEX_API_KEY_PRIMARY,
userId: controlWalletAddress,
sessionPrivateKey,
chainId: 622112261,
copyTradeId: '<64-char-hex-id>',
traderWallet: 'SolanaTraderAddress',
copyTradeName: 'Alpha Trader',
buyMode: 1,
copyBuyAmount: '0.001',
lossPercent: '50',
profitPercent: '100',
isDelete: true, // permanently deletes the copy trade
});
// { isSuccess: true, message: 'Updated' }
WARNING: Both
isDelete: trueandisChangeStatus: truepermanently delete the copy trade. There is no toggle/pause functionality on the current backend. Boolean fields use''for false and'1'for true internally; string'0'is truthy in JS and will trigger deletion.
| Action | Fields | ABI Types |
|---|---|---|
create_copy_trade | 12 | ['string','string','uint256','string'×9] |
update_copy_trade | 16 | ['string','string','uint256','string'×13] |
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, copySell, excludedDexNumbers, copyTradeId, isDelete, isChangeStatus, excludedProgramIds]
chainIdat position 2 isuint256, all other fields arestring. Nonce is auto-generated by the SDK.
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.
// 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');
import { buildGdexUserSessionData } from '@gdexsdk/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, ... }]
await skill.createHlCopyTrade({
apiKey,
userId: controlWalletAddress,
sessionPrivateKey,
traderWallet: '0xTraderEvmAddress',
copyTradeName: 'BTC Whale',
copyMode: 1, // 1 = fixed USD, 2 = proportion
fixedAmountCostPerOrder: '50', // $50 per copied trade
lossPercent: '25', // mandatory, > 0 and < 100
profitPercent: '100', // mandatory, > 0
oppositeCopy: false, // true = copy opposite direction
});
// Update parameters
await skill.updateHlCopyTrade({
apiKey, userId: controlWalletAddress, sessionPrivateKey,
copyTradeId: 'abc123...',
traderWallet: '0xTraderEvmAddress',
copyTradeName: 'Updated Name',
copyMode: 2, fixedAmountCostPerOrder: '0.5',
lossPercent: '30', profitPercent: '150',
oppositeCopy: true,
});
// Delete permanently (use isDelete)
await skill.updateHlCopyTrade({ ...existingParams, isDelete: true });
// WARNING: isChangeStatus also PERMANENTLY DELETES (does NOT toggle)
await skill.updateHlCopyTrade({ ...existingParams, isChangeStatus: true });
| Action | Fields | ABI Types |
|---|---|---|
hl_create | 8 | ['string' × 8] |
hl_update | 11 | ['string' × 11] |
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]
All fields are
string. Boolean fields:'1'= true,''= false. Nonce auto-generated by SDK. Note:copyModeandoppositeCopyin responses contain ABI byte-offsets (e.g., 416), not actual values. BothisDeleteandisChangeStatuspermanently delete the trade.
// Full cross-chain portfolio
const portfolio = await skill.getPortfolio({ walletAddress: '0x...' });
// { 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...', page: 1, limit: 20,
startTime: 1700000000, endTime: 1700086400,
});
🔓 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),
});
🔓 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 }]
// Get quote first
const quote = await skill.getBridgeQuote({
fromChain: 'solana',
toChain: ChainId.ETHEREUM,
tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
amount: '100',
});
console.log('Output:', quote.outputAmount, '— fee (USD):', quote.feeUsd);
// Execute bridge
const result = await skill.bridge({
fromChain: 'solana',
toChain: ChainId.ETHEREUM,
tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
amount: '100',
destinationAddress: '0xYourEthAddress',
slippage: 0.5,
});
const info = await skill.getWalletInfo({ walletAddress: '...', chain: 'solana' });
// { address, nativeBalance, nativeSymbol, totalValueUsd, tokenCount }
🔓 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 walletGenerates a new EVM-compatible wallet (Ethereum, Base, Arbitrum, BSC, etc.) using ethers.Wallet.createRandom().
import {
GdexSkill,
generateEvmWallet,
generateGdexSessionKeyPair,
buildGdexSignInMessage,
GDEX_API_KEY_PRIMARY,
} from '@gdexsdk/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
| 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
ChainIdenum is wider than this table. It also defines Avalanche (43114), Polygon (137), zkSync Era (324), Linea (59144), Blast (81457) and Scroll (534352). The backend'ssupportedChainIdsdoes not include them, so calls against those ids will not route. Trade only the chains listed above.
import { ChainId } from '@gdexsdk/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
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 '@gdexsdk/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) |
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 '@gdexsdk/gdex-skill';
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 signingtests/actions/spotTrade.test.ts — buy/sell, slippage, validationtests/actions/perpTrade.test.ts — open/close positions, leverage, depositstests/actions/portfolio.test.ts — balances, history, wallet infotests/actions/tokenInfo.test.ts — trending, OHLCV, token details, top traderstests/utils/walletGeneration.test.ts — EVM control wallet generation (offline)tests/utils/gdexManagedCrypto.test.ts — managed-custody crypto helpers (AES, signing, ABI encoding)AI Agent (Claude Code / Cursor / Codex / ...)
│
│ npx skills add GemachDAO/gdex-skill
│ ──────────────────────────────────
│ SKILL.md → agent skill directory
│
▼
@gdexsdk/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 · …)
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.
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
| 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_depositchainId usesuint64, NOTuint256. This is the single most common cause of "Unauthorized" errors. The backend re-encodes withuint64for signature verification — if you encode withuint256, the hex differs, signature recovery fails, and you get code 103.
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 0
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @gemachdao/gdex-mcp-serverMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-gemachdao-gdex-mcp-server": {
"command": "npx",
"args": [
"-y",
"@gemachdao/gdex-mcp-server"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceGDEX Trading works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.