#!/usr/bin/env node
/**
* Test script to verify Derive API credentials work properly
* Tests both public and private endpoints
*/
import dotenv from 'dotenv';
import axios from 'axios';
import { Wallet } from 'ethers';
// Load .env file
dotenv.config();
const DERIVE_ENVIRONMENT = process.env.DERIVE_ENVIRONMENT || "mainnet";
const DERIVE_WALLET = process.env.DERIVE_WALLET || "";
const DERIVE_PRIVATE_KEY = process.env.DERIVE_PRIVATE_KEY || "";
const API_BASES = {
mainnet: "https://api.lyra.finance",
testnet: "https://api-demo.lyra.finance",
};
const BASE_API_URL = API_BASES[DERIVE_ENVIRONMENT];
console.log("π Derive API Credential Verification\n");
console.log("β".repeat(50));
// Check .env file
console.log("\nπ Configuration Check:");
console.log(` Environment: ${DERIVE_ENVIRONMENT}`);
console.log(` API URL: ${BASE_API_URL}`);
console.log(` Wallet: ${DERIVE_WALLET ? "β
Set" : "β Missing"}`);
console.log(` Private Key: ${DERIVE_PRIVATE_KEY ? "β
Set" : "β οΈ Not set (optional)"}`);
if (!DERIVE_WALLET) {
console.error("\nβ ERROR: DERIVE_WALLET not set in .env");
process.exit(1);
}
// Validate wallet format
if (!DERIVE_WALLET.match(/^0x[a-fA-F0-9]{40}$/)) {
console.error(`\nβ ERROR: Invalid wallet address format: ${DERIVE_WALLET}`);
console.error(" Expected format: 0x followed by 40 hex characters");
process.exit(1);
}
console.log("\nβ
Configuration looks good!");
console.log("\nβ".repeat(50));
// Test 1: Public endpoint (no auth needed)
async function testPublicEndpoint() {
console.log("\nπ Test 1: Public Endpoint (get_currencies)");
console.log(" Testing: public/get_all_currencies");
try {
const response = await axios.post(BASE_API_URL + "/public/get_all_currencies", {}, {
headers: { "Content-Type": "application/json" },
timeout: 10000,
});
if (response.data.error) {
console.error(` β API Error: ${JSON.stringify(response.data.error)}`);
return false;
}
const currencies = response.data.result || response.data || [];
console.log(` β
Success! Found ${currencies.length} currencies`);
if (currencies.length > 0) {
const currencyNames = currencies.slice(0, 3).map(c => c.currency || c);
console.log(` Sample: ${currencyNames.join(", ")}`);
}
return true;
} catch (error) {
console.error(` β Connection Error: ${error.message}`);
if (error.response?.status === 404) {
console.error(" API endpoint not reachable. Check DERIVE_ENVIRONMENT setting.");
}
return false;
}
}
// Test 2: Private endpoint (requires auth)
async function testPrivateEndpoint() {
console.log("\nπ Test 2: Private Endpoint (get_account)");
console.log(` Testing: private/get_account for wallet ${DERIVE_WALLET.slice(0, 6)}...`);
const timestamp = Date.now().toString();
let signature = "";
if (DERIVE_PRIVATE_KEY) {
try {
// Use Ethereum wallet signing (EIP-191 personal_sign)
const wallet = new Wallet(DERIVE_PRIVATE_KEY);
signature = await wallet.signMessage(timestamp);
console.log(` Signing wallet address: ${wallet.address}`);
} catch (error) {
console.warn(` β οΈ Could not create signature: ${error.message}`);
}
}
const headers = {
"Content-Type": "application/json",
"X-LyraWallet": DERIVE_WALLET,
"X-LyraTimestamp": timestamp,
};
if (signature) {
headers["X-LyraSignature"] = signature;
}
const requestBody = {
wallet: DERIVE_WALLET,
};
try {
const response = await axios.post(BASE_API_URL + "/private/get_account", requestBody, {
headers,
timeout: 10000,
});
if (response.data.error) {
const errorMsg = response.data.error.message || JSON.stringify(response.data.error);
console.error(` β API Error: ${errorMsg}`);
if (errorMsg.includes("auth") || errorMsg.includes("signature")) {
console.log(" π‘ Tip: Authentication failed. Check your wallet address.");
}
return false;
}
const account = response.data.result || response.data;
console.log(` β
Success! Account accessed`);
console.log(` Subaccounts: ${account.subaccount_ids?.length || 0}`);
if (account.subaccount_ids && account.subaccount_ids.length > 0) {
console.log(` First subaccount ID: ${account.subaccount_ids[0]}`);
}
return true;
} catch (error) {
if (error.response?.status === 401 || error.response?.status === 403) {
console.error(` β Authentication Failed (${error.response.status})`);
console.log(" π‘ Possible causes:");
console.log(" β’ DERIVE_WALLET not registered on this environment");
console.log(" β’ Session key not registered properly");
console.log(" β’ Using wrong environment (mainnet vs testnet)");
return false;
} else {
console.error(` β Connection Error: ${error.message}`);
return false;
}
}
}
// Test 3: Check if wallet has subaccounts
async function testSubaccounts() {
console.log("\nπ Test 3: List Subaccounts");
const timestamp = Date.now().toString();
let signature = "";
if (DERIVE_PRIVATE_KEY) {
try {
// Use Ethereum wallet signing (EIP-191 personal_sign)
const wallet = new Wallet(DERIVE_PRIVATE_KEY);
signature = await wallet.signMessage(timestamp);
} catch (error) {
// Silent fail
}
}
const headers = {
"Content-Type": "application/json",
"X-LyraWallet": DERIVE_WALLET,
"X-LyraTimestamp": timestamp,
};
if (signature) {
headers["X-LyraSignature"] = signature;
}
const requestBody = {
wallet: DERIVE_WALLET,
};
try {
const response = await axios.post(BASE_API_URL + "/private/get_subaccounts", requestBody, {
headers,
timeout: 10000,
});
if (response.data.error) {
console.error(` β οΈ Could not fetch subaccounts: ${response.data.error.message}`);
return null;
}
const subaccounts = response.data.result || response.data || [];
console.log(` β
Found ${subaccounts.length} subaccount(s)`);
if (subaccounts.length > 0) {
subaccounts.forEach((sub, idx) => {
console.log(` ${idx + 1}. ID: ${sub.subaccount_id} (Label: "${sub.label || "default"}")`);
});
} else {
console.log(" π‘ No subaccounts yet. Create one in the Derive app to start trading.");
}
return subaccounts;
} catch (error) {
console.error(` β Error: ${error.message}`);
return null;
}
}
// Main test sequence
async function runTests() {
const publicOk = await testPublicEndpoint();
if (!publicOk) {
console.log("\nβ οΈ Public endpoint failed. Cannot continue testing.");
console.log(" Check your network connection and DERIVE_ENVIRONMENT setting.");
process.exit(1);
}
const privateOk = await testPrivateEndpoint();
if (privateOk) {
await testSubaccounts();
}
// Summary
console.log("\n" + "β".repeat(50));
console.log("\nπ Summary:");
if (publicOk && privateOk) {
console.log(" β
Public API: Working");
console.log(" β
Private API: Working");
console.log(" β
Credentials: Valid\n");
console.log("π Everything is set up correctly!");
console.log("\nYou can now use the Derive MCP server for:");
console.log(" β’ Reading market data (no auth needed)");
console.log(" β’ Accessing your account and positions");
console.log(" β’ Placing and managing orders");
console.log("\nπ Next steps:");
console.log(" 1. Test with Claude: ask it to show market data");
console.log(" 2. Connect to Claude Desktop/Claude Code CLI");
console.log(" 3. Use natural language to trade and analyze markets");
process.exit(0);
} else if (publicOk) {
console.log(" β
Public API: Working");
console.log(" β Private API: Not working\n");
console.log("β οΈ You can read public market data, but cannot access your account.");
console.log("\nπ‘ To fix private API access:");
console.log(" 1. Verify DERIVE_WALLET is correct (same address you use to sign in)");
console.log(" 2. Check you're on the correct environment (DERIVE_ENVIRONMENT)");
console.log(" 3. Create/register session keys in the Derive platform");
console.log(" 4. Wait for transactions to confirm");
process.exit(1);
} else {
console.log(" β API Unreachable\n");
console.log("Cannot connect to Derive API. Check:");
console.log(" β’ Network connectivity");
console.log(" β’ DERIVE_ENVIRONMENT is 'mainnet' or 'testnet'");
console.log(" β’ API status: https://status.lyra.finance");
process.exit(1);
}
}
runTests().catch((error) => {
console.error("\nβ Unexpected error:", error.message);
process.exit(1);
});