This repository has been archived by the owner on Jan 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Tyera Eulberg
committed
May 14, 2020
1 parent
d195dce
commit ee0c675
Showing
3 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
# Add Solana to Your Exchange | ||
|
||
This guide describes how to add Solana's native token SOL to your cryptocurrency | ||
exchange. | ||
|
||
## Node Setup | ||
|
||
We highly recommend setting up at least one of your own Solana api nodes to | ||
give you a trusted entrypoint to the network and allow you full control over how | ||
much data is retained. | ||
|
||
To run an api node: | ||
1. [Install the Solana command-line tool suite](../cli/install-solana-cli-tools.md) | ||
2. Boot the node with at least the following parameters: | ||
```bash | ||
solana-validator \ | ||
--ledger <LEDGER_PATH> \ | ||
--entrypoint <CLUSTER_ENTRYPOINT> \ | ||
--expected-genesis-hash <EXPECTED_GENESIS_HASH> \ | ||
--expected-shred-version <EXPECTED_SHRED_VERSION> \ | ||
--rpc-port 8899 \ | ||
--no-voting \ | ||
--enable-rpc-transaction-history | ||
``` | ||
|
||
Customize `--ledger` to your desired ledger storage location, and `--rpc-port` to the port you want to expose. | ||
|
||
The `--entrypoint`, `--expected-genesis-hash`, and `--expected-shred-version` parameters are all specific to the cluster you are joining. | ||
[Current parameters for Mainnet Beta](../clusters.md#example-solana-validator-command-line-2) | ||
|
||
Optional parameters to consider: | ||
- `--limit-ledger-size` specifies how many ledger shreds to retain on disk. If you do not include this parameter, the ledger will keep the entire ledger until it runs out of disk space. A larger value like `--limit-ledger-size 250000000000` is good for a couple days | ||
- `--trusted-validator` can protect you from booting from a malicious snapshot. [More on the value of booting with trusted validators](../running-validator/validator-start.md#trusted-validators) | ||
|
||
## 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](../wallet-guide/cli.md). | ||
|
||
We recommend using a unique deposit account for each of your users. | ||
|
||
## 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 the Solana api node. | ||
|
||
1. To identify which blocks are available, send a [`getConfirmedBlocks` request](../apps/jsonrpc-api.md#getconfirmedblocks), | ||
passing the last block you have already processed as the start-slot parameter: | ||
|
||
```bash | ||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocks","params":[5]}' localhost:8899 | ||
|
||
{"jsonrpc":"2.0","result":[5,6,8,9,11],"id":1} | ||
``` | ||
Not every slot produces a block, so there may be gaps in the sequence of integers. | ||
|
||
2. For each block, request its contents with a [`getConfirmedBlock` request](../apps/jsonrpc-api.md#getconfirmedblock): | ||
|
||
```bash | ||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[5, "json"]}' localhost:8899 | ||
|
||
{"jsonrpc":"2.0","result":{"blockhash":"2WcrsKSVANoe6xQHKtCcqNdUpCQPQ3vb6QTgi1dcE2oL","parentSlot":4,"previousBlockhash":"7ZDoGW83nXgP14vnn9XhGSaGjbuLdLWkQAoUQ7pg6qDZ","rewards":[],"transactions":[{"meta":{"err":null,"fee":5000,"postBalances":[2033973061360,218099990000,42000000003],"preBalances":[2044973066360,207099990000,42000000003],"status":{"Ok":null}},"transaction":{"message":{"accountKeys":["Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX","47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi","11111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":1,"numRequiredSignatures":1},"instructions":[{"accounts":[0,1],"data":"3Bxs3zyH82bhpB8j","programIdIndex":2}],"recentBlockhash":"7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh"},"signatures":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6"]}}]},"id":1} | ||
``` | ||
|
||
The `preBalances` and `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](../terminology.md#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 | ||
|
||
If you need more information about the transaction type or other specifics, you | ||
can request the block in binary format, and parse it using either our | ||
[Rust SDK](https://github.com/solana-labs/solana/tree/master/sdk) or | ||
[Javascript SDK](https://github.com/solana-labs/solana-web3.js). | ||
|
||
### Address History | ||
|
||
You can also query the transaction history of a specific address. | ||
|
||
1. Send a [`getConfirmedSignaturesForAddress`](../apps/jsonrpc-api.md#getconfirmedsignaturesforaddress) | ||
request to the api node, specifying a range of recent slots: | ||
|
||
```bash | ||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedSignaturesForAddress","params":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC", 0, 10]}' localhost:8899 | ||
|
||
{"jsonrpc":"2.0","result":{["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4bJdGN8Tt2kLWZ3Fa1dpwPSEkXWWTSszPSf1rRVsCwNjxbbUdwTeiWtmi8soA26YmwnKD4aAxNp8ci1Gjpdv4gsr","dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6"]},"id":1} | ||
``` | ||
|
||
2. For each signature returned, get the transaction details by sending a | ||
[`getConfirmedTransaction`](../apps/jsonrpc-api.md#getconfirmedtransaction) request: | ||
|
||
```bash | ||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedTransaction","params":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6", "json"]}' localhost:8899 | ||
|
||
// Result | ||
{"jsonrpc":"2.0","result":{"slot":5,"transaction":{"message":{"accountKeys":["Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX","47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi","11111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":1,"numRequiredSignatures":1},"instructions":[{"accounts":[0,1],"data":"3Bxs3zyH82bhpB8j","programIdIndex":2}],"recentBlockhash":"7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh"},"signatures":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6"]},"meta":{"err":null,"fee":5000,"postBalances":[2033973061360,218099990000,42000000003],"preBalances":[2044973066360,207099990000,42000000003],"status":{"Ok":null}}},"id":1} | ||
``` | ||
|
||
## Sending Withdrawals | ||
|
||
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 the | ||
cluster. | ||
|
||
### Synchronous | ||
|
||
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. | ||
|
||
```bash | ||
solana transfer <USER_ADDRESS> <AMOUNT> --keypair <KEYPAIR> --url http://api.mainnet-beta.solana.com | ||
``` | ||
|
||
The [Solana Javascript SDK](https://github.com/solana-labs/solana-web3.js) | ||
offers a similar approach for the JS ecosystem. Use the `SystemProgram` to build | ||
a transfer transaction, and submit it using the `sendAndConfirmTransaction` | ||
method. | ||
|
||
### Asynchronous | ||
|
||
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](../transaction.md#blockhash-format) | ||
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. We | ||
recommend waiting 500 slots between retries. | ||
|
||
In the command-line tool, pass the `--no-wait` argument to send a transfer | ||
asynchronously: | ||
|
||
```bash | ||
solana transfer <USER_ADDRESS> <AMOUNT> --no-wait --keypair <KEYPAIR> --url http://api.mainnet-beta.solana.com | ||
``` | ||
|
||
You can also build and serialize the transaction manually, and fire it off to | ||
the cluster using the JSON-RPC [`sendTransaction` endpoint](../apps/jsonrpc-api.md#sendtransaction). | ||
|
||
#### Transaction Confirmations & Finality | ||
|
||
Get the status of a batch of transactions using the | ||
[`getSignatureStatuses` JSON-RPC endpoint](../apps/jsonrpc-api.md#getsignaturestatuses). | ||
The `confirmations` field reports how many | ||
[confirmed blocks](../terminology.md#confirmed-block) have elapsed since the | ||
transaction was processed. If `confirmations: null`, it is [finalized](../terminology.md#finality). | ||
|
||
```bash | ||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]}' http://localhost:8899 | ||
|
||
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "err": null, "status": {"Ok": null}}, {"slot": 48, "confirmations": null, "err": null, "status": {"Ok": null}}]},"id":1} | ||
``` | ||
|
||
#### Blockhash Expiration | ||
|
||
To check whether a recent blockhash is still valid, send a | ||
[`getFeeCalculatorForBlockhash`](../apps/jsonrpc-api.md#getfeecalculatorforblockhash) | ||
request with the blockhash as a parameter. If the response value is null, the | ||
blockhash is expired, and the withdrawal transaction is safe to retry. | ||
|
||
## Testing the Integration | ||
|
||
|
||
|
||
## Going Further | ||
|
||
- **Offline 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](../offline-signing/README.md). | ||
- |