Authentication Troubleshooting
This guide helps you diagnose and resolve common authentication issues when using the Synthetix API.
Quick Diagnosis
Authentication Checklist
Before diving into specific errors, verify these basics:
- Private key is valid and has proper format
- Domain separator matches exactly
- Nonce is increasing and hasn't been used
- Signature components are properly formatted
- Network connectivity to the API endpoints
- System time is synchronized (for nonce generation)
Common Error Messages
1. "Invalid signature"
Symptoms:{
"status": "error",
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid signature",
"details": {}
},
"request_id": "5ccf215d37e3ae6d",
"timestamp": "2025-01-01T00:00:00Z"
}Wrong Domain Separator
// Wrong
const domain = {
name: "SynthetixOffchain", // Wrong name
version: "1",
chainId: 1,
verifyingContract: "0x1234567890123456789012345678901234567890" // Wrong address
};
// Correct
const domain = {
name: "Synthetix", // Exact match required
version: "1",
chainId: 1,
verifyingContract: "0x0000000000000000000000000000000000000000" // Zero address for off-chain
};Incorrect Message Structure
// Wrong - order type as object
const message = {
orders: [{
symbol: "BTC-USDT",
side: "buy",
quantity: "0.1",
price: "50000",
orderType: "limitGtc", // String enum format
triggerPrice: "",
isTriggerMarket: false,
reduceOnly: false
}],
nonce,
expiresAfter
};
// Correct - order type as string
const message = {
orders: [{
symbol: "BTC-USDT",
side: "buy",
quantity: "0.1",
price: "50000",
orderType: "limitGtc", // Direct string enum
triggerPrice: "",
isTriggerMarket: false,
reduceOnly: false
}],
nonce,
expiresAfter
};Chain ID Mismatch
// Check your target network
const chainIds = {
mainnet: 1,
};
// Domain chainId must match your target
const domain = {
name: "Synthetix",
version: "1",
chainId: 1, // Must match your environment
verifyingContract: "0x0000000000000000000000000000000000000000"
};-
Verify Domain:
console.log('Domain used:', JSON.stringify(domain, null, 2)); -
Check Message Structure:
console.log('Message structure:', JSON.stringify(message, null, 2)); -
Validate Signature Components:
function validateSignature(sig) { console.log('Signature v:', sig.v, '(should be 27 or 28)'); console.log('Signature r length:', sig.r.length, '(should be 66)'); console.log('Signature s length:', sig.s.length, '(should be 66)'); console.log('Signature r format:', /^0x[0-9a-fA-F]{64}$/.test(sig.r)); console.log('Signature s format:', /^0x[0-9a-fA-F]{64}$/.test(sig.s)); } -
Test Signature Recovery:
import { ethers } from 'ethers'; // Verify signature locally const typedData = { domain, types, message }; const digest = ethers.utils._TypedDataEncoder.hash(domain, types, message); const recoveredAddress = ethers.utils.recoverAddress(digest, signature); console.log('Expected address:', wallet.address); console.log('Recovered address:', recoveredAddress); console.log('Match:', recoveredAddress.toLowerCase() === wallet.address.toLowerCase());
2. "Nonce already used"
Symptoms:{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Nonce already used",
"details": {
"lastNonce": 1704067200000,
"attemptedNonce": 1704067199000
}
},
"request_id": "5ccf215d37e3ae6d",
"timestamp": "2025-01-01T00:00:00Z"
}Concurrent Requests
// Use proper nonce management for concurrent requests
class ThreadSafeNonceManager {
constructor() {
this.lastNonce = 0;
this.lock = false;
}
async generateNonce() {
// Simple lock mechanism
while (this.lock) {
await new Promise(resolve => setTimeout(resolve, 10));
}
this.lock = true;
const timestamp = Date.now();
this.lastNonce = Math.max(timestamp, this.lastNonce + 1);
const nonce = this.lastNonce;
this.lock = false;
return nonce;
}
}Multiple Systems with Same Key
// Use distributed nonce strategy
class DistributedNonceManager {
constructor(systemId, totalSystems) {
this.systemId = systemId;
this.totalSystems = totalSystems;
}
generateNonce() {
const baseTime = Date.now();
// Each system gets different remainder when divided by total systems
return baseTime * this.totalSystems + this.systemId;
}
}
// System 0: nonces end in 0, 3, 6, 9...
// System 1: nonces end in 1, 4, 7...
// System 2: nonces end in 2, 5, 8...3. "Insufficient margin"
Symptoms:{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Insufficient margin",
"details": {
"requiredMargin": "5000.00",
"availableMargin": "1000.00"
}
},
"request_id": "5ccf215d37e3ae6d",
"timestamp": "2025-01-01T00:00:00Z"
}Check Account Balance First
async function checkMarginBeforeOrder(client, order) {
// Get account info (implement based on your client)
const accountInfo = await client.getAccountInfo();
const availableMargin = parseFloat(accountInfo.availableMargin);
// Estimate required margin for order
const orderValue = parseFloat(order.price) * parseFloat(order.quantity);
const estimatedMargin = orderValue * 0.1; // Rough 10x leverage estimate
if (availableMargin < estimatedMargin) {
throw new Error(`Insufficient margin: need ${estimatedMargin}, have ${availableMargin}`);
}
return true;
}Self-Help Resources
- Authentication Overview - Understand the basics
- EIP-712 Signing - Implementation details
- Examples - Working code samples
- Error Handling - Comprehensive error reference
Community Support
- Discord: Join the Synthetix developer community