On-ramp Use Case 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 On Ramp use case?
With the on ramp use case, you or your customers can safely and securely send in fiat, buy cryptocurrencies, stablecoins and hold or withdraw the digital currencies. We support a number of different digital assets today. Click here for the list of supported assets.
Fiat can be deposited using various different rails such as ACH, Fedwire, Swift, etc. The digital assets can be custodied on the Rail platform with qualified custodians such as banks, trusts or can be withdrawn to external custodial or non-custodial wallets.
Build Guide
This guide should take no longer than 10 minutes and will allow you to execute a Fiat On Ramp in Sandbox.
You'll open USD and USDC accounts for an already onboarded customer, add USD, exchange it to USDC and then withdraw the USDC to an external wallet.
NOTE: Given there is no testnet for fiat, there are some differences between Sandbox and Production. We will highlight those differences as we go along.
By the end of this guide you will have:
- Authenticated your request
- Created the fiat and crypto accounts for the customer
- Deposited the fiat
- Sold fiat for crypto
- Withdrawn crypto to an external wallet
Let’s go!
1. Authenticating your request
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=accounts:read+settlements:read+customers:read+customers:write+accounts:write+withdrawals:read+withdrawals:write+adjustments:read+adjustments:write+exchanges:read+exchanges:write+transfers:read+transfers:write+deposits:read+deposits:write+applications:read+applications: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 accounts:write accounts:read exchanges:read exchanges:write withdrawals:read withdrawals:write deposits:read deposits: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. -
exchanges:read
andexchanges:write
so we can create an exchange operation and get information about it -
deposits:read
anddeposits:write
so we can create a deposit operation and get information about it -
withrawals:read
andwithdrawals:write
so we can create a withdrawal operation and get information about it
The full list of scopes are available here.
With that done, we can get on to setting up accounts for a individual customer we have pre-created for you: Daniel Lee, with a customer_id of DANIELLEE001
. To learn how to create a customer, check out Applications.
2. Creating the fiat and crypto accounts
We need two accounts, a fiat deposit account and a crypto deposit account.
To create these two accounts we need:
-
The
customer_id
from above. For this example that isDANIELLEE001
. -
An
account_id
for each account that will be the unique identifier for that specific account. Here we’ll useDANIELLEE001_USD.001
as our fiat account id andDANIELLEE001_USDC.001
for our crypto account id. -
The
product_id
: This is the type of account to be opened. For the fiat account, it isDEPOSIT_FORT_FIAT
. For crypto, it isDEPOSIT_FORT_CRYPTO
. Different products have different capabilities. Click here to learn more about the various products we support. -
The
asset_type_id
: The asset type that the accounts are going to use. Our fiat account is in USD, so theasset_type_id
isFIAT_TESTNET_USD
. For the USDC account, theasset_type_id
isETHEREUM_GOERLI_USDC
. Click here to learn more about the various assets we support.
Creating our fiat account
For our fiat account, we’re going to call the /accounts/deposits
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": "DANIELLEE001",
"account_to_open": {
"account_id": "DANIELLEE001_USD.001",
"product_id": "DEPOSIT_FORT_FIAT",
"asset_type_id": "FIAT_TESTNET_USD"
}
}'
The response contains the account_id
that you passed in :
{
data: {
id: "DANIELLEE001_USD.001"
}
}
Again, we can use that account_id
and a GET
request to the accounts/deposits/${account_id}
endpoint to retrieve information about this account:
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/DANIELLEE001_USD.001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'
The response object will have the status of the account, product and asset type ids, and the current and available balance:
{
"data": {
"id": "DANIELLEE001_USD.001",
"status": "OPEN",
"asset_type_id": "FIAT_TESTNET_USD",
"product_id": "DEPOSIT_FORT_FIAT",
"current_balance": 0,
"available_balance": 0
}
}
Creating our crypto account
For our crypto account, we’re going to call the /accounts/deposits
endpoint as well:
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": "DANIELLEE001",
"account_to_open": {
"account_id": "DANIELLEE001_USDC.001",
"product_id": "DEPOSIT_FORT_CRYPTO",
"asset_type_id": "ETHEREUM_GOERLI_USDC"
}
}'
The response contains the account_id
that you passed in :
{
"data": {
"id": "DANIELLEE_USDC.001"
}
}
You can use that ACCOUNT_ID
and a GET
request to the /accounts/deposits/
endpoint to retrieve information about this account:
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/DANIELLEE001_USDC.001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'
The response object will have the status of the account, product and asset type ids, and the current and available balance:
{
"data": {
"id": "DANIELLEE001_USDC.001",
"status": "OPEN",
"asset_type_id": "ETHEREUM_GOERLI_USDC",
"product_id": "DEPOSIT_FORT_CRYPTO",
"current_balance": 0,
"available_balance": 0
}
}
That’s it for creating our accounts. Now let's start transacting!
3. Depositing fiat
Next, we have to deposit fiat. you can do that by calling 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-raw '{
"deposit_type": "PUSH",
"deposit_destination": {
"destination_account_id": "DANIELLEE001_USD.001"
}
}'
You will get back the following response with the fiat deposit instructions that you can supply to your customers. Once the fiat funds are deposited, the customer account will be credited.
You will notice 2 memo fields under the deposit_instructions
. One is under the ACH instruction_type
and the other is under the FEDWIRE instruction_type
. Save one of those memo fields as you will need it to siumulate the deposit in Sandbox.
{
"data": {
"id": "90804565-effe-4fd4-8fb7-61b40eaa88c9",
"status": "REQUESTED",
"created_timestamp": "2023-02-20T12:09:26.63017-05:00",
"destination_details": {
"destination_account_id": "DANIELLEE001_USD.001",
"asset_type_id": "FIAT_TESTNET_USD"
},
"deposit_instructions": [
{
"instruction_type": "ACH",
"account_holder_name": "Joe Bloggs",
"account_number": "9695859732",
"account_routing_number": "715960587",
"memo": "15218d1f-6651-4f62-812c-2e4aaa5c241c",
"asset_type_id": "FIAT_TESTNET_USD"
},
{
"instruction_type": "FEDWIRE",
"account_holder_name": "Joe Bloggs",
"account_number": "9695859732",
"account_routing_number": "715960587",
"account_holder_address": {
"unit_number": null,
"address_line1": "123 Sample St",
"address_line2": null,
"address_line3": null,
"city": "Boston",
"state": "MA",
"postal_code": "02135",
"country_code": "US"
},
"institution_address": {
"unit_number": null,
"address_line1": "1 Financial Place",
"address_line2": "Floor 34",
"address_line3": null,
"city": "Boston",
"state": "MA",
"postal_code": "02135",
"country_code": "US"
},
"memo": "ea947e78-50ba-40b0-8e6e-32345eeb2cab",
"asset_type_id": "FIAT_TESTNET_USD"
}
]
}
}
To simulate the deposit in sandbox, Login to the Management UI using your provided organization id and credentials. Navigate to the Customers screen and click on the 'Create manual deposit' on the right.
Enter one of the memo
entries from the above response object and 1000 as the amount you want to deposit.
Click on the 'Confirm' button and your funds will now be deposited within a few minutes into the account in sandbox.
After a few minutes, if you query the account using accounts/deposits/{ACCOUNT_ID}
endpoint, you will see the current and available balance reflecing the $1,000 you just added.
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/DANIELLEE001_USD.001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'
The response object will reflect the available_balance
of 1000.
{
"data": {
"id": "DANIELLEE001_USD.001",
"status": "OPEN",
"asset_type_id": "FIAT_TESTNET_USD",
"product_id": "DEPOSIT_FORT_FIAT",
"current_balance": 1000,
"available_balance": 1000
}
}
You can also check the account balance through the Management UI, by navigating to the customer and then the account for the customer.
Now that the USD is in the fiat account, lets exchange it for some USDC!
4. Exchanging fiat for crypto
To exchange the fiat to crypto, you have to call the /exchanges/market
endpoint with a sell order, passing in the USD account as the source, and the USDC account as the destination.
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/exchanges/market' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
"source_account_id": "DANIELLEE001_USD.001",
"destination_account_id": "DANIELLEE001_USDC.001",
"amount": 1000,
"action": "FIX_SOURCE"
}'
This will give us the response with the details of the REQUESTED
exchange. Save the id
of the exchange. You will need it for the EXCHANGE_ID
in the next operation:
{
"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": "DANIELLEE001_USD.001",
"asset_type_id": "FIAT_TESTNET_USD",
"amount_to_debit": 1000
},
"destination_details": {
"destination_account_id": "DANIELLEE001_USDC.001",
'asset_type_id': 'ETHEREUM_GOERLI_USDC',
'amount_to_credit': 1000
}
}
}
We can use the id
in the URL 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-11T09:32:14.450333",
"action": "SELL",
"source_details'": {
"source_account_id": "DANIELLEE001_USD.001",
"asset_type_id": "FIAT_TESTNET_USD",
"amount_to_debit": 1000
},
"destination_details": {
"destination_account_id": "DANIELLEE001_USDC.001",
'asset_type_id': 'ETHEREUM_GOERLI_USDC',
'amount_to_credit': 1000
}
}
If it’s not accepted, you might have to increase your maximum_slippage
.
The USDC funds are now in the USDC account, DANIELLEE001_USDC.001, but the funds have not yet settled. You can query account to check its balance with the /accounts/deposits
endpoint.
curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/DANIELLEE001_USDC.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": "DANIELLEE001_USDC.001",
"status": "OPEN",
"asset_type_id": "ETHEREUM_GOERLI_USDC",
"product_id": "DEPOSIT_FORT_CRYPTO",
"current_balance": 1000.000000000000000000,
"available_balance": 0
}
}
NOTE: the availablebalance above is showing as 0. In Sandbox, we don't have settlement on exchange yet. In Production, the `availablebalance` will show 0 until settlement completes.
Now that exchange is complete, lets see how to withdraw USDC funds to an external wallet.
5. Withdrawing crypto
As mentioned above, we don't have settlement in Sandbox yet. So you can't withdraw the USDC funds that you just bought. To test out the withdrawal functionality in Sandbox, you have to first add some USDC funds into the USDC deposit account.
Depositing USDC funds
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": "DANIELLEE001_USDC.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": "DANIELLEE001_USDC.001",
"asset_type_id": "ETHEREUM_GOERLI_USDC"
},
"deposit_instructions": [
{
"instruction_type": "CRYPTO",
"address": "0x9ED9961A87ba49511Cf522059df52e2a94eF188b",
"blockchain": "ethereum",
"network": "goerli",
"asset_type_id": "ETHEREUM_GOERLI_USDC"
}
]
}
}
You can deposit funds into the address
using any Goerli faucet, such as the Alchemy Goerli faucet (requires signup) or All That Node (no signup needed). You can also send us 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/DANIELLEE001_USDC.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. The available_balance should now reflect amount you added as well:
{
"data": {
"id": "DANIELLEE001_USDC.001",
"status": "OPEN",
"asset_type_id": "ETHEREUM_GOERLI_USDC",
"product_id": "DEPOSIT_FORT_CRYPTO",
"current_balance": 1050.000000000000000000,
"available_balance": 50.000000000000000000
}
}
Once you see the available_balance
updated, the USDC funds are now available to withdraw.
If the available_balance
is not updated, the transaction is still going through all the blockchain confirmations, AML and Fraud checks. You can check on the status of the transactions by querying the accounts/deposits/{ACCOUNT_ID}/transactions
endpoint. If you give it a few minutes, it will appear.
Withdrawing crypto funds to external wallet
Withdrawing funds has 3 steps: create a counterparty, setup the withdrawal and accept the withdrawal.
Create a Counterparty
To create the counterparty, call the counterparties
endpoint.
Set the blockchain_address
to the address you want to withdraw to.
curl --location 'https://sandbox.layer2financial.com/api/v1/counterparties' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": "DANIELLEE001",
"description": "sample counterparty",
"counterparty_type": "CRYPTO",
"is_international": false,
"supported_rails": ["CRYPTO"],
"profile": {
"name":"tarun",
"address":{
"country_code": "US"
},
"relationship_to_customer": "SELF"
},
"wallet_information": {
"asset_type_id": "ETHEREUM_GOERLI_USDC",
"blockchain_address": {BLOCKCHAIN_ADDRESS},
"wallet_type": "OTHER",
"institution_name": "SUPERCUSTODY",
"institution_address": {
"country_code":"US"
}
}
}'
A successful 200 response as follows will provide you the id, which is the counterparty_id you will need to setup the withdrawal.
{
"data": {
"id": "137df787-f903-4f0a-9ac0-f456d60bfb71"
}
}
Setup the Withdrawal
To setup the withdrawal, use the /withdrawals
endpoint by passing the above id as the COUNTERPARTY_ID
.
curl --location 'https://sandbox.layer2financial.com/api/v1/withdrawals' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"withdrawal_rail": "CRYPTO",
"description": "Withdraw crypto",
"source_account_id": "DANIELLEE001_USDC.001",
"amount":"3",
"destination_counterparty_id": "{COUNTERPARTY_ID}",
"memo": "withdrawal"
}'
A 200
response will give you the id of the withdrawal. This will be the WITHDRAWAL_ID
in the subsequent step.
{
"data": {
"id": "887804df-13c2-4019-835b-08c92ed77c72",
"status": "REQUESTED"
}
}
Accept Withdrawal
Now its time to accept the withdrawal, by calling the withdrawals/{WITHDRAWAL_ID}/accept
endpoint.
curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/withdrawals/{WITHDRAWAL_ID}/accept' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
If its successful, you will get the following response.
{
'data': {
"id":"c67e81e5-3784-45d7-a2b9-b0c7f19a466f",
"status":"ACCEPTED",
}
}
And thats it, we take care of gas management, sweeping, etc behind the scenes.
Summary
Let’s review:
-
Authenticate
-Authenticate using the OAuth Endpoint to get the
AUTH_TOKEN
. -
Create accounts for the customer
- Create a USD deposit account (
DEPOSIT_FORT_FIAT
product type andFIAT_TESTNET_USD
asset type) and USDC deposit account (DEPOSIT_FORT_CRYPTO
product type andETHEREUM_GOERLI_USDC
) using theaccounts/deposits
endpoint . -
Send funds into Deposit account
- Setup a deposit into the USD deposit account and get deposit instructions using the
deposits
endpoint . This will give you amemo
in the deposit instructions. You will need thismemo
to simulate a fiat deposit in Sandbox, using the Management UI . -
Exchange to crypto
- Setup the USD to USDC exchange using the
exchanges
endpoint . You have to accept the exchange using theexchanges/{EXCHANGE_ID}/accept
endpoint . -
Withdraw crypo
- Setup the USDC withdrawal to an external wallet using the
withdrawals
endpoint . You have to accept the withdrawal using thewithdrawals/{WITHDRAWAL_ID}/accept
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 via slack, website or email support@layer2financial.com.