# Wallet as a Service 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!* buysellhold ## 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](#1-authenticating-your-requests) 2. [Created an account for an onboarded customer](#2-creating-an-account) 3. [Generated a deposit address for the account](#3-generating-a-deposit-address) 4. [Deposited funds into the account](#4-depositing-your-funds) 5. [Transferred funds between two accounts](#5-transferring-funds-between-accounts) 6. [Sold these funds to fiat through an exchange](#6-exchanging-funds) Once you are done with this quick start guide, head over to our our [API documentation](/api-docs/openapi/rail-spec) 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: ```bash 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:` ```javascript { "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](/guides/authentication). 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](/guides/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](/guides/externalidentifiers) 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](/guides/accounts) to learn more about Accounts. Different products have different capabilities. [Click here](/guides/products) 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](/guides/assets) 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: ```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": "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: ```jsx { 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: ```bash 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: ```javascript { 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: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' \ ``` The response: ```javascript { 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. ```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": "QUORUM001.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": "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](https://goerlifaucet.com/) (requires signup) or [All That Node](https://www.allthatnode.com/faucet/ethereum.dsrv) (no signup needed). You can also send 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/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: ```javascript { "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. ```bash 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: ```javascript { '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! ```jsx { 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. Transferring 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: ```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": "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: ```jsx { 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](/api-docs/openapi/rail-spec#operation/createTransfer). Now we are set to transfer funds between the two accounts: ```bash 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: ```jsx { 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: ```bash 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`: ```jsx { data: { id: "38c24bb4-c117-4bb2-951f-efd69c83a7d3", status: "ACCEPTED" } } ``` Query our second account and we’ll see the funds: ```jsx { 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: ```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": "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. ```bash 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: ```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': '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: ```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-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 ```bash 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](/api-docs/openapi/rail-spec#operation/openAccount). 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](/api-docs/openapi/rail-spec#operation/createDeposits). 4. Deposit funds using external faucets to the `address` and query the balance of the the account using the [`accounts/deposits/{account_id}` endpoint](/api-docs/openapi/rail-spec#operation/getAccount) 5. Transfer funds between accounts using the [`transfers` endpoint](/api-docs/openapi/rail-spec#operation/createTransfer). 6. Exchange funds with the [`exchanges` endpoint](/api-docs/openapi/rail-spec#operation/createExchangeMarket). To dive deeper into what you can do on the Rail platform, head to our [API documentation](/api-docs/openapi/rail-spec).