Wallet as a Service Build Guide

If you haven’t already, Sign Up for Sandbox Access to get your client ID and secret to work through this Build Guide!

path

What is the Wallet as a Service Use Case?

Rail's Wallet as a service allows you to embed fully compliant digital currency wallets/accounts in your experience. Customers can use these accounts to deposit, hold, transfer, withdraw, exchange various stablecoins and cryptocurrencies. Assets are custodied at multiple qualified custodians such as banks/trusts.

Build Guide

This guide should take no longer than 10 minutes. By the end of this tutorial you will have:

  1. Authenticated your Request
  2. Created an account for an onboarded customer
  3. Generated a deposit address for the account
  4. Deposited funds into the account
  5. Transferred funds between two accounts
  6. Sold these funds to fiat through an exchange

Once you are done with this quick start guide, head over to our our API documentation to continue diving deeper.

1. Authenticating your requests

Every request you make to a Rail API endpoint requires an AUTH_TOKEN. We secure our endpoints using standards based OAuth2 Client Credentials Grant and scopes. This makes authenticating secure and easy.

To obtain the AUTH_TOKEN, you will need to authorize your account using your BASE_64_ENCODED_CLIENTID_AND_SECRET with the scopes you require:

Copy
Copied
curl --location --request POST 'https://auth.layer2financial.com/oauth2/ausbdqlx69rH6OjWd696/v1/token?grant_type=client_credentials&scope=customers:read+customers:write+accounts:read+accounts:write+transfers:read+transfers:write+exchanges:read+exchanges:write' \
--header 'Accept: application/json' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cache-Control: no-cache' \
--header 'Authorization: Basic {BASE_64_ENCODED_CLIENTID_AND_SECRET}' \

This gives you a response object containing your AUTH_TOKEN:

Copy
Copied
{
	"token_type":"Bearer",
	"expires_in":43200,
	"access_token": AUTH_TOKEN,
	"scope":"customers:read customers:write transfers:read transfers:write accounts:write accounts:read exchanges:read exchanges:write"
}

The scopes we’ll need for this tutorial are:

  • customers:read and customers:write so we can create our customer and get information about them.
  • accounts:read and accounts:write so we can create our accounts and get information about them.
  • transfers:read and transfers:write so we can create our transfers and get information about them.
  • exchanges:read and exchanges:write so we can create our exchanges and get information about them

The full list of scopes are available here.

With that done, we can get on to setting up accounts for a customer we have pre-created for you, Quorum, Inc., with a customer_id of QUORUM001. To learn how to create a customer, check out Applications.

2. Creating an Account

Accounts are the center of the Rail platform. They allow your customers to trade and hold fiat and other digital assets. They abstract away the complexity of wallets and keys.

Here we’re going to create an ETH account, but the process is the same whether your are creating a fiat account or a digital asset account

To create an account for your customer you need:

  • The customer_id from above. For this example that is ‘QUORUM001’.
  • An account_id : This is the unique identifier for the account. As with the customer id, this can be an External Entity Identifier from your own system. For this example, we’ll use ‘QUORUM001.001’ as our account id.
  • The product_id : This is the type of account to be opened. In this guide, we will be working with Deposit accounts. So, we are going to be using the DEPOSIT_FORT_CRYPTO for the crypto account AND DEPOSIT_FORT_FIAT for the fiat account. Click here to learn more about Accounts. Different products have different capabilities. Click here to learn more about the various products we support.
  • The asset_type_id : The asset type that this account is going to use. The asset_type_id is in the form of {BLOCKCHAIN}_{NETWORK}_{CURRENCY_CODE} . We are going to open an account for ETH so this is ETHEREUM_GOERLI_ETH . So for USD, this will be FIAT_TESTNET_USD . Click here to learn more about the various assets we support.

With all that information, we can add it to our data object and POST it to our accounts API endpoint:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"customer_id": "QUORUM001",
	"account_to_open": {
  "account_id": "QUORUM001.001",
  "product_id": "DEPOSIT_FORT_CRYPTO",
  "asset_type_id": "ETHEREUM_GOERLI_ETH"
	}
}'

If all goes well, we’ll get a 200 and this response:

Copy
Copied
{
	data: {
		id: "QUORUM001.001"
	}
}

If we need to query this specific account again we can use the same endpoint with GET and the ACCOUNT_ID appended:

Copy
Copied
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUM001.001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

This gives a response with the information we added above, the status of the account, and the current and available balance:

Copy
Copied
{
  data: {
    id: "QUORUM001.001",
    status: "OPEN",
    asset_type_id: "ETHEREUM_GOERLI_ETH",
    product_id: "DEPOSIT_FORT_CRYPTO",
    current_balance: 0,
    available_balance: 0
  }
}

As with the customers API, calling the accounts endpoint without appending an ACCOUNT_ID will return data on all accounts across all customers:

Copy
Copied
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \

The response:

Copy
Copied
{
  data: {
    accounts: [
      {
        id: "QUORUM001.001",
        status: "OPEN",
        asset_type_id: "ETHEREUM_GOERLI_ETH",
        product_type: "DEPOSIT",
        product_id: "DEPOSIT_FORT_CRYPTO",
        customer_id: "QUORUM001",
        current_balance: 0,
        available_balance: 0
      },
...
]
  },
}

Currently, we only have one account. We’ll change that soon. But first, let’s add funds to this account.

3. Generating a deposit address

You can add funds to your accounts by requesting an address for the account, via the deposits endpoint.

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
  "deposit_type": "PUSH",
	"deposit_destination": {
	  "destination_account_id": "QUORUM001.001"
  }
}'

This gives you a response object with the address. Click here to learn more about Deposits.

Copy
Copied
{
    "data": {
        "id": "d9b4cd0e-c66e-4540-9abd-55fa79833791",
        "status": "REQUESTED",
        "created_timestamp": "2023-03-05T14:56:32.316773-05:00",
        "destination_details": {
            "destination_account_id": "QUORUM001.001",
            "asset_type_id": "ETHEREUM_GOERLI_ETH"
        },
        "deposit_instructions": [
            {
                "instruction_type": "CRYPTO",
                "address": "0x9ED9961A87ba49511Cf522059df52e2a94eF188b",
                "blockchain": "ethereum",
                "network": "goerli",
                "asset_type_id": "ETHEREUM_GOERLI_USDC"
            }
        ]
    }
}

4. Depositing your funds

To add funds, all we need is the address from above. With the address, you can deposit USDC into this account using any Goerli faucet, such as the Alchemy Goerli faucet (requires signup) or All That Node (no signup needed). You can also send a note at support@layer2financial.com.

Once you’ve deposited funds, you can check they are in the account by querying the accounts/deposits/{account_id} endpoint again:

Copy
Copied
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUM001.001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

The current_balance should have changed to the amount you added to the account:

Copy
Copied
{
  "data": {
    "id": "QUORUM001.001",
    "status": "OPEN",
    "asset_type_id": "ETHEREUM_GOERLI_ETH",
    "product_id": "DEPOSIT_FORT_CRYPTO",
    "current_balance": 0.2,
    "available_balance": 0
  }
}

If the available_balance is showing 0 as above, the transaction is still going through all the AML and Fraud checks. You can check on the status of the transactions by querying the transactions endpoint.

Copy
Copied
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUM001.001/transactions' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

The response object will tell us what we need to know about our transactions:

Copy
Copied
{
	'data': {
		'transactions': [
			{
				'id': 'd01c36a3-78ef-41f0-8209-455dd0b46680', 
				'value': 0.2, 
				'transaction_date': '2022-11-09T09:46:55.519763', 
				'transaction_posted_date': '2022-11-09T09:46:55.519763', 
				'transaction_status': 'PENDING', 
				'description': 'TRANSFER_IN', 
				'transaction_type': 'TRANSFER_IN'
			}
		]
	}
}

As we can see, our transaction_status is still PENDING. Once the transaction is ACCEPTED, we’ll see the funds in our available_balance. You may find that your transaction was already POSTED by the time you have checked. That’s ok - we’re just moving faster than we planned!

Copy
Copied
{
  data: {
    id: "QUORUM001.001",
    status: "OPEN",
    asset_type_id: "ETHEREUM_GOERLI_ETH",
    product_id: "DEPOSIT_FORT_CRYPTO",
    current_balance: 0.2,
    available_balance: 0.2
  }
}

5. Transfering funds between accounts

We now have available funds in our account. Let’s spread the wealth and transfer some to another account.

First, we’ll need another account. Customers can have multiple accounts of the same or different product type and asset type. We’ll create another account for CUSTOMER_ID QUORUM001:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"customer_id": "QUORUM001",
	"account_to_open": {
	  "account_id": "QUORUM001.002",
	  "product_id": "DEPOSIT_FORT_CRYPTO",
	  "asset_type_id": "ETHEREUM_GOERLI_ETH"
	}
}'

We’ll get the response object with the new account id:

Copy
Copied
{
	data: {
		id: "QUORUM001.002"
	}
}

Note: Here we’re transferring funds between two accounts for the same customer. But you can also transfer funds between accounts of different customers just as easily—all you ever need are the ACCOUNT_IDs. You can find out more what’s needed to transfer between accounts in the documentation here.

Now we are set to transfer funds between the two accounts:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/transfers' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"source_account_id": "QUORUM001.001",
    "destination_account_id": "QUORUM001.002",
    "amount": 0.1,
    "description": "ETHEREUM_GOERLI_ETH"
}'

The data we get back shows that the transfer has been REQUESTED correctly:

Copy
Copied
{
  data: {
    id: "38c24bb4-c117-4bb2-951f-efd69c83a7d3",
    status: "REQUESTED",
    created_timestamp: "2022-11-07T21:56:20.254871",
    valid_until: "2022-11-07T22:06:20.233949",
    source_details: {
      source_account_id: "QUORUM001.001",
      asset_type_id: "ETHEREUM_GOERLI_ETH",
      amount_to_debit: 0.1
    },
    destination_details: {
      destination_account_id: "QUORUM001.002",
      asset_type_id: "ETHEREUM_GOERLI_ETH",
      amount_to_credit: 0.1
    }
  }
}

For the transfer to be executed, the receiving account has to accept the transfer. To do so, you pass the TRANSFER_ID to the transfers API accept endpoint:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/transfers/{TRANSFER_ID}/accept' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

Then the status should show ACCEPTED:

Copy
Copied
{
	data: {
		id: "38c24bb4-c117-4bb2-951f-efd69c83a7d3", 
		status: "ACCEPTED" 
	} 
}

Query our second account and we’ll see the funds:

Copy
Copied
{
  data: {
    id: "QUORUM001.002",
    status: "OPEN",
    asset_type_id: "ETHEREUM_GOERLI_ETH",
    product_id: "DEPOSIT_FORT_CRYPTO",
    current_balance: 0.1,
    available_balance: 0.1
  }
}

6. Exchanging funds

Customers can buy and sell funds on the Rail platform through the exchanges endpoint. Exchanges are like transfers, but between accounts in different currencies (asset types).

Let’s set up a third account, but this time in USD:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"customer_id": "QUORUM001",
	"account_to_open": {
	  "account_id": "QUORUM001.003",
	  "product_id": "DEPOSIT_FORT_FIAT",
	  "asset_type_id": "FIAT_TESTNET_USD"
	}	
}'

Once that account is set up we can SELL our 0.1 ETH in our QUORUM001.002 to our QUORUM001.003 account. We start by getting a quote.

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/exchanges/quote' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"source_account_id": "QUORUM001.002",
  "destination_account_id": "QUORUM001.003",
  "amount": 0.1,
  "action": "FIX_SOURCE"
}'

This will give us the response with the details of the REQUESTED exchange:

Copy
Copied
{
	'data': {
		'id': 'c3ac732e-fae0-4585-a7c3-71e24b8e7293', 
		'status': 'REQUESTED', 
		'created_timestamp': '2022-11-11T09:32:14.450333', 
		'action': 'FIX_SOURCE', 
		'source_details': {
			'source_account_id': 'QUORUM001.002', 
			'asset_type_id': 'ETHEREUM_GOERLI_ETH', 
			'amount_to_debit': 0.1
		}, 
		'destination_details': {
			'destination_account_id': 'QUORUM001.003', 
			'asset_type_id': 'FIAT_TESTNET_USD', 
			'amount_to_credit': 127.12
		}
	}
}

We can use the id from the 200 response above as the EXCHANGE_ID below to accept this exchange:

Copy
Copied
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/exchanges/{EXCHANGE_ID}/accept' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
	"maximum_slippage": "0.001"
}'

We have to provide the maximum_slippage, which is the percentage difference between the initial quote from the /exchanges/quote endpoint and what you are willing to accept. You can learn more here.

If the exchange is accepted, you’ll get this response:

Copy
Copied
"data": {
        "id": "c3ac732e-fae0-4585-a7c3-71e24b8e7293",
        "status": "ACCEPTED",
        "created_timestamp": "2022-11-11T10:31:59.728741",
        "action": "SELL",
        "source_details": {
            "source_account_id": "QUORUM001.002",
            "asset_type_id": "ETHEREUM_GOERLI_ETH",
            "amount_to_debit": 0.1
        },
        "destination_details": {
            "destination_account_id": "QUORUM001.003",
            "asset_type_id": "FIAT_TESTNET_USD",
            "amount_to_credit": 125.43
        }
    }
}

If it’s not accepted, you might have to increase your maximum_slippage.

You can now query the ACCOUNT_ID QUORUM001.003 to see the balance

Copy
Copied
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUM001.003' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

And that’s it. You have everything you need to set up each of your customers on the Rail platform and allow them to open accounts, and receive, transfer, and buy and sell funds.

Summary

Let’s review:

  1. Authenticate using the OAuth Endpoint to get the AUTH_TOKEN .
  2. Open an account with the accounts/deposits endpoint . Then use the account_id to query or update information.
  3. Generate a address to deposit funds into the new account using the deposits endpoint .
  4. Deposit funds using external faucets to the address and query the balance of the the account using the accounts/deposits/{account_id} endpoint
  5. Transfer funds between accounts using the transfers endpoint .
  6. Exchange funds with the exchanges endpoint .

To dive deeper into what you can do on the Rail platform, head to our API documentation.

Contact Us - We’d love to hear your thoughts, and you can contact the team here or email us at sales@layer2financial.com.

© 2024 Rail. All Rights Reserved.