Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add "Use case: Centralized Exchanges" to Deposit Wallet
- Loading branch information
1 parent
1ddbb9e
commit 19379e7
Showing
2 changed files
with
177 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# User Manual | ||
|
||
Information about using the Deposit Wallet. |
174 changes: 174 additions & 0 deletions
174
lib/customer-deposit-wallet/docs/user/use-cases/centralized-exchange.md
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,174 @@ | ||
# Use case: Centralized Exchange | ||
|
||
This document describes how a centralized exchange (CEX) can use the Deposit Wallet to | ||
|
||
1. Assign an address to a customer ID. | ||
2. Track deposits at this address. | ||
3. Track deposits at all addresses. | ||
4. Create payments to a different wallet. | ||
|
||
# Scenarios, Haskell | ||
|
||
In this section, we describe the scenarios using Haskell. | ||
|
||
```hs | ||
import Prelude | ||
|
||
import Cardano.Crypto.Wallet | ||
( XPrv | ||
, XPub | ||
) | ||
import Cardano.Wallet.Deposit.IO | ||
( TxSummary (..) | ||
, WalletEnv | ||
, WalletInstance | ||
) | ||
import Cardano.Wallet.Test.Scenarios | ||
( Faucet | ||
, ada | ||
, assert | ||
, sendFunds | ||
-- ^ waits until transaction is on chain | ||
, signTx | ||
, submitTx | ||
) | ||
|
||
import qualified Cardano.Wallet.Deposit.IO as Wallet | ||
import qualified Data.Map as Map | ||
``` | ||
|
||
## 0. Start a Wallet | ||
|
||
A `WalletInstance` denotes a mutable wallet that is actively synchronizing to the blockchain, continuously writes its state to a database file, and responds to queries. | ||
|
||
In order to create a fresh wallet, or in order to restore a wallet from its public key all the way from genesis, use the function `withWalletFromMnemonic`. In addition to the public key, this function expects a number which equals the numerically largest customer ID previously handled with this wallet. | ||
|
||
```hs | ||
scenarioRestore | ||
:: XPub -> WalletEnv IO -> IO () | ||
scenarioRestore xpub env = | ||
let knownCustomers = 127 | ||
Wallet.withWalletFromMnemonic xpub knownCustomers env $ \w -> do | ||
value <- Wallet.availableBalance w | ||
print value | ||
``` | ||
|
||
Use the function `withWalletFromDatabase` function in order to load the wallet state from a database file and resume operation from that state. | ||
|
||
```hs | ||
scenarioStart | ||
:: WalletEnv IO -> IO () | ||
scenarioStart env = | ||
Wallet.withWalletFromDatabase env $ \w -> do | ||
value <- Wallet.availableBalance w | ||
print value | ||
``` | ||
|
||
## 1. Assign an address to a customer ID | ||
|
||
The type `Customer` represents a numerical customer ID. | ||
Given such a customer ID, the function `createAddress` will create an address and add the association between the customer and this address to the wallet state. | ||
|
||
(The mapping from customer ID to address is deterministic and based on the [BIP-32][] address derivation scheme.) | ||
|
||
[bip-32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki | ||
|
||
|
||
The function `listCustomers` returns the associations between customers and addresses recorded in the current wallet state. | ||
|
||
```hs | ||
scenarioCreateAddressList | ||
:: WalletInstance -> IO () | ||
scenarioCreateAddressList w = do | ||
let customer = 31 | ||
address <- Wallet.createAddress customer w | ||
customers <- Wallet.listCustomers w | ||
|
||
assert $ (customer, address) `elem` customers | ||
``` | ||
|
||
## 2. Track deposits at this address. | ||
|
||
As soon as an association between customer and address has been added to the wallet state using `createAddress`, the wallet will track deposits made at this address. | ||
|
||
The function `getCustomerHistory` returns a `TxSummary` for each transaction that is related to this customer. For every `TxSummary`, the `received` field records the total deposit made by the customer at this address in this transaction. | ||
|
||
(The `spent` field has informative purpose only, and records whether the wallet has moved any funds out of this address.) | ||
|
||
```hs | ||
scenarioTrackDepositOne | ||
:: Faucet -> WalletInstance -> IO () | ||
scenarioTrackDepositOne faucet w = do | ||
address <- Wallet.createAddress customer w | ||
sendFunds faucet [(address, coin)] | ||
txsummaries <- Wallet.getCustomerHistory customer | ||
assert $ coin `elem` map received txsummaries | ||
where | ||
customer = 7 :: Customer | ||
coin = 12 * ada :: Wallet.Value | ||
``` | ||
|
||
## 3. Track deposits at all addresses. | ||
|
||
A centralized exchange typically wants to monitor all transactions in a recent time window for activity in order to synchronize customer deposits on the blockchain ledger with the exchange ledger recording customer balances. | ||
|
||
This is a task for the `getCustomerHistories` function — it returns a mapping from customers to `TxSummaries` that record the entire activity within the given time interval. | ||
|
||
The time interval is specified by a `from` and a `to` point on the blockchain. We note that the `from` argument is exclusive while the `to` argument is inclusive. | ||
|
||
The wallet is synchronized to a particular point on the blockchain — use `getWalletTip` to query it. | ||
|
||
```hs | ||
scenarioTrackDepositAll | ||
:: Faucet -> WalletInstance -> IO () | ||
scenarioTrackDepositAll faucet w = do | ||
address1 <- Wallet.createAddress customer1 w | ||
address2 <- Wallet.createAddress customer2 w | ||
|
||
from <- Wallet.getWalletTip w | ||
sendFunds faucet [(address1, coin), (address2, coin)] | ||
sendFunds faucet [(address1, 2*coin)] | ||
to <- Wallet.getWalletTip w | ||
|
||
history <- Wallet.getCustomerHistories (from,to) | ||
assert $ | ||
Map.map received history | ||
== | ||
Map.fromList | ||
[ (customer0, 3*coin ) | ||
, (customer1, coin ) | ||
] | ||
where | ||
customer1, customer2 :: Customer | ||
customer1 = 1 | ||
customer2 = 2 | ||
coin = 3 * ada :: Wallet.Value | ||
``` | ||
|
||
## 4. Create payments to a different wallet. | ||
|
||
The `createPayment` function allows you to move funds from one wallet to another, e.g. in order to process customer withdrawals. If the wallet has sufficient funds, this function creates a transaction body which sends the given values to the given coins. | ||
|
||
The transaction body needs to be signed. Given a transaction body, the function `getBIP32PathsForOwnedInputs` will provide you with all [BIP-32][] address derivation paths of all inputs that are owned by the wallet, and which therefore require a signature. | ||
|
||
```hs | ||
scenarioCreatePayment | ||
:: XPrv -> Faucet -> WalletInstance -> WalletInstance -> IO () | ||
scenarioCreatePayment xprv faucet w1 w2 = do | ||
address1 <- Wallet.createAddress customer1 w1 | ||
address2 <- Wallet.createAddress customer2 w2 | ||
sendFunds faucet [(address1, 2*coin)] | ||
|
||
Just txBody <- Wallet.createPayment w1 [(address2, coin)] | ||
paths <- Wallet.getBIP32PathsForOwnedInputs w1 txBody | ||
tx <- signTx xprv paths txBody | ||
submitTx tx | ||
|
||
value <- Wallet.availableBalance w2 | ||
assert $ value == coin | ||
where | ||
customer1, customer2 :: Customer | ||
customer1 = 1 | ||
customer2 = 2 | ||
coin = 5 * ada :: Wallet.Value | ||
``` |