@ -24,12 +24,15 @@
|
||||
* [Command-line Reference](cli/usage.md)
|
||||
* [Solana Clusters](clusters.md)
|
||||
* [Develop Applications](apps/README.md)
|
||||
* [Accounts and Rent](apps/rent.md)
|
||||
* [Example: Web Wallet](apps/webwallet.md)
|
||||
* [Example: Tic-Tac-Toe](apps/tictactoe.md)
|
||||
* [Drones](apps/drones.md)
|
||||
* [Anatomy of a Transaction](transaction.md)
|
||||
* [JSON RPC API](apps/jsonrpc-api.md)
|
||||
* [JavaScript API](apps/javascript-api.md)
|
||||
* [Integration Guides](integrations/README.md)
|
||||
* [Exchange](integrations/exchange.md)
|
||||
* [Run a Validator](running-validator/README.md)
|
||||
* [Validator Requirements](running-validator/validator-reqs.md)
|
||||
* [Start a Validator](running-validator/validator-start.md)
|
||||
|
1
docs/src/apps/rent.md
Normal file
1
docs/src/apps/rent.md
Normal file
@ -0,0 +1 @@
|
||||
# Accounts and Rent
|
0
docs/src/integrations/README.md
Normal file
0
docs/src/integrations/README.md
Normal file
389
docs/src/integrations/exchange.md
Normal file
389
docs/src/integrations/exchange.md
Normal file
@ -0,0 +1,389 @@
|
||||
# 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 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:
|
||||
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 \
|
||||
--limit-ledger-size <SHRED_COUNT> \
|
||||
--trusted-validator <VALIDATOR_ADDRESS> \
|
||||
--no-untrusted-rpc
|
||||
```
|
||||
|
||||
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. The shred version will change on any hard forks in the cluster, so including `--expected-shred-version` ensures you are receiving current data from the cluster you expect.
|
||||
[Current parameters for Mainnet Beta](../clusters.md#example-solana-validator-command-line-2)
|
||||
|
||||
The `--limit-ledger-size` parameter allows you to specify how many ledger [shreds](../terminology.md#shred) your node retains 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
|
||||
|
||||
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](../running-validator/validator-start.md#trusted-validators)
|
||||
|
||||
Optional parameters to consider:
|
||||
- `--private-rpc` prevents your RPC port from being published for use by other nodes
|
||||
- `--rpc-bind-address` allows you to specify a different IP address to bind the RPC port
|
||||
|
||||
### Automatic Restarts
|
||||
|
||||
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.
|
||||
|
||||
### Ledger Continuity
|
||||
|
||||
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
|
||||
snapshot.
|
||||
|
||||
If you pass the `--no-snapshot-fetch` parameter on your initial boot, it will
|
||||
take your node a very long time to catch up. We recommend booting from a
|
||||
snapshot first, and then using 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.
|
||||
|
||||
## 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.
|
||||
|
||||
Solana accounts are charged [rent](../apps/rent.md) 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,
|
||||
query the
|
||||
[`getMinimumBalanceForRentExemption` endpoint](../apps/jsonrpc-api.md#getminimumbalanceforrentexemption):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[0]}' localhost:8899
|
||||
|
||||
{"jsonrpc":"2.0","result":890880,"id":1}
|
||||
```
|
||||
|
||||
### 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).
|
||||
|
||||
## 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.
|
||||
|
||||
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 from RPC in binary format, and parse it using either our
|
||||
[Rust SDK](https://github.com/solana-labs/solana) 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 your
|
||||
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://localhost:8899
|
||||
```
|
||||
|
||||
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. See
|
||||
more on [blockhash expiration](#blockhash-expiration) below.
|
||||
|
||||
First, get a recent blockhash using the [`getFees` endpoint](../apps/jsonrpc-api.md#getfees)
|
||||
or the CLI command:
|
||||
```bash
|
||||
solana fees --url http://localhost:8899
|
||||
```
|
||||
|
||||
In the command-line tool, pass the `--no-wait` argument to send a transfer
|
||||
asynchronously, and include your recent blockhash with the `--blockhash` argument:
|
||||
|
||||
```bash
|
||||
solana transfer <USER_ADDRESS> <AMOUNT> --no-wait --blockhash <RECENT_BLOCKHASH> --keypair <KEYPAIR> --url http://localhost:8899
|
||||
```
|
||||
|
||||
You can also build, sign, 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
|
||||
|
||||
When you request a recent blockhash for your withdrawal transaction using the
|
||||
[`getFees` endpoint](../apps/jsonrpc-api.md#getfees) 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](../apps/jsonrpc-api.md#getslot); once the cluster slot is
|
||||
greater than `lastValidSlot`, the withdrawal transaction using that blockhash
|
||||
should never succeed.
|
||||
|
||||
You can also doublecheck whether a particular blockhash is still valid by sending 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 should never succeed.
|
||||
|
||||
## Testing the Integration
|
||||
|
||||
Be sure to test your complete workflow on Solana devnet and testnet
|
||||
[clusters](../clusters.md) 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. Devnet features a token faucet, but
|
||||
you will need to request some testnet SOL to get going on testnet.
|
Reference in New Issue
Block a user