Signing request example

You can check below some examples of reference clients to help you signing the transactions.

# https://pypi.org/project/PyNaCl/

from nacl import signing

import time

import requests


class AnchorageAuth(requests.auth.AuthBase):

    ACCESS_KEY_HEADER = "Api-Access-Key"
    SIGNATURE_HEADER = "Api-Signature"
    TIMESTAMP_HEADER = "Api-Timestamp"

    access_key: str
    signing_key: signing.SigningKey

    def __init__(self, access_key: str, signing_key_seed: bytes):
        self.access_key = access_key
        self.signing_key = signing.SigningKey(signing_key_seed)

    def __call__(self, r: requests.PreparedRequest):
        r.headers[self.ACCESS_KEY_HEADER] = self.access_key

        timestamp = str(int(time.time()))
        method = r.method.upper() if r.method else "GET"
        body: bytes = bytes()
        if r.body and isinstance(r.body, bytes):
            body = r.body
        elif r.body and isinstance(r.body, str):
            body = bytearray(r.body, "utf-8")
        message = b"".join(
            [bytearray(timestamp, "utf-8"), bytearray(method, "utf-8"), bytearray(r.path_url,
"utf-8"), body]

        )
        signature = self.signing_key.sign(message).signature.hex()
        r.headers[self.SIGNATURE_HEADER] = signature
        r.headers[self.TIMESTAMP_HEADER] = timestamp
        return r


# load secrets

# Use the API key generated in the Anchorage Digital Web Dashboard

access_key = ...

# Use the Ed25519 signing private key

signing_key_str = ... # load the raw string

signing_key = bytes(bytearray.fromhex(signing_key_str))

data = {}

anchorage_auth = AnchorageAuth(access_key, signing_key)
# Update the URL below to match the environment you are using 
# Prod: https://api.anchorage.com/v2
# Staging: https://api.anchorage-staging.com/v2
r = requests.post("https://api.anchorage.com/v2/transfers", data=data, auth=anchorage_auth)
const {sign} = require('@noble/ed25519');

const axios = require('axios');

class AnchorageClient {
    constructor(aKey, sKey) {
        this.keys = {
            'api-key': aKey,
            'sign-key': sKey
        };
        this.version = '/v2';
        this.basePath = 'https://api.anchorage-staging.com'; 
    }

    async getTimestamp() {
        return Math.floor(new Date().getTime() / 1000);
    }

    async sendSignedRequest(method, endPoint, body) {
        var time = await this.getTimestamp();
        const signatureRequest = `${time}${method}${endPoint}${JSON.stringify(body)}`  
        var hexMessage = Buffer.from(signatureRequest, 'utf8').toString('hex')
        const signature = await sign(hexMessage, this.keys['sign-key']);
        var hexSign = Buffer.from(signature, 'uint8').toString('hex')
        var finalUrl = this.basePath + endPoint;
        return axios({
            method: method,
            url: finalUrl,
            data: body,
            headers: {
                'Api-Access-Key': this.keys['api-key'],
                'Api-Signature': hexSign,
                'Api-Timestamp': time,
                "Content-Type": "application/json"
            }
        }).then(res => res.data);
    }
};

const getQuoteRequest = () => {
    return {
        "tradingPair": "BTC-USD",
        "quantity": "1",
        "currency": "BTC",
        "side": "BUY"
      };
}

const getAcceptQuoteRequest = (quoteId) => {
    return {
        "quoteID": quoteId,
        "side": "BUY",
        "allowedSlippage": "0.001"
      };
}

const callAPI = async () =>{
    const apiClient = new AnchorageClient('your API access KEY','Your signing key');    
    try{        
        const quoteResponse = await apiClient.sendSignedRequest('POST', '/v2/trading/quote', getQuoteRequest());
        console.log(quoteResponse);

        const quoteId = quoteResponse['data']['quoteID']
        console.log(`quoteId=${quoteId}`);

        const acceptQuote = await apiClient.sendSignedRequest('POST', '/v2/trading/quote/accept', getAcceptQuoteRequest(quoteId));
        console.log(acceptQuote);

    }catch (error) {
        console.warn(error)
      }
}

callAPI();
package com.anchorage.api.client;


import okio.Buffer;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.json.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.time.Instant;

/**
 * A sample java API client to connect to Anchorage API v2
 *
 *
 * The critical denpendencies include: *
 *          <dependency>
 *             <groupId>org.json</groupId>
 *             <artifactId>json</artifactId>
 *             <version>20210307</version>
 *         </dependency>
 *         <dependency>
 *             <groupId>org.bouncycastle</groupId>
 *             <artifactId>bcpkix-jdk15on</artifactId>
 *             <version>1.69</version>
 *         </dependency>
 *         <dependency>
 *             <groupId>commons-codec</groupId>
 *             <artifactId>commons-codec</artifactId>
 *             <version>1.15</version>
 *         </dependency>
 *         <dependency>
 *             <groupId>com.squareup.okio</groupId>
 *             <artifactId>okio</artifactId>
 *             <version>1.9.0</version>
 *         </dependency>
 *
 */
public class RestClientWithSigning {

    private RestTemplate restTemplate;

    public static void main(String[] args){
        RestClientWithSigning api = new RestClientWithSigning();
        api.init();
        api.apiCall();
    }

    private void init(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(2000);
        factory.setReadTimeout(2000);
        restTemplate = new RestTemplate(factory);
    }

    private void apiCall() {
        try {
            String url = "https://api.anchorage-staging.com/v2/trading/quote";
            String body = createRequestBody();


            HttpEntity<String> request = new HttpEntity<>(body, createHeaders("/v2/trading/quote", HttpMethod.POST, body ));
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, request,String.class);
            if (responseEntity != null && HttpStatus.CREATED == responseEntity.getStatusCode()) {
                System.out.println(String.format("Getting data from ( %s ) response: %s", url, responseEntity.getBody()));
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String createRequestBody(){
        JSONObject body = new JSONObject();
        body.put("currency", "ETH");
        body.put("quantity", "1.2");
        body.put("side", "BUY");
        body.put("tradingPair", "ETH-USD");
        return body.toString();
    }

    private HttpHeaders createHeaders(String requestPaht, HttpMethod httpmethod, String body) throws DecoderException, CryptoException {
        String api_key = "your API Key";
        String signing_key_hex = "Your signing key";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Api-Access-Key", api_key);

        String timestamp = String.valueOf(Instant.now().getEpochSecond());
        byte[] toSign = decodeMessage(timestamp,httpmethod,requestPaht, body);

        String signature = bc_sign(Hex.decodeHex(signing_key_hex), toSign);
        headers.add("Api-Signature",signature);
        headers.add("Api-Timestamp", timestamp);
        return headers;
    }

    private byte[] decodeMessage(String timestamp, HttpMethod httpmethod, String url_path,String body ){
        Buffer buffer = new Buffer();
        buffer.write(timestamp.getBytes(StandardCharsets.UTF_8))
                .write(httpmethod.name().getBytes(StandardCharsets.UTF_8))
                .write(url_path.getBytes(StandardCharsets.UTF_8))
                .write( body.getBytes(StandardCharsets.UTF_8) );
        return buffer.readByteArray();
    }

    private String bc_sign(byte[] signing_key, byte[] toSign ) throws CryptoException {
        Ed25519PrivateKeyParameters privateKeyParameters = new Ed25519PrivateKeyParameters(signing_key);

        Signer signer = new Ed25519Signer();
        signer.init(true, privateKeyParameters);
        signer.update(toSign, 0, toSign.length);
        String signature = Hex.encodeHexString(signer.generateSignature());

        return signature;
    }
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using System.Text;
using System.Linq;
using Sodium;
using static Sodium.Utilities;


// 1. add package: add Sodium.Core --version 1.3.1
// 2. replace the privateKey,publicKey and Api-Access-Key with your keys
// 3. get a quote Id
// 4. replace the quoteId in the payload

namespace WebAPIClient
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static async Task Main(string[] args)
        {
            await ProcessAnchorageAPISign();      
            //await getQuoteId();          
        }       
    
         private static async Task<Object> ProcessAnchorageAPISign()
        {
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            
            string privateKey = "your private key here";
            string publicKey = "your public key here";
            string signingKey = $"{privateKey}{publicKey}";  
            
            string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
            string path="/v2/trading/quote/accept";
            string method="POST";
            string payload = "{\"quoteID\":\"1b1748b0-a62a-46c7-802c-7c61ac222424\",\"side\":\"BUY\"}";
            string msgToSing = $"{timestamp}{method}{path}{payload}";
            var signature = ComputeSignature(HexToBinary(signingKey), Encoding.UTF8.GetBytes(msgToSing) );

            Console.WriteLine($"api signature => {signature}");
            client.DefaultRequestHeaders.Add("Api-Access-Key", "your api access key");
            client.DefaultRequestHeaders.Add("Api-Timestamp", timestamp);
            client.DefaultRequestHeaders.Add("Api-Signature", signature);

            Uri u = new Uri("https://api.anchorage-staging.com/v2/trading/quote/accept");
            HttpContent c = new StringContent(payload, Encoding.UTF8, "application/json");
            HttpResponseMessage result = await client.PostAsync(u, c);
            
            var response = await result.Content.ReadAsStringAsync();
            Console.WriteLine(response);
            return response;
            
        }


        static string ComputeSignature(byte[] privateKey, byte[] toSign)
        {
            byte[] signature = PublicKeyAuth.SignDetached(toSign, privateKey);
            return Utilities.BinaryToHex(signature);
        }
        
        static async Task<string> getQuoteId()
        {
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("Api-Access-Key", "your api access key");

            string payload = "{\"currency\": \"ETH\",\"quantity\": \"0.5\",\"side\": \"BUY\",\"tradingPair\": \"ETH-USD\"}";
            Uri u = new Uri("https://api.anchorage-staging.com/v2/trading/quote");
            HttpContent c = new StringContent(payload, Encoding.UTF8, "application/json");
            HttpResponseMessage result = await client.PostAsync(u, c);
            string response = await result.Content.ReadAsStringAsync();
            
            Console.WriteLine(response);     
            return response;   
        }
    }
}
  require "ed25519"
  require "net/http"
  require "time"

  def hex_to_bin(s)
      [s].pack('H*')
  end

  def bin_to_hex(s)
      s.unpack('H*').first
  end

  private_key_seed_hex = '0101010101010101010101010101010101010101010101010101010101010101'
  public_key_hex = '8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c'
  key_pair_hex = private_key_seed_hex + public_key_hex

  key_pair = hex_to_bin(key_pair_hex)

  signing_key = Ed25519::SigningKey.from_keypair(key_pair)

  timestamp = '1577880000' # Time.now.to_i.to_s

  req = Net::HTTP::Post.new('/v2/transfers?foo=bar&baz=bang')
  req.body = '{"source": {"id": "1c920f4241b78a1d483a29f3c24b6c4c", "type": "VAULT"},
"assetType": "ETH", "destination": {"id": "55e89d4a644d736b01533a2ea9b32a20", "type": VAULT"}, "amount": "1000.00000000"}'


  signature = signing_key.sign(timestamp + req.method + req.path + req.body)

  req['Api-Access-Key'] = 'YOUR_ACCESS_KEY'
  req['Api-Timestamp'] = timestamp
  req['Api-Signature'] = bin_to_hex(signature)

  puts bin_to_hex(signature)