Solutions
No Results
Solution

Using the Circle API to transfer funds between the Algorand blockchain and traditional bank accounts

Overview

As blockchain systems approach mainstream adoption for digital payments, one of the remaining challenges is support for on and off-ramps for transferring money between traditional banking systems and the blockchain ecosystem. This gap makes it difficult for consumers to fund blockchain wallets for spending. Also, merchants must be able to transfer earnings from their blockchain account to their bank account.

This solutions article will show how we implemented the Algorand TestNet Dispenser allowing developers access to both Algos and USDC stable coins. For USDC, the Circle API will enable transferring US dollars from a traditional bank account to USDC stable coins on the Algorand blockchain.

Algorand Dispenser

The Algorand Dispenser allows developers and others to fund Algorand testnet accounts with faux Algos for testing on the Testnet network.

The Dispenser is a web application that requires manual human interaction to transfer funds from the dispenser to a target account. The dispenser limits disbursements to a predefined amount to incrementally distribute funds as needed. A captcha ensures that the user is human and not a bot. Protecting the private account key used to sign transactions is critical.

Support for USDC Stable Coins

Circle recently made USDC stable coins available on the Algorand blockchain. Actual USDC is available on the Algorand mainnet, and also faux USDC is available on testnet. Algorand has updated the Algorand dispenser to allow developers to fund their testnet accounts with USDC. This article describes the new dispenser’s design and implementation, which uses the Circle API to transfer USD from a bank account to USDC on the Algorand blockchain.

Dispenser Web Interface

The following screenshot shows the Dispenser ReactJs web interface. In this case, the user has completed the captcha, entered the account address, and selected the USDC currency. The user is now ready to press the “DISPENSE” button to dispense $100 faux USDC to the provided account.
EditorImages/2021/02/19 01:28/1.png
Fig 1. Algorand Dispenser ready to dispense $100 faux USDC

The following screenshot shows the dispenser UI following the disbursement. The USDC balance is incremented by $100, and the transaction ID is populated.

EditorImages/2021/02/19 01:43/2a.png

Fig 2. The Algorand Dispenser, after dispensing $100 faux USDC.

Clicking on the Transaction ID will show the transaction in the RandLabs AlgoExplorer. Inspecting the transaction details, you can see that the dispenser transferred 100 of the asset id of 10458941 (USDC) to the target account.

EditorImages/2021/02/19 01:45/3.png
Fig 3. AlgoExplorer (Randlabs) showing the USDC asset transfer details.

The disbursements are visible with the Circle Account Management interface.

EditorImages/2021/02/19 01:47/4.png

Fig 4. Circle Account Management Interface, showing disbursements.

Requirements

The following criteria define the dispenser requirements:

  • Support dispensing Algo and USDC
  • Secure API keys and spending keys
  • Provide a web-based interface
  • Withstand bot attacks

Use Cases

The following use case diagram illustrates the use-cases supported by the dispenser.

EditorImages/2021/02/19 01:48/5.png

Fig 5. Dispenser use case diagram.

Actors:

Developer

The developer is a person building applications using the Algorand blockchain who requires a funded account on the Algorand test networks.

Use Cases:

Dispense Algos

This use case is to support dispensing Algos to an Algorand account on one of the test networks.

Dispense USDC

This use case is to support dispensing USDC assets to an Algorand account on Testnet.

Dispense X

This use case supports dispensing future assets to an Algorand account on one or more test networks. This use case ensures that the dispenser can be extended in the future to support assets not yet available.

View Result

Show the result of the dispensing operation, either success or failure, on failure, including the reason for the error.

Design

The following component diagram illustrates the dispenser’s high-level design.

EditorImages/2021/02/19 18:51/Dispenser_Component_Diagram.png

Fig 6. Dispenser Component Diagram

The dispenser is composed of a NodeJs server that serves a ReactJs web app to the user’s web browser. A separate Node Express app provides a REST endpoint for the web app to delegate disbursement requests. The Express app handles verifying the captcha token, signing and submitting Algorand transactions, and calling the Circle API for USDC transfers. The Express server secures the API keys used to communicate with the Algod API, Circle API, the Google Recaptcha API, and the signing keys used to sign disbursement transactions.

Dependent components include the Algod Node that supports interaction with the Algorand blockchain. The Circle API supports transferring US dollars and USDC between traditional bank accounts and the Algorand blockchain. The Dispenser uses a sandbox instance of the Circle API intended to support testing and does not process real currency.

Helm deploys the dispenser and the Algod node as pods within a Kubernetes cluster.

The following sequence diagram describes the message flow for processing the USDC disbursement request.

EditorImages/2021/02/19 18:53/Dispense_USDC_Sequence_Diagram.png

  • (1) The user starts the process by completing the captcha, entering their Algorand account address, selecting the USDC asset, and pressing the “dispense” button.
  • (1.1) The dispenser React App forwards the request to the Dispenser Express server.
  • (1.1.1) The captcha is first validated by calling the Google Recaptcha service.
  • (1.1.2) A call is made to the Circle API to transfer 100 USD from the dispensers bank account to the user’s Algorand account as USDC.
  • (1.1.2.1) The Circle API requests 100 USD from the dispenser’s bank account.
  • (1.1.2.2) The Circle API deposits 100 USDC into the user’s Algorand account.

Source Code

The following code is the CircleTransaction class to support interaction with the Circle API.

class CircleTransaction {

   max_disbursement_amount  = config.MAX_USDC_AMOUNT;

   async createAndSendTransaction(account, amount) {

       let transferAmount = Math.min(amount, this.max_disbursement_amount);

       console.log(`create and send Circle USDC transaction to account '${account}' with amount ${transferAmount} USDC`);

       let idempotencyKey = uuidv4();

       let body = {
           "idempotencyKey": idempotencyKey,
           "destination": {
               "type": "blockchain",
               "address": account,
               "chain": "ALGO"
           },
           "amount": {"amount": transferAmount, "currency": "USD"}
       };

       console.log(`Circle Transaction: \n  ${JSON.stringify(body)}`);

       let options = {
           method: 'POST',
           headers: {
               'Accept': 'application/json',
               'Content-Type': 'application/json',
               "Authorization": "Bearer " + config.CIRCLE_API_KEY
           },
           body: JSON.stringify(body)
       };

       let transferResponse = (await fetch(config.CIRCLE_TRANSFER_URL, options)
           .then(function (response) {
               return response.json();
           }).catch(err => {
               console.error(err);
           }));

       console.log(`transfer response:\n ${JSON.stringify(transferResponse)}`);

       return await this.waitForConfirmation(transferResponse);
   }

   sleep(delay) {
       return new Promise(res => setTimeout(res, delay));
   }

   async waitForConfirmation(transferResponse) {
       let i = 0;
       for (i; i < 15; i++) {
           await this.sleep(5000);
           let statusResponseData = await this.getTransferUSDCStatus(transferResponse.data);

           console.log(`status response Data \n ${JSON.stringify(statusResponseData)}`);

           if (statusResponseData.status === "complete") {
               return statusResponseData;
            } else if (statusResponseData.status !== "pending") {
               return statusResponseData;
           }
       }
   }

   async getTransferUSDCStatus(transferResponseData) {

       console.log(`check status of Circle USDC transaction: ${transferResponseData.id}`);

       let options = {
           method: 'GET',
           headers: {
               'Accept': 'application/json',
               'Content-Type': 'application/json',
               "Authorization": "Bearer " + config.CIRCLE_API_KEY
           }
       };

       let targetGetTransferURL = `${config.CIRCLE_TRANSFER_URL}/${transferResponseData.id}`;

       let statusResponse = await fetch(targetGetTransferURL, options)
           .then(response => {
               return response.json();
           })
           .catch(err => {
               console.error(err);
           });

       console.log(`status response: + ${JSON.stringify(statusResponse)}`);

       return statusResponse.data;
   }

February 19, 2021