# On-ramp Use Case Build Guide *If you haven’t already, [Sign Up for Sandbox Access](https://rail.io/contact-us) 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](/guides/assets) 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. On-Ramp Flow Diagram ## 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: 1. [Authenticated your request](#1--authenticating-your-request) 2. [Created the fiat and crypto accounts for the customer](#2-creating-the-fiat-and-crypto-accounts) 3. [Deposited the fiat](#3-depositing-fiat) 4. [Sold fiat for crypto](#4-exchanging-fiat-for-crypto) 5. [Withdrawn crypto to an external wallet](#5-withdrawing-crypto) 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: ```bash 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:` ```javascript { "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](/guides/authentication). 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](/guides/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 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. Use `DEPOSIT_BASIC` - 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`. For the USDC account, the `asset_type_id` is `BITCOIN_TESTNET_BTC`. [Click here](/guides/assets) 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: ```bash 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_BASIC", "asset_type_id": "FIAT_TESTNET_USD" } }' ``` The response contains the `account_id` that you passed in : ```jsx { 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: ```bash 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: ```jsx { "data": { "id": "DANIELLEE001_USD.001", "status": "OPEN", "asset_type_id": "FIAT_TESTNET_USD", "product_id": "DEPOSIT_BASIC", "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: ```bash 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_BASIC", "asset_type_id": "ETHEREUM_GOERLI_USDC" } }' ``` The response contains the `account_id` that you passed in : ```javascript { "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: ```bash 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: ```jsx { "data": { "id": "DANIELLEE001_USDC.001", "status": "OPEN", "asset_type_id": "ETHEREUM_GOERLI_USDC", "product_id": "DEPOSIT_BASIC", "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. ```bash 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. ```javascript { "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](https://management-sandbox.layer2financial.com/) 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. Management UI Deposit Entry Click on the 'Confirm' button and your funds will now be deposited within a few minutes into the account in sandbox. Management UI Deposit Confirmation 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. ```bash 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. ```jsx { "data": { "id": "DANIELLEE001_USD.001", "status": "OPEN", "asset_type_id": "FIAT_TESTNET_USD", "product_id": "DEPOSIT_BASIC", "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. ```bash 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: ```jsx { "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: ```bash 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](/api-docs/openapi/rail-spec#operation/acceptExchange). If the exchange is accepted, you’ll get this response: ```jsx "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. ```bash 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: ```javascript { "data": { "id": "DANIELLEE001_USDC.001", "status": "OPEN", "asset_type_id": "ETHEREUM_GOERLI_USDC", "product_id": "DEPOSIT_BASIC", "current_balance": 1000.000000000000000000, "available_balance": 0 } } ``` > NOTE: the available_balance above is showing as 0. In Sandbox, we don't have settlement on exchange yet. In Production, the `available_balance` 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. ```bash 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](/guides/deposits) 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](https://goerlifaucet.com/) (requires signup) or [All That Node](https://www.allthatnode.com/faucet/ethereum.dsrv) (no signup needed). You can also send us a note at [support@layer2financial.com](mailto: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: ```bash 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: ```javascript { "data": { "id": "DANIELLEE001_USDC.001", "status": "OPEN", "asset_type_id": "ETHEREUM_GOERLI_USDC", "product_id": "DEPOSIT_BASIC", "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](/api-docs/openapi/rail-spec#operation/createCounterparty). Set the `blockchain_address` to the address you want to withdraw to. ```bash 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. ```javascript { "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`. ```bash 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. ```javascript { "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. ```bash 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. ```javascript { '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 accounts for the customer** - Create a USD deposit account (`DEPOSIT_BASIC` product type and `FIAT_TESTNET_USD` asset type) and USDC deposit account (`DEPOSIT_BASIC` product type and `ETHEREUM_GOERLI_USDC`) using the [`accounts/deposits` endpoint](/api-docs/openapi/rail-spec#operation/openAccount). 3. **Send funds into Deposit account** - Setup a deposit into the USD deposit account and get deposit instructions using the [`deposits` endpoint](/api-docs/openapi/rail-spec#operation/createDeposits). 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](https://management-sandbox.layer2financial.com). 4. **Exchange to crypto** - Setup the USD to USDC exchange using the [`exchanges` endpoint](/api-docs/openapi/rail-spec#operation/createExchangeMarket). You have to accept the exchange using the [`exchanges/{EXCHANGE_ID}/accept` endpoint](/api-docs/openapi/rail-spec#operation/acceptExchange). 5. **Withdraw crypo** - Setup the USDC withdrawal to an external wallet using the [`withdrawals` endpoint](/api-docs/openapi/rail-spec#operation/createWithdrawal). You have to accept the withdrawal using the [`withdrawals/{WITHDRAWAL_ID}/accept` endpoint](/api-docs/openapi/rail-spec#operation/acceptWithdrawal) To dive deeper into what you can do on the Rail platform, head to our [API documentation](/api-docs/openapi/rail-spec).