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)