This guide describes how to add Solana's native token SOL to your cryptocurrency exchange.
We highly recommend setting up at least two of your own Solana api nodes to give you a trusted entrypoint to the network, allow you full control over how much data is retained, and ensure you do not miss any data if one node fails.
To run an api node:
- Install the Solana command-line tool suite
- Start the validator with at least the following parameters:
--ledger to your desired ledger storage location, and
--rpc-port to the port you want to expose.
--expected-genesis-hash parameters are all specific to the cluster you are joining.
Current parameters for Mainnet Beta
--limit-ledger-size parameter allows you to specify how many ledger shreds your node retains on disk. If you do not include this parameter, the validator will keep the entire ledger until it runs out of disk space. The default value is good for at least a couple days but larger values may be used by adding an argument to
--limit-ledger-size if desired. Check
solana-validator --help for the default limit value used by
Specifying one or more
--trusted-validator parameters can protect you from booting from a malicious snapshot. More on the value of booting with trusted validators
Optional parameters to consider:
--private-rpcprevents your RPC port from being published for use by other nodes
--rpc-bind-addressallows you to specify a different IP address to bind the RPC port
We recommend configuring each of your nodes to restart automatically on exit, to ensure you miss as little data as possible. Running the solana software as a systemd service is one great option.
By default, each of your nodes will boot from a snapshot provided by one of your
trusted validators. This snapshot reflects the current state of the chain, but
does not contain the complete historical ledger. If one of your node exits and
boots from a new snapshot, there may be a gap in the ledger on that node. In
order to prevent this issue, add the
--no-snapshot-fetch parameter to your
solana-validator command to receive historical ledger data instead of a
Do not pass the
--no-snapshot-fetch parameter on your initial boot as it's not
possible to boot the node all the way from the genesis block. Instead boot from
a snapshot first and then add the
--no-snapshot-fetch parameter for reboots.
It is important to note that the amount of historical ledger available to your nodes is limited to what your trusted validators retain. You will need to ensure your nodes do not experience downtimes longer than this span, if ledger continuity is crucial for you.
Minimizing Validator Port Exposure
The validator requires that various UDP and TCP ports be open for inbound traffic from all other Solana validators. While this is the most efficient mode of operation, and is strongly recommended, it is possible to restrict the validator to only require inbound traffic from one other Solana validator.
First add the
--restricted-repair-only-mode argument. This will cause the
validator to operate in a restricted mode where it will not receive pushes from
the rest of the validators, and instead will need to continually poll other
validators for blocks. The validator will only transmit UDP packets to other
validators using the Gossip and ServeR ("serve repair") ports, and only
receive UDP packets on its Gossip and Repair ports.
The Gossip port is bi-directional and allows your validator to remain in contact with the rest of the cluster. Your validator transmits on the ServeR to make repair requests to obtaining new blocks from the rest of the network, since Turbine is now disabled. Your validator will then receive repair responses on the Repair port from other validators.
To further restrict the validator to only requesting blocks from one or more
validators, first determine the identity pubkey for that validator and add the
--gossip-pull-validator PUBKEY --repair-validator PUBKEY arguments for each
PUBKEY. This will cause your validator to be a resource drain on each validator
that you add, so please do this sparingly and only after consulting with the
Your validator should now only be communicating with the explicitly listed validators and only on the Gossip, Repair and ServeR ports.
Setting up Deposit Accounts
Solana accounts do not require any on-chain initialization; once they contain some SOL, they exist. To set up a deposit account for your exchange, simply generate a Solana keypair using any of our wallet tools.
We recommend using a unique deposit account for each of your users.
Solana accounts are charged rent on creation and once per
epoch, but they can be made rent-exempt if they contain 2-years worth of rent in
SOL. In order to find the minimum rent-exempt balance for your deposit accounts,
You may wish to keep the keys for one or more collection accounts offline for greater security. If so, you will need to move SOL to hot accounts using our offline methods.
Listening for Deposits
When a user wants to deposit SOL into your exchange, instruct them to send a transfer to the appropriate deposit address.
Poll for Blocks
The easiest way to track all the deposit accounts for your exchange is to poll for each confirmed block and inspect for addresses of interest, using the JSON-RPC service of your Solana api node.
- To identify which blocks are available, send a
getConfirmedBlocksrequest, passing the last block you have already processed as the start-slot parameter:
Not every slot produces a block, so there may be gaps in the sequence of integers.
- For each block, request its contents with a
postBalances fields allow you to track the balance
changes in every account without having to parse the entire transaction. They
list the starting and ending balances of each account in
lamports, indexed to the
accountKeys list. For
example, if the deposit address if interest is
47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi, this transaction represents a
transfer of 218099990000 - 207099990000 = 11000000000 lamports = 11 SOL
You can also query the transaction history of a specific address.
- Send a
getConfirmedSignaturesForAddressrequest to the api node, specifying a range of recent slots:
- For each signature returned, get the transaction details by sending a
To accommodate a user's request to withdraw SOL, you must generate a Solana transfer transaction, and send it to the api node to be forwarded to your cluster.
Sending a synchronous transfer to the Solana cluster allows you to easily ensure that a transfer is successful and finalized by the cluster.
Solana's command-line tool offers a simple command,
solana transfer, to
generate, submit, and confirm transfer transactions. By default, this method
will wait and track progress on stderr until the transaction has been finalized
by the cluster. If the transaction fails, it will report any transaction errors.
offers a similar approach for the JS ecosystem. Use the
SystemProgram to build
a transfer transaction, and submit it using the
For greater flexibility, you can submit withdrawal transfers asynchronously. In these cases, it is your responsibility to verify that the transaction succeeded and was finalized by the cluster.
Note: Each transaction contains a recent blockhash to indicate its liveness. It is critical to wait until this blockhash expires before retrying a withdrawal transfer that does not appear to have been confirmed or finalized by the cluster. Otherwise, you risk a double spend. See more on blockhash expiration below.
First, get a recent blockhash using the
or the CLI command:
In the command-line tool, pass the
--no-wait argument to send a transfer
asynchronously, and include your recent blockhash with the
You can also build, sign, and serialize the transaction manually, and fire it off to
the cluster using the JSON-RPC
Transaction Confirmations & Finality
Get the status of a batch of transactions using the
getSignatureStatuses JSON-RPC endpoint.
confirmations field reports how many
confirmed blocks have elapsed since the
transaction was processed. If
confirmations: null, it is finalized.
When you request a recent blockhash for your withdrawal transaction using the
getFees endpoint or
solana fees, the
response will include the
lastValidSlot, the last slot in which the blockhash
will be valid. You can check the cluster slot with a
getSlot query; once the cluster slot is
lastValidSlot, the withdrawal transaction using that blockhash
should never succeed.
You can also doublecheck whether a particular blockhash is still valid by sending a
request with the blockhash as a parameter. If the response value is null, the
blockhash is expired, and the withdrawal transaction should never succeed.
Validating User-supplied Account Addresses for Withdrawals
As withdrawals are irreversible, it may be a good practice to validate a user-supplied account address before authorizing a withdrawal in order to prevent accidental loss of user funds.
The address of a normal account in Solana is a Base58-encoded string of a 256-bit ed25519 public key. Not all bit patterns are valid public keys for the ed25519 curve, so it is possible to ensure user-supplied account addresses are at least correct ed25519 public keys.
Here is a Java example of validating a user-supplied address as a valid ed25519 public key:
The following code sample assumes you're using the Maven.
Supporting the SPL Token Standard
SPL Token is the standard for wrapped/synthetic token creation and exchange on the Solana blockchain.
The SPL Token workflow is similar to that of native SOL tokens, but there are a few differences which will be discussed in this section.
Each type of SPL Token is declared by creating a mint account. This account stores metadata describing token features like the supply, number of decimals, and various authorities with control over the mint. Each SPL Token account references its associated mint and may only interact with SPL Tokens of that type.
spl-token CLI Tool
SPL Token accounts are queried and modified using the
spl-token command line
utility. The examples provided in this section depend upon having it installed
on the local system.
spl-token is distributed from Rust crates.io via the Rust
cargo command line utility. The latest version of
cargo can be installed
using a handy one-liner for your platform at rustup.rs. Once
cargo is installed,
spl-token can be obtained with the following command:
You can then check the installed version to verify
Which should result in something like
SPL Token accounts carry additional requirements that native System Program accounts do not:
- SPL Token accounts are not implicitly created, so must be created explicitly before an SPL Token balance can be deposited
- SPL Token accounts must remain rent-exempt for the duration of their existence and therefore require a small amount of native SOL tokens be deposited at account creation. For SPL Token v2 accounts, this amount is 0.00203928 SOL (2,039,280 lamports).
To create an SPL Token account with the following properties:
- At a random address
- Associated with the given mint
- Owned by the funding account's keypair
Checking an Account's Balance
For SPL Token transfers to succeed, a few prerequisite conditions must be met:
- The recipient account must exist before the transfer is executed. As described in account creation, SPL Token accounts are not explicitly created.
- Both the sender and recipient accounts must belong to the same mint. SPL Token accounts can only hold one type of SPL token.
(user, mint) pair requires a separate account on chain, it is
recommended that an exchange create batches of token accounts in advance and assign them
to users on request. These accounts should all be owned by exchange-controlled
Monitoring for deposit transactions should follow the block polling method described above. Each new block should be scanned for successful transactions issuing SPL Token Transfer or Transfer2 instructions referencing user accounts, then querying the token account balance updates.
Considerations are being
made to exend the
postBalance transaction status metadata
fields to include SPL Token balance transfers.
The withdrawal address a user provides must point to an initialized SPL Token account of the correct mint. Before executing a withdrawal transfer, it is recommended that the exchange check the address as described above as well as query the account to verify its existence and that it belongs to the correct mint.
For regulatory compliance reasons, an SPL Token issuing entity may optionally choose to hold "Freeze Authority" over all accounts created in association with its mint. This allows them to freeze the assets in a given account at will, rendering the account unusable until thawed. If this feature is in use, the freeze authority's pubkey will be registered in the SPL Token's mint account.
Testing the Integration
Be sure to test your complete workflow on Solana devnet and testnet
clusters before moving to production on mainnet-beta. Devnet
is the most open and flexible, and ideal for initial development, while testnet
offers more realistic cluster configuration. Both devnet and testnet support a faucet,
solana airdrop 10 to obtain some devnet or testnet SOL for developement and testing.