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!
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:
- Authenticated your Request
- Created an account for an onboarded customer
- Generated a deposit address for the account
- Deposited funds into the account
- Transferred funds between two accounts
- 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:
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:
{
"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
andcustomers:write
so we can create our customer and get information about them. -
accounts:read
andaccounts:write
so we can create our accounts and get information about them. -
transfers:read
andtransfers:write
so we can create our transfers and get information about them. -
exchanges:read
andexchanges: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 theDEPOSIT_FORT_CRYPTO
for the crypto account ANDDEPOSIT_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. Theasset_type_id
is in the form of{BLOCKCHAIN}_{NETWORK}_{CURRENCY_CODE}
. We are going to open an account for ETH so this isETHEREUM_GOERLI_ETH
. So for USD, this will beFIAT_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:
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:
{
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:
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:
{
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:
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
The response:
{
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.
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.
{
"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:
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:
{
"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.
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:
{
'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!
{
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:
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:
{
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_ID
s. 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:
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:
{
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:
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
:
{
data: {
id: "38c24bb4-c117-4bb2-951f-efd69c83a7d3",
status: "ACCEPTED"
}
}
Query our second account and we’ll see the funds:
{
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:
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.
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:
{
'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:
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:
"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
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:
-
Authenticate using the OAuth Endpoint to get the
AUTH_TOKEN
. -
Open an account with the
accounts/deposits
endpoint . Then use theaccount_id
to query or update information. -
Generate a
address
to deposit funds into the new account using thedeposits
endpoint . -
Deposit funds using external faucets to the
address
and query the balance of the the account using theaccounts/deposits/{account_id}
endpoint -
Transfer funds between accounts using the
transfers
endpoint . -
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.