Fiat 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 Fiat On Ramp use case?

With the fiat on ramp use case, you or your customers can safely and securely send in fiat, buy cryptocurrencies/stablecoins and hold or withdraw the crypto. 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, Credit Cards (coming soon), Debit Cards (coming soon). The digital assets can be custodied on the Layer2 platform with qualified custodians such as banks, trusts or can be withdrawn to external custodial or non-custodial wallets.

If you are not regulated, Layer2 and its partners can provide regulatory sponsorship and will have to conduct KYC/KYB on your customers. Regulatory sponsorship is only available in US, Canada today.

path

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 create a new customer, open USD and USDC accounts for the 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:

  1. Authenticated your request
  2. Created a customer
  3. Created the fiat and crypto accounts for the customer
  4. Deposited the fiat
  5. Sold fiat for crypto
  6. Withdrawn crypto to an external wallet

Let’s go!

1. Authenticating your request

Every request you make to a Layer2 Financial 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+exchanges:read+exchanges:write+withdrawals:read+withdrawals:write+deposits:read+deposits: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 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 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.
  • exchanges:read and exchanges:write so we can create an exchange operation and get information about it
  • deposits:read and deposits:write so we can create a deposit operation and get information about it
  • withrawals:read and withdrawals: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 our customer, 'Daniel Lee'.

2. Creating a customer

The first step is to create a customer account for the individual, in this case Daniel Lee. It is this customer object that will be assigned the fiat and crypto accounts

We are going to create a individual customer using the customers endpoint.

Note: The customers endpoint is for use by regulated entities. If you are using Layer2 for regulatory sponsorship, you will need to use the applications endpoint for onboarding, KYC. For this build guide, we will skip onboarding via the applications endpoint. See Applications for more information.

To create an individual customer, the data you need is:

  • The customer id . This is the unique identifier of your customer within the Layer2 system. It must be unique to you.
  • The customer_type . This is INDIVIDUAL .
  • The first_name of the Individual.
  • The last_name of the Individual.

In this example, we’re going to onboard a new customer called Daniel Lee. So we’ll set up his id as DANIELLEE001, his first_name as ‘Daniel' and last_name as 'Lee'.

Note: The id doesn’t have to include the customer name. It just has to be unique. You can use any current customer identifier from your own system. See External Entity Identifiers for more information.

Each of those attributes can be added to a data object and POSTed to the customers API endpoint.

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/api/v1/customers' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
"id": "DANIELLEE001",
"customer_type": "INDIVIDUAL",
"first_name": "Daniel",
"last_name":"Lee"
}'

A successful request will return a status code of 200 and a response object with the id of the new customer:

Copy
Copied
{
	'data': {
		'id': 'DANIELLEE001'
	}
}

Once you’ve created a customer, you can retrieve information about that customer at any time using a GET request to the same endpoint, appended with their CUSTOMER_ID:

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/api/v1/customers/DANIELLEE001' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json'

The response object:

Copy
Copied
{
    "data": {
        "id": "DANIELLEE001",
        "customer_type": "INDIVIDUAL",
        "first_name": "Daniel",
        "last_name": "Lee"
    }
}

Now we have our customer, the next step is to create fiat and crypto accounts.

3. 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 is DANIELLEE001 .
  • An account_id for each account that will be the unique identifier for that specific account. Here we’ll use DANIELLEE001_USD.001 as our fiat account id and DANIELLEE001_USDC.001 for our crypto account id.
  • The product_id : This is the type of account to be opened. For the fiat account, it is BASIC_DEPOSIT_FIAT . For crypto, it is BASIC_DEPOSIT .
  • The asset_type_id : The asset type that the accounts are going to use. Our fiat account is in USD, so the asset_type_id is FIAT_TESTNET_USD_T . For the USDC account, the asset_type_id is ETHEREUM_GOERLI_USDC_T .

NOTE: In Production, the assettypeid will be FIAT_MAINNET_USD AND ETHEREUM_MAINNET_USDC.

Creating our fiat account

For our fiat account, we’re going to call the /accounts/deposits endpoint:

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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": "BASIC_DEPOSIT_FIAT",
        "asset_type_id": "FIAT_TESTNET_USD_T"
    }        
}'

The response contains the account_id that you passed in :

Copy
Copied
{
	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:

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/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:

Copy
Copied
{
  "data": {
    "id": "DANIELLEE001_USD.001",
    "status": "OPEN",
    "asset_type_id": "FIAT_TESTNET_USD_T",
    "product_id": "BASIC_DEPOSIT_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:

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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": "BASIC_DEPOSIT",
        "asset_type_id": "ETHEREUM_GOERLI_USDC_T"
    }
}'

The response contains the account_id that you passed in :

Copy
Copied
{
	"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:

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/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:

Copy
Copied
{
  "data": {
    "id": "DANIELLEE001_USDC.001",
    "status": "OPEN",
    "asset_type_id": "ETHEREUM_GOERLI_USDC_T",
    "product_id": "BASIC_DEPOSIT",
    "current_balance": 0,
    "available_balance": 0
  }
}

That’s it for creating our accounts. Now let's start transacting!

4. Depositing fiat

Next, we have to deposit fiat. you can do that by calling the deposits endpoint.

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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.

Copy
Copied
{
    "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_T"
        },
        "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_T"
            },
            {
                "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_T"
            }
        ]
    }
}

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.

path

Click on the 'Confirm' button and your funds will now be deposited within a few minutes into the account in sandbox.

path

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.

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/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.

Copy
Copied
{
  "data": {
    "id": "DANIELLEE001_USD.001",
    "status": "OPEN",
    "asset_type_id": "FIAT_TESTNET_USD_T",
    "product_id": "BASIC_DEPOSIT_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!

5. Exchanging fiat for crypto

To exchange the fiat to crypto, you have to call the /exchanges/quote endpoint with a sell order, passing in the USD account as the source, and the USDC account as the destination.

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/api/v1/exchanges/quote' \
--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": "SELL"
}'

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:

Copy
Copied
{
	"data": {
		**"id": "c3ac732e-fae0-4585-a7c3-71e24b8e7293"**, 
		"status": "REQUESTED", 
		"created_timestamp": "2022-11-11T09:32:14.450333", 
		"action": "SELL", 
		"source_details'": {
			"source_account_id": "DANIELLEE001_USD.001", 
			"asset_type_id": "FIAT_TESTNET_USD_T", 
			"amount_to_debit": 1000
		}, 
		"destination_details": {
			"destination_account_id": "DANIELLEE001_USDC.001", 
			'asset_type_id': 'ETHEREUM_GOERLI_USDC_T', 
			'amount_to_credit': 1000
		}
	}
}

We can use the id in the URL to accept this exchange:

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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-11T09:32:14.450333", 
		"action": "SELL", 
		"source_details'": {
			"source_account_id": "DANIELLEE001_USD.001", 
			"asset_type_id": "FIAT_TESTNET_USD_T", 
			"amount_to_debit": 1000
		}, 
		"destination_details": {
			"destination_account_id": "DANIELLEE001_USDC.001", 
			'asset_type_id': 'ETHEREUM_GOERLI_USDC_T', 
			'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.

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/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:

Copy
Copied
{
  "data": {
    "id": "DANIELLEE001_USDC.001",
    "status": "OPEN",
    "asset_type_id": "ETHEREUM_GOERLI_USDC_T",
    "product_id": "BASIC_DEPOSIT",
    "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.

6. 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.

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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.

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": "DANIELLEE001_USDC.001",
            "asset_type_id": "ETHEREUM_GOERLI_USDC_T"
        },
        "deposit_instructions": [
            {
                "instruction_type": "CRYPTO",
                "address": "0x9ED9961A87ba49511Cf522059df52e2a94eF188b",
                "blockchain": "ethereum",
                "network": "goerli",
                "asset_type_id": "ETHEREUM_GOERLI_USDC_T"
            }
        ]
    }
}

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:

Copy
Copied
curl --location --request GET 'https://alpha.layer2financial.dev/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:

Copy
Copied
{
  "data": {
    "id": "DANIELLEE001_USDC.001",
    "status": "OPEN",
    "asset_type_id": "ETHEREUM_GOERLI_USDC_T",
    "product_id": "BASIC_DEPOSIT",
    "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.

Copy
Copied
curl --location 'https://alpha.layer2financial.dev/api/v1/counterparties' \
--header 'Authorization: Bearer {AUTH_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
    "customer_id": "DANIELLEE001",
    "counterparty_type": "CRYPTO",
    "is_international": false,
    "supported_rails": ["CRYPTO"],
    "profile": {
        "crypto_counterparty_type": "SOURCE_ACCOUNT_HOLDER"
    },
    "wallet_information": {
        "asset_type_id": "ETHEREUM_GOERLI_USDC_T",
        "blockchain_address": "0x314e60586245a3A00eb1f1322cA26b02C28660D1",
        "wallet_type": "OTHER"
    }
}'

A successful 200 response as follows will provide you the id, which is the counterparty_id you will need to setup the withdrawal.

Copy
Copied
{
    "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.

Copy
Copied
curl --location 'https://alpha.layer2financial.dev/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.

Copy
Copied
{
    "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.

Copy
Copied
curl --location --request POST 'https://alpha.layer2financial.dev/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.

Copy
Copied
{
	'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:

  1. Authenticate -Authenticate using the OAuth Endpoint to get the AUTH_TOKEN .
  2. Create a customer - Create a customer using the customers endpoint
  3. Create accounts for the customer - Create a USD deposit account ( BASIC_DEPOSIT_FIAT product type and FIAT_TESTNET_USD_T asset type) and USDC deposit account ( BASIC_DEPOSIT product type and ETHEREUM_GOERLI_USDC_T ) using the accounts/deposits endpoint .
  4. 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 a memo in the deposit instructions. You will need this memo to simulate a fiat deposit in Sandbox, using the Management UI .
  5. Exchange to crypto - Setup the USD to USDC exchange using the exchanges endpoint . You have to accept the exchange using the exchanges/{EXCHANGE_ID}/accept endpoint .
  6. Withdraw crypo - Setup the USDC withdrawal to an external wallet using the withdrawals endpoint . You have to accept the withdrawal using the withdrawals/{WITHDRAWAL_ID}/accept endpoint

To dive deeper into what you can do on the Layer2 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.

© 2023 Layer2 Financial Inc. All Rights Reserved.