GuideReference

Authentication

Client ID/Secret ModelCopied!

All API requests require proper signing with your Client Secret. The Client ID alone cannot be used to access the API.

  • Client ID: Your public project identifier

  • Client Secret: Your private signing key (must be kept secure)

Important Security Note: Knowing just your Client ID is not enough to access the API. All requests must be signed with your Client Secret.

Security BenefitsCopied!

This authentication model offers several security advantages:

  • Two-factor authentication: Access requires both the Client ID and a valid signature

  • No secret transmission: Your Client Secret is never sent to our servers

  • Request integrity: The signature ensures requests haven't been tampered with

  • Reduced risk: Even if your Client ID is exposed, attackers can't make API calls without the secret

  • Non-repudiation: Signed requests provide a strong audit trail

Authentication HeadersCopied!

All API requests to orda must include these headers:

Header

Description

x-client-id

Your project's client ID

x-signature

HMAC-SHA256 signature derived from your client secret

Content-Type

Usually application/json for requests with a body

Optional Headers

Header

Description

x-timestamp

Optional timestamp (in milliseconds since epoch) for preventing replay attacks

Creating a SignatureCopied!

The signature is an HMAC-SHA256 hash of the canonicalized request body, using your Client Secret as the key.

Signature Process

  1. Canonicalize your request body to ensure consistent JSON serialization

  2. For GET requests with no body, use an empty string

  3. Create an HMAC-SHA256 hash of the canonical string using your Client Secret as the key

  4. Convert the resulting hash to a hexadecimal string

  5. Include this hex string in the x-signature header

Canonical JSON Serialization

The request body is canonicalized to ensure consistent signatures regardless of JSON key ordering:

  • Object keys are sorted alphabetically

  • No whitespace between elements

  • Consistent string escaping

  • Handles nested objects and arrays recursively

Example:

// Original JSON (key order may vary)
{"name": "John", "age": 30, "city": "New York"}

// Canonical JSON (always same output)
{"age":30,"city":"New York","name":"John"}

Implementation Examples

const crypto = require('crypto');

function canonicalizeJSON(obj, seenObjects = new WeakSet()) {
    if (obj === null) {
        return 'null';
    }
    
    if (typeof obj === 'undefined') {
        return 'undefined';
    }
    
    if (typeof obj === 'boolean' || typeof obj === 'number') {
        return String(obj);
    }
    
    if (typeof obj === 'string') {
        return JSON.stringify(obj);
    }
    
    if (obj instanceof Date) {
        return JSON.stringify(obj.toISOString());
    }
    
    if (Array.isArray(obj)) {
        if (seenObjects.has(obj)) {
            return '[Circular]';
        }
        seenObjects.add(obj);
        
        const items = obj.map(item => canonicalizeJSON(item, seenObjects));
        seenObjects.delete(obj);
        return `[${items.join(',')}]`;
    }
    
    if (typeof obj === 'object') {
        if (seenObjects.has(obj)) {
            return '{Circular}';
        }
        seenObjects.add(obj);
        
        // Sort keys to ensure consistent ordering
        const sortedKeys = Object.keys(obj).sort();
        const pairs = sortedKeys.map(key => {
            const value = canonicalizeJSON(obj[key], seenObjects);
            return `${JSON.stringify(key)}:${value}`;
        });
        
        seenObjects.delete(obj);
        return `{${pairs.join(',')}}`;
    }
    
    return JSON.stringify(obj);
}

function createSignature(clientSecret, requestBody) {
  // For GET requests with no body, use an empty string
  const canonicalBody = requestBody ? canonicalizeJSON(requestBody) : '';
  
  // Create HMAC using client secret
  const hmac = crypto.createHmac('sha256', clientSecret);
  
  // Update HMAC with the canonical body and get the hex digest
  const signature = hmac.update(canonicalBody).digest('hex');
  return signature;
}

// Example usage for a POST request
const clientId = 'client_12345abcde';
const clientSecret = 'secret_67890fghij';
const requestBody = {
  name: 'Test Account',
  toChain: '1',
  toToken: 'ETH',
  toAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44b'
};
const signature = createSignature(clientSecret, requestBody);

// Making the request with axios
const headers = {
  'Content-Type': 'application/json',
  'x-client-id': clientId,
  'x-signature': signature
};

axios.post('https://api.orda.network/v1.1/projects/proj_id/accounts',
           requestBody,
           { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error('Error:', error));

// For GET requests with no body
const getSignature = createSignature(clientSecret, null);
axios.get('https://api.orda.network/v1.1/projects/proj_id/accounts', {
  headers: {
    'x-client-id': clientId,
    'x-signature': getSignature
  }
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error));
import hmac
import hashlib
import json
import requests
from typing import Any, Set

def canonicalize_json(obj: Any, seen_objects: Set[int] = None) -> str:
    if seen_objects is None:
        seen_objects = set()
        
    if obj is None:
        return 'null'
    elif isinstance(obj, bool):
        return 'true' if obj else 'false'
    elif isinstance(obj, (int, float)):
        return str(obj)
    elif isinstance(obj, str):
        return json.dumps(obj)
    elif isinstance(obj, list):
        obj_id = id(obj)
        if obj_id in seen_objects:
            return '[Circular]'
        seen_objects.add(obj_id)
        items = [canonicalize_json(item, seen_objects) for item in obj]
        seen_objects.remove(obj_id)
        return f"[{','.join(items)}]"
    elif isinstance(obj, dict):
        obj_id = id(obj)
        if obj_id in seen_objects:
            return '{Circular}'
        seen_objects.add(obj_id)
        sorted_keys = sorted(obj.keys())
        pairs = [f'"{key}":{canonicalize_json(obj[key], seen_objects)}' for key in sorted_keys]
        seen_objects.remove(obj_id)
        return f"{{{','.join(pairs)}}}"
    else:
        return json.dumps(obj)

def create_signature(client_secret, request_body=None):
    # For GET requests with no body, use an empty string
    canonical_body = canonicalize_json(request_body) if request_body else ''
    
    # Create HMAC using client secret
    signature = hmac.new(
        client_secret.encode('utf-8'),
        canonical_body.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return signature

# Example usage for a POST request
client_id = 'client_12345abcde'
client_secret = 'secret_67890fghij'
request_body = {
    'name': 'Test Account',
    'toChain': '1',
    'toToken': 'ETH',
    'toAddress': '0x742d35Cc6634C0532925a3b844Bc454e4438f44b'
}
signature = create_signature(client_secret, request_body)
headers = {
    'Content-Type': 'application/json',
    'x-client-id': client_id,
    'x-signature': signature
}
response = requests.post(
    'https://api.orda.network/v1.1/projects/proj_id/accounts',
    headers=headers,
    json=request_body
)
print(response.json())

# For GET requests with no body
get_signature = create_signature(client_secret)
get_headers = {
    'x-client-id': client_id,
    'x-signature': get_signature
}
get_response = requests.get(
    'https://api.orda.network/v1.1/projects/proj_id/accounts',
    headers=get_headers
)
print(get_response.json())
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.apache.commons.codec.binary.Hex;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

public class HmacSigner {
    
    public static String canonicalizeJSON(Object obj, Set<Object> seenObjects) {
        if (seenObjects == null) {
            seenObjects = new HashSet<>();
        }
        
        if (obj == null) {
            return "null";
        }
        
        if (obj instanceof Boolean || obj instanceof Number) {
            return obj.toString();
        }
        
        if (obj instanceof String) {
            return "\"" + obj.toString().replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
        }
        
        if (obj instanceof List) {
            if (seenObjects.contains(obj)) {
                return "[Circular]";
            }
            seenObjects.add(obj);
            
            List<?> list = (List<?>) obj;
            List<String> items = new ArrayList<>();
            for (Object item : list) {
                items.add(canonicalizeJSON(item, seenObjects));
            }
            seenObjects.remove(obj);
            return "[" + String.join(",", items) + "]";
        }
        
        if (obj instanceof Map) {
            if (seenObjects.contains(obj)) {
                return "{Circular}";
            }
            seenObjects.add(obj);
            
            Map<?, ?> map = (Map<?, ?>) obj;
            List<String> keys = new ArrayList<>();
            for (Object key : map.keySet()) {
                keys.add(key.toString());
            }
            Collections.sort(keys);
            
            List<String> pairs = new ArrayList<>();
            for (String key : keys) {
                String value = canonicalizeJSON(map.get(key), seenObjects);
                pairs.add("\"" + key + "\":" + value);
            }
            seenObjects.remove(obj);
            return "{" + String.join(",", pairs) + "}";
        }
        
        return "\"" + obj.toString() + "\"";
    }
    
    public static String createSignature(String clientSecret, Object requestBody) throws Exception {
        // Convert request body to canonical JSON string, or use empty string for null
        String canonicalBody = "";
        if (requestBody != null) {
            canonicalBody = canonicalizeJSON(requestBody, null);
        }
        
        // Create HMAC-SHA256 signature
        Mac hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(
            clientSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        hmac.init(secretKey);
        byte[] hash = hmac.doFinal(canonicalBody.getBytes(StandardCharsets.UTF_8));
        
        // Convert to hex string
        return Hex.encodeHexString(hash);
    }

    public static void main(String[] args) {
        try {
            String clientId = "client_12345abcde";
            String clientSecret = "secret_67890fghij";
            
            // Create request body
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("name", "Test Account");
            requestBody.put("toChain", "1");
            requestBody.put("toToken", "ETH");
            requestBody.put("toAddress", "0x742d35Cc6634C0532925a3b844Bc454e4438f44b");
            
            // Generate signature
            String signature = createSignature(clientSecret, requestBody);
            
            // Make HTTP request
            CloseableHttpClient client = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost("https://api.orda.network/v1.1/projects/proj_id/accounts");
            
            // Set headers
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setHeader("x-client-id", clientId);
            httpPost.setHeader("x-signature", signature);
            
            // Set body
            ObjectMapper mapper = new ObjectMapper();
            String jsonBody = mapper.writeValueAsString(requestBody);
            httpPost.setEntity(new StringEntity(jsonBody));
            
            // Execute request
            client.execute(httpPost);
            
            // For GET requests, signature would be created with null requestBody
            String getSignature = createSignature(clientSecret, null);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'openssl'
require 'json'
require 'net/http'
require 'uri'

def canonicalize_json(obj, seen_objects = Set.new)
  case obj
  when nil
    'null'
  when true, false
    obj.to_s
  when Numeric
    obj.to_s
  when String
    obj.to_json
  when Array
    if seen_objects.include?(obj.object_id)
      return '[Circular]'
    end
    seen_objects.add(obj.object_id)
    items = obj.map { |item| canonicalize_json(item, seen_objects) }
    seen_objects.delete(obj.object_id)
    "[#{items.join(',')}]"
  when Hash
    if seen_objects.include?(obj.object_id)
      return '{Circular}'
    end
    seen_objects.add(obj.object_id)
    sorted_keys = obj.keys.sort
    pairs = sorted_keys.map do |key|
      value = canonicalize_json(obj[key], seen_objects)
      "#{key.to_json}:#{value}"
    end
    seen_objects.delete(obj.object_id)
    "{#{pairs.join(',')}}"
  else
    obj.to_json
  end
end

def create_signature(client_secret, request_body = nil)
  # For GET requests with no body, use an empty string
  canonical_body = request_body ? canonicalize_json(request_body) : ''
  
  # Create HMAC using client secret
  signature = OpenSSL::HMAC.hexdigest(
    'SHA256',
    client_secret,
    canonical_body
  )
  return signature
end

# Example usage for a POST request
client_id = 'client_12345abcde'
client_secret = 'secret_67890fghij'
request_body = {
  name: 'Test Account',
  toChain: '1',
  toToken: 'ETH',
  toAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44b'
}
signature = create_signature(client_secret, request_body)

uri = URI('https://api.orda.network/v1.1/projects/proj_id/accounts')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri,
  'Content-Type' => 'application/json',
  'x-client-id' => client_id,
  'x-signature' => signature
)
request.body = request_body.to_json
response = http.request(request)
puts response.body

# For GET requests with no body
get_signature = create_signature(client_secret)
get_uri = URI('https://api.orda.network/v1.1/projects/proj_id/accounts')
get_request = Net::HTTP::Get.new(get_uri,
  'x-client-id' => client_id,
  'x-signature' => get_signature
)
get_response = http.request(get_request)
puts get_response.body
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "reflect"
    "sort"
    "strings"
    "bytes"
    "net/http"
)

func canonicalizeJSON(obj interface{}, seenObjects map[uintptr]bool) string {
    if seenObjects == nil {
        seenObjects = make(map[uintptr]bool)
    }
    
    if obj == nil {
        return "null"
    }
    
    v := reflect.ValueOf(obj)
    
    switch v.Kind() {
    case reflect.Bool:
        if v.Bool() {
            return "true"
        }
        return "false"
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return fmt.Sprintf("%d", v.Int())
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return fmt.Sprintf("%d", v.Uint())
    case reflect.Float32, reflect.Float64:
        return fmt.Sprintf("%g", v.Float())
    case reflect.String:
        jsonBytes, _ := json.Marshal(v.String())
        return string(jsonBytes)
    case reflect.Slice, reflect.Array:
        ptr := v.Pointer()
        if seenObjects[ptr] {
            return "[Circular]"
        }
        seenObjects[ptr] = true
        defer delete(seenObjects, ptr)
        
        var items []string
        for i := 0; i < v.Len(); i++ {
            items = append(items, canonicalizeJSON(v.Index(i).Interface(), seenObjects))
        }
        return "[" + strings.Join(items, ",") + "]"
    case reflect.Map:
        ptr := v.Pointer()
        if seenObjects[ptr] {
            return "{Circular}"
        }
        seenObjects[ptr] = true
        defer delete(seenObjects, ptr)
        
        keys := v.MapKeys()
        sort.Slice(keys, func(i, j int) bool {
            return fmt.Sprintf("%v", keys[i].Interface()) < fmt.Sprintf("%v", keys[j].Interface())
        })
        
        var pairs []string
        for _, key := range keys {
            keyStr, _ := json.Marshal(fmt.Sprintf("%v", key.Interface()))
            valueStr := canonicalizeJSON(v.MapIndex(key).Interface(), seenObjects)
            pairs = append(pairs, string(keyStr)+":"+valueStr)
        }
        return "{" + strings.Join(pairs, ",") + "}"
    default:
        jsonBytes, _ := json.Marshal(obj)
        return string(jsonBytes)
    }
}

func createSignature(clientSecret string, requestBody interface{}) string {
    var canonicalBody string
    if requestBody != nil {
        canonicalBody = canonicalizeJSON(requestBody, nil)
    } else {
        canonicalBody = ""
    }
    
    h := hmac.New(sha256.New, []byte(clientSecret))
    h.Write([]byte(canonicalBody))
    return hex.EncodeToString(h.Sum(nil))
}

func main() {
    clientID := "client_12345abcde"
    clientSecret := "secret_67890fghij"
    
    requestBody := map[string]interface{}{
        "name":      "Test Account",
        "toChain":   "1",
        "toToken":   "ETH",
        "toAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44b",
    }
    
    signature := createSignature(clientSecret, requestBody)
    
    // Create HTTP request
    jsonBody, _ := json.Marshal(requestBody)
    req, _ := http.NewRequest("POST", "https://api.orda.network/v1.1/projects/proj_id/accounts", bytes.NewBuffer(jsonBody))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-client-id", clientID)
    req.Header.Set("x-signature", signature)
    
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // For GET requests with no body
    getSignature := createSignature(clientSecret, nil)
    getReq, _ := http.NewRequest("GET", "https://api.orda.network/v1.1/projects/proj_id/accounts", nil)
    getReq.Header.Set("x-client-id", clientID)
    getReq.Header.Set("x-signature", getSignature)
    
    fmt.Printf("POST signature: %s\n", signature)
    fmt.Printf("GET signature: %s\n", getSignature)
}
use hmac::{Hmac, Mac};
use sha2::Sha256;
use serde_json::{Map, Value};
use std::collections::{HashMap, HashSet};
use reqwest;
use serde_json;

type HmacSha256 = Hmac<Sha256>;

fn canonicalize_json(obj: &Value, seen_objects: &mut HashSet<*const Value>) -> String {
    let obj_ptr = obj as *const Value;
    
    match obj {
        Value::Null => "null".to_string(),
        Value::Bool(b) => b.to_string(),
        Value::Number(n) => n.to_string(),
        Value::String(s) => serde_json::to_string(s).unwrap(),
        Value::Array(arr) => {
            if seen_objects.contains(&obj_ptr) {
                return "[Circular]".to_string();
            }
            seen_objects.insert(obj_ptr);
            
            let items: Vec<String> = arr.iter()
                .map(|item| canonicalize_json(item, seen_objects))
                .collect();
            
            seen_objects.remove(&obj_ptr);
            format!("[{}]", items.join(","))
        }
        Value::Object(map) => {
            if seen_objects.contains(&obj_ptr) {
                return "{Circular}".to_string();
            }
            seen_objects.insert(obj_ptr);
            
            let mut keys: Vec<&String> = map.keys().collect();
            keys.sort();
            
            let pairs: Vec<String> = keys.iter()
                .map(|key| {
                    let key_str = serde_json::to_string(key).unwrap();
                    let value_str = canonicalize_json(map.get(*key).unwrap(), seen_objects);
                    format!("{}:{}", key_str, value_str)
                })
                .collect();
            
            seen_objects.remove(&obj_ptr);
            format!("{{{}}}", pairs.join(","))
        }
    }
}

fn create_signature(client_secret: &str, request_body: Option<&Value>) -> Result<String, Box<dyn std::error::Error>> {
    let canonical_body = match request_body {
        Some(body) => canonicalize_json(body, &mut HashSet::new()),
        None => String::new(),
    };
    
    let mut mac = HmacSha256::new_from_slice(client_secret.as_bytes())?;
    mac.update(canonical_body.as_bytes());
    let result = mac.finalize();
    
    Ok(hex::encode(result.into_bytes()))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client_id = "client_12345abcde";
    let client_secret = "secret_67890fghij";
    
    let request_body = serde_json::json!({
        "name": "Test Account",
        "toChain": "1",
        "toToken": "ETH",
        "toAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44b"
    });
    
    let signature = create_signature(client_secret, Some(&request_body))?;
    
    let client = reqwest::Client::new();
    let response = client
        .post("https://api.orda.network/v1.1/projects/proj_id/accounts")
        .header("Content-Type", "application/json")
        .header("x-client-id", client_id)
        .header("x-signature", &signature)
        .json(&request_body)
        .send()
        .await?;
    
    println!("Response: {:?}", response.status());
    
    // For GET requests with no body
    let get_signature = create_signature(client_secret, None)?;
    let get_response = client
        .get("https://api.orda.network/v1.1/projects/proj_id/accounts")
        .header("x-client-id", client_id)
        .header("x-signature", &get_signature)
        .send()
        .await?;
    
    println!("GET Response: {:?}", get_response.status());
    
    Ok(())
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Net.Http;
using System.Threading.Tasks;

public class HmacSigner
{
    private static readonly HashSet<object> SeenObjects = new HashSet<object>();
    
    public static string CanonicalizeJson(object obj, HashSet<object> seenObjects = null)
    {
        seenObjects = seenObjects ?? new HashSet<object>();
        
        if (obj == null)
            return "null";
            
        if (obj is bool boolVal)
            return boolVal.ToString().ToLower();
            
        if (obj is int || obj is long || obj is double || obj is float || obj is decimal)
            return obj.ToString();
            
        if (obj is string strVal)
            return JsonSerializer.Serialize(strVal);
            
        if (obj is JsonElement jsonElement)
        {
            switch (jsonElement.ValueKind)
            {
                case JsonValueKind.Null:
                    return "null";
                case JsonValueKind.True:
                    return "true";
                case JsonValueKind.False:
                    return "false";
                case JsonValueKind.Number:
                    return jsonElement.ToString();
                case JsonValueKind.String:
                    return JsonSerializer.Serialize(jsonElement.GetString());
                case JsonValueKind.Array:
                    if (seenObjects.Contains(jsonElement))
                        return "[Circular]";
                    seenObjects.Add(jsonElement);
                    
                    var arrayItems = jsonElement.EnumerateArray()
                        .Select(item => CanonicalizeJson(item, seenObjects))
                        .ToArray();
                    seenObjects.Remove(jsonElement);
                    return $"[{string.Join(",", arrayItems)}]";
                    
                case JsonValueKind.Object:
                    if (seenObjects.Contains(jsonElement))
                        return "{Circular}";
                    seenObjects.Add(jsonElement);
                    
                    var objectPairs = jsonElement.EnumerateObject()
                        .OrderBy(prop => prop.Name)
                        .Select(prop => $"{JsonSerializer.Serialize(prop.Name)}:{CanonicalizeJson(prop.Value, seenObjects)}")
                        .ToArray();
                    seenObjects.Remove(jsonElement);
                    return $"{{{string.Join(",", objectPairs)}}}";
            }
        }
        
        return JsonSerializer.Serialize(obj);
    }
    
    public static string CreateSignature(string clientSecret, object requestBody = null)
    {
        string canonicalBody = requestBody != null 
            ? CanonicalizeJson(requestBody) 
            : string.Empty;
            
        using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(clientSecret)))
        {
            byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(canonicalBody));
            return Convert.ToHexString(hashBytes).ToLower();
        }
    }
    
    public static async Task Main(string[] args)
    {
        string clientId = "client_12345abcde";
        string clientSecret = "secret_67890fghij";
        
        var requestBody = new
        {
            name = "Test Account",
            toChain = "1",
            toToken = "ETH",
            toAddress = "0x742d35Cc6634C0532925a3b844Bc454e4438f44b"
        };
        
        string signature = CreateSignature(clientSecret, requestBody);
        
        using (var client = new HttpClient())
        {
            var request = new HttpRequestMessage(HttpMethod.Post, 
                "https://api.orda.network/v1.1/projects/proj_id/accounts");
            
            request.Headers.Add("x-client-id", clientId);
            request.Headers.Add("x-signature", signature);
            request.Content = new StringContent(
                JsonSerializer.Serialize(requestBody),
                Encoding.UTF8,
                "application/json"
            );
            
            var response = await client.SendAsync(request);
            Console.WriteLine($"POST Response: {response.StatusCode}");
            
            // For GET requests with no body
            string getSignature = CreateSignature(clientSecret);
            var getRequest = new HttpRequestMessage(HttpMethod.Get,
                "https://api.orda.network/v1.1/projects/proj_id/accounts");
            getRequest.Headers.Add("x-client-id", clientId);
            getRequest.Headers.Add("x-signature", getSignature);
            
            var getResponse = await client.SendAsync(getRequest);
            Console.WriteLine($"GET Response: {getResponse.StatusCode}");
        }
    }
}
import * as crypto from 'crypto';
import axios from 'axios';

function canonicalizeJSON(obj: any, seenObjects: WeakSet<object> = new WeakSet()): string {
    if (obj === null) {
        return 'null';
    }
    
    if (typeof obj === 'undefined') {
        return 'undefined';
    }
    
    if (typeof obj === 'boolean' || typeof obj === 'number') {
        return String(obj);
    }
    
    if (typeof obj === 'string') {
        return JSON.stringify(obj);
    }
    
    if (obj instanceof Date) {
        return JSON.stringify(obj.toISOString());
    }
    
    if (Array.isArray(obj)) {
        if (seenObjects.has(obj)) {
            return '[Circular]';
        }
        seenObjects.add(obj);
        
        const items = obj.map(item => canonicalizeJSON(item, seenObjects));
        seenObjects.delete(obj);
        return `[${items.join(',')}]`;
    }
    
    if (typeof obj === 'object') {
        if (seenObjects.has(obj)) {
            return '{Circular}';
        }
        seenObjects.add(obj);
        
        const sortedKeys = Object.keys(obj).sort();
        const pairs = sortedKeys.map(key => {
            const value = canonicalizeJSON(obj[key], seenObjects);
            return `${JSON.stringify(key)}:${value}`;
        });
        
        seenObjects.delete(obj);
        return `{${pairs.join(',')}}`;
    }
    
    return JSON.stringify(obj);
}

function createSignature(clientSecret: string, requestBody?: any): string {
    const canonicalBody = requestBody ? canonicalizeJSON(requestBody) : '';
    
    const hmac = crypto.createHmac('sha256', clientSecret);
    const signature = hmac.update(canonicalBody).digest('hex');
    return signature;
}

interface RequestBody {
    name: string;
    toChain: string;
    toToken: string;
    toAddress: string;
}

// Example usage for a POST request
const clientId: string = 'client_12345abcde';
const clientSecret: string = 'secret_67890fghij';
const requestBody: RequestBody = {
    name: 'Test Account',
    toChain: '1',
    toToken: 'ETH',
    toAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44b'
};

const signature = createSignature(clientSecret, requestBody);

const headers = {
    'Content-Type': 'application/json',
    'x-client-id': clientId,
    'x-signature': signature
};

axios.post('https://api.orda.network/v1.1/projects/proj_id/accounts', requestBody, { headers })
    .then(response => console.log(response.data))
    .catch(error => console.error('Error:', error));

// For GET requests with no body
const getSignature = createSignature(clientSecret);
axios.get('https://api.orda.network/v1.1/projects/proj_id/accounts', {
    headers: {
        'x-client-id': clientId,
        'x-signature': getSignature
    }
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error));

export { createSignature, canonicalizeJSON };

Security Recommendations

  • Never share your Client Secret: Your Client Secret should be treated like a password and never shared publicly or committed to version control.

  • Store secrets securely: Use environment variables, secret management services, or secure storage solutions to manage your Client Secret.

  • Regenerate secrets if compromised: If you suspect your Client Secret has been compromised, regenerate it immediately through the orda Dashboard.

  • Use HTTPS: Always make API requests over HTTPS to ensure the security of your Client ID and signature.

Implementation Tips

  • Canonical JSON serialization: The canonical serialization ensures consistent signatures regardless of object key ordering or JSON library differences.

  • Empty body handling: For requests without a body (like GET requests), use an empty string as the input to the HMAC function.

  • Character encoding: Always use UTF-8 encoding when converting strings to bytes for HMAC calculation.

  • Error handling: Include proper error handling to diagnose signature creation issues during development.

Common Issues and TroubleshootingCopied!

Invalid Signature Errors

If you receive "Invalid signature" errors:

  • Check your Client Secret: Ensure you're using the correct Client Secret for the Client ID.

  • Verify canonical serialization: Ensure you're using the canonical JSON serialization method shown above.

  • HTTP method: Confirm you're using the correct HTTP method (GET, POST, PUT, DELETE).

  • String encoding: Verify you're using UTF-8 encoding for string conversions.

  • Empty bodies: For GET requests, ensure you're signing an empty string, not "null" or "undefined".

SupportCopied!

If you encounter any issues implementing this authentication method, reach out!