Create and commit withdrawals

Request flow of withdrawals.

Once you have completed the setup we can start sending withdrawal requests. The request flow is consists of two parts, the proposal and the commit. The proposal response is a validation of the withdrawal and most importantly contains the estimated onchain transaction fees. To actually trigger the onchain withdrawal take the UUID of the proposal and send a commit request.

Signed JWT

All requests my contain a signed JWT as a bearer token. The JWT contains both a header and a payload, which is combined to be signed by the private key part of the key pair generated. The header will need contain the key id which is the response from uploading the public key, while the payload contains the scope of the request, which in our case it must include external_withdrawal. See Typescript sample code below, using jose npm package.

See more detailed docs on signing JWT.

import * as jose from 'jose';
import * as fs from 'fs';

const API_KEY_ID = process.env.API_KEY_ID;

async function signJWT(payload: jose.JWTPayload, header: jose.JWTHeaderParameters) {
  const privateKeyJson = JSON.parse(fs.readFileSync('./private_key.json', 'utf-8'));
  const privateKey = await jose.importJWK(privateKeyJson, 'ES256');
  const jwt = await new jose.SignJWT(payload)
    .setProtectedHeader(header)
    .sign(privateKey);

  return jwt;
}

export async generateJWT() {
  const payload: jose.JWTPayload = {
    aud: 'CJX',
    scope: 'read external_withdrawal',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600,
  };

  const header = {
    alg: 'ES256',
    kid: API_KEY_ID,
    typ: 'JWT'
  }
  const jwt = await signJWT(payload, header);

  return jwt;
} 

generateJWT() function will be used by all subsequent requests.

Create withdraw proposal request

  • The account_number is the last part of the url when you navigate to your specific crypto account on CoinJar Exchange, for instance it is CJX100040062BTC for https://exchange.coinjar.com/accounts/CJX100040062BTC
  • create proposal API reference.
const BASE_URL = 'https://api.exchange.coinjar.com'

async createWithdrawalProposal(): Promise {
  const jwt = await generateJWT(); // from previous step

  const headers = {
    'Authorization': `Bearer ${jwt}`,
   'Content-Type': 'application/json',
  };
  
  const body = {
    account_number: 'CJX100040062BTC',
    amount: '0.001',
    network: 'bitcoin',
    address: 'bc1qu99p89zds6nff3zpc05spgju658e3xljlvp0v6'
  };

  const response = await fetch(`${BASE_URL}/external_crypto_withdrawals`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
  }

  return response.json();
}

Response json object contains a uuid string which is used in the subsequent withdrawal commit request.

Commit withdraw request

  • Only the uuid from the previous proposal response is need to commit the withdraw with a PATCH request.
  • commit request API reference.
const BASE_URL = 'https://api.exchange.coinjar.com'

async commitWithdrawal(uuid: string): Promise {
  const jwt = await generateJWT(); // from previous step

  const headers = {
    'Authorization': `Bearer ${jwt}`,
   'Content-Type': 'application/json',
  };
  
  const response = await fetch(`${BASE_URL}/external_crypto_withdrawalsa/${uuid}/commit`, {
    method: 'PATCH',
    headers
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
  }

  return response.json();
}

A successful commit will have a response contain a state property with transferring.

Get withdraw request

Once the commit for the withdraw is done you can check the state of the withdraw as it flows through our systems and then broadcasted to the appropriate blockchain. You can poll the below API using uuid from previous step to get the state of the withdraw. State is a property of the json response of the below API.

The states are:

  • transferring: Assets are marked to transferred from CoinJar Exchange to CoinJar App.
  • transferred: Assets have been successfully transferred from CoinJar Exchange to CoinJar App.
  • sending: Withdraw transaction has been created (passing more checks) but not yet broadcasted to the appropriate blockchain.
  • confirming: Transaction has been broadcasted but not enough block confirmation yet.
  • confirmed: A number of block confirmations have passed, this varies depending on the blockchain.
const BASE_URL = 'https://api.exchange.coinjar.com'

async getWithdrawal(uuid: string): Promise {
  const jwt = await generateJWT(); // from previous step

  const headers = {
    'Authorization': `Bearer ${jwt}`,
   'Content-Type': 'application/json',
  };
  
  const response = await fetch(`${BASE_URL}/external_crypto_withdrawalsa/${uuid}`, {
    method: 'GET',
    headers
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
  }

  return response.json();
}