For the complete documentation index, see llms.txt. This page is also available as Markdown.

Full Example

API Signed Request Examples

If you use the Quadrata SDK to make your API calls, the request signature is generated for you.

const buffer = require('buffer');
const crypto = require('crypto');

/* Message Signing Helpers */

/**
 * Convert a string to an array buffer
 * @param {string} str
 * @returns {string}
 */
function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

/**
 * Convert a string to base64-url safe encoding
 * @param {string} str
 * @returns {string}
 */
function base64UrlEncode(str) {
    return Buffer.from(str).toString('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');
}

/**
 * Import a private key and return a CryptoKey
 * @param {string} privateKeyDer base64 encoded private key der
 * @returns {Promise<CryptoKey>}
 */
async function getPrivateKeyFromDer(privKeyDer) {
    const decoded = Buffer.from(privKeyDer, 'base64').toString('binary');
    const buf = str2ab(decoded);
    return crypto.subtle.importKey('pkcs8', buf, {
        name: 'ECDSA',
        namedCurve: 'P-256',
    }, true, ['sign']);
}

/**
 * Sign a message with a private CryptoKey
 * @param {CryptoKey} privateKey
 * @param {string} message The message to sign
 * @returns {string} The signed message
 */
async function signMessage(privateKey, message) {
    return crypto.subtle.sign(
        {
            name: 'ECDSA',
            hash: 'SHA-256'
        }, 
        privateKey, 
        new TextEncoder().encode(message)
    );
}

/**
 * Create a request signature
 * @param {CryptoKey} privateKey
 * @param {string} date HTTP header date value
 * @param {string} method HTTP method value
 * @param {string} path HTTP path 
 * @param {string} [queryString] HTTP path query string (? not included)
 */
async function createRequestSignature({
    privateKey,
    date, 
    method, 
    path, 
    queryString = undefined
}) {
    const nonce = Date.now().toString();
    const sigParts = [
        method.toUpperCase(),
        path,
        queryString,
        date,
        nonce
    ].filter(str => str && str.length !== 0);
    const signature = await signMessage(privateKey, sigParts.join('\n'));
    return base64UrlEncode(signature) + '.' + base64UrlEncode(nonce);
}

/* HTTP Request */

const API_URL = 'https://int.quadrata.com';
const API_KEY = '...';

/**
 * Send a signed request
 * @param {CryptoKey} privateKey
 * @param {string} method HTTP method value
 * @param {string} path HTTP path
 * @param {string} [queryString] HTTP path query string (? not included)
 * @param {object} [body] HTTP body payload
 * @param {object} [extraHeaders] Optional headers to send with the request
 * @returns {Promise<Response|void>}
 */
async function makeRequest({
    privateKey, 
    method, 
    path, 
    queryString = undefined,
    body = undefined,
    extraHeaders = undefined
}) {
    const date = (new Date()).toUTCString();
    const signature = await createRequestSignature({
        privateKey,
        method,
        path,
        queryString,
        date
    });
    const headers = {
        ...extraHeaders,
        'Authorization': `Basic ${Buffer.from(API_KEY).toString('base64')}`,
        'Date': date,
        'Signature': signature
    };
    let urlPath = `${API_URL}${path}`;
    if (queryString && queryString.length !== 0) {
        urlPath += `?${queryString}`;
    }
    const response = await fetch(urlPath, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined
    });
    if (!response.ok) {
        console.error(response);
        return;
    }
    return response.json();
}

/**
 * Base64 Encoded Private Key DER
 * @type {string}
 */
const privateKeyDer = '...';

/**
 * @type {CryptoKey}
 */
const privateKey = await getPrivateKeyFromDer(privateKeyDer);

/**
 * @type {string}
 */
const walletAddress = '...';

// make the request
const json = await makeRequest({
    method: 'GET',
    privateKey: privateKey,
    path: `/api/v1/privacy/grants/${encodeURIComponent(walletAddress)}`,
});

Last updated