Skip to content

Commit

Permalink
Add exchange integration doc
Browse files Browse the repository at this point in the history
  • Loading branch information
CriesofCarrots committed May 14, 2020
1 parent d195dce commit ee0c675
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
* [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)
Expand Down
Empty file added docs/src/integrations/README.md
Empty file.
183 changes: 183 additions & 0 deletions docs/src/integrations/exchange.md
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).
-

0 comments on commit ee0c675

Please sign in to comment.