Skip to content

Authentication Overview

The Synthetix API uses EIP-712 signatures for secure, cryptographic authentication. This approach ensures that only authorized accounts can execute trades while maintaining the highest security standards.

Why EIP-712?

EIP-712 (Ethereum Improvement Proposal 712) is the gold standard for signing structured data in the Ethereum ecosystem:

Security

  • Type Safety: Prevents signing malformed or malicious data
  • Domain Separation: Protects against cross-contract signature replay attacks
  • Human Readable: Users can see exactly what they're signing

Authentication Components

Every authenticated request consists of three key components:

Action Object

Contains the specific operation you want to perform:

{
  "action": "placeOrders",
  "orders": [{
    "symbol": "BTC-USDT",
    "side": "buy",
    "quantity": "0.1",
    "price": "50000",
    "orderType": "limitGtc",
    "triggerPrice": "",
    "isTriggerMarket": false,
    "reduceOnly": false
  }]
}

Nonce

A unique, incrementing number to prevent replay attacks:

{
  "nonce": 1704067200000  // Unix timestamp in milliseconds
}

EIP-712 Signature

Cryptographic proof that you authorized this specific request:

{
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  }
}

Signature Format Validation

All EIP-712 signatures must conform to the following strict format:

ComponentTypeValid ValuesDescription
vinteger0, 1, 27, or 28Recovery ID (typically 27 or 28)
rstring32 bytes hexFirst 32 bytes of the signature
sstring32 bytes hexSecond 32 bytes of the signature
Validation Rules:
  • v must be exactly one of: 0, 1, 27, or 28
  • r must match regex: ^0x[a-fA-F0-9]{64}$ (0x prefix + 64 hex chars)
  • s must match regex: ^0x[a-fA-F0-9]{64}$ (0x prefix + 64 hex chars)
  • Both r and s must be non-zero values

Authentication Flow

CLIENT                    API                     BLOCKCHAIN
  |                        |                         |
  |-- 1. Generate nonce ---|                         |
  |                        |                         |
  |-- 2. Create typed ------|                         |
  |    message             |                         |
  |                        |                         |
  |-- 3. Sign with --------|                         |
  |    private key         |                         |
  |                        |                         |
  |-- 4. Send signed ----->|                         |
  |    request             |                         |
  |                        |-- 5. Validate ---------|
  |                        |    signature format    |
  |                        |                         |
  |                        |-- 6. Recover signer -->|
  |                        |    address             |
  |                        |                         |
  |                        |-- 7. Check permissions |
  |                        |                         |
  |<-- 8. Execute ---------|                         |
      operation            |                         |

Step-by-Step Process

  1. Prepare the Request: Create your action object with the operation details
  2. Generate Nonce: Use current timestamp or increment from last nonce
  3. Structure the Message: Combine action, nonce, and optional parameters
  4. Sign with EIP-712: Use your private key to create the cryptographic signature
  5. Send to API: Submit the complete authenticated request
  6. API Validation: Server verifies signature and executes operation

Domain Separator

The domain separator uniquely identifies the Synthetix API and prevents signature reuse across different applications. For the complete domain configuration, see Environments.

Important: The domain must match exactly for signatures to be valid.

Message Types

Each API operation has a specific typed data structure. For example, placing orders:

const types = {
  PlaceOrders: [
    { name: "orders", type: "Order[]" },
    { name: "nonce", type: "uint256" },
    { name: "expiresAfter", type: "uint256" }
  ],
  Order: [
    { name: "symbol", type: "string" },
    { name: "side", type: "string" },
    { name: "quantity", type: "string" },
    { name: "price", type: "string" },
    { name: "orderType", type: "string" },
    { name: "reduceOnly", type: "bool" }
  ]
};

Optional Parameters

Request Expiration

Add an expiration timestamp to prevent stale request execution:

{
  "expiresAfter": 1704067300000  // 5 minutes from nonce
}

Subaccount Trading

Trade on behalf of another account (requires proper delegation):

{
  "subAccountId": "1867542890123456789"
}

Common Issues

  • Reusing nonces (breaks replay protection)
  • Wrong domain separator (signatures will fail)
  • Exposing private keys in logs or client code
  • Not handling clock drift (use buffer for expiration)
  • Signing malformed or unvalidated data

Next Steps

Related Documentation