Skip to content

Commit

Permalink
Add "Use case: Centralized Exchanges" to Deposit Wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
HeinrichApfelmus committed Mar 28, 2024
1 parent 1ddbb9e commit 19379e7
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/customer-deposit-wallet/docs/user.md
@@ -0,0 +1,3 @@
# User Manual

Information about using the Deposit Wallet.
@@ -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
```

0 comments on commit 19379e7

Please sign in to comment.