Add exchange integration docs (#10054)
* Add exchange integration doc * Round 1 review comments * Add rent stub doc * Pretty-print some things * Rework blockhash info, move offline signing * Add something to test section * Update blockhash/last-valid-slot info
This commit is contained in:
		@@ -24,12 +24,15 @@
 | 
				
			|||||||
  * [Command-line Reference](cli/usage.md)
 | 
					  * [Command-line Reference](cli/usage.md)
 | 
				
			||||||
* [Solana Clusters](clusters.md)
 | 
					* [Solana Clusters](clusters.md)
 | 
				
			||||||
* [Develop Applications](apps/README.md)
 | 
					* [Develop Applications](apps/README.md)
 | 
				
			||||||
 | 
					  * [Accounts and Rent](apps/rent.md)
 | 
				
			||||||
  * [Example: Web Wallet](apps/webwallet.md)
 | 
					  * [Example: Web Wallet](apps/webwallet.md)
 | 
				
			||||||
  * [Example: Tic-Tac-Toe](apps/tictactoe.md)
 | 
					  * [Example: Tic-Tac-Toe](apps/tictactoe.md)
 | 
				
			||||||
  * [Drones](apps/drones.md)
 | 
					  * [Drones](apps/drones.md)
 | 
				
			||||||
  * [Anatomy of a Transaction](transaction.md)
 | 
					  * [Anatomy of a Transaction](transaction.md)
 | 
				
			||||||
  * [JSON RPC API](apps/jsonrpc-api.md)
 | 
					  * [JSON RPC API](apps/jsonrpc-api.md)
 | 
				
			||||||
  * [JavaScript API](apps/javascript-api.md)
 | 
					  * [JavaScript API](apps/javascript-api.md)
 | 
				
			||||||
 | 
					* [Integration Guides](integrations/README.md)
 | 
				
			||||||
 | 
					  * [Exchange](integrations/exchange.md)
 | 
				
			||||||
* [Run a Validator](running-validator/README.md)
 | 
					* [Run a Validator](running-validator/README.md)
 | 
				
			||||||
  * [Validator Requirements](running-validator/validator-reqs.md)
 | 
					  * [Validator Requirements](running-validator/validator-reqs.md)
 | 
				
			||||||
  * [Start a Validator](running-validator/validator-start.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