## Setup
* Clone `go-nitro` repo and checkout to the required branch:
```bash
git clone git@github.com:cerc-io/go-nitro.git
# In go-nitro
cd go-nitro
git checkout ts-interop
```
* Install dependencies:
```bash
# In go-nitro
go mod tidy
```
* Clone `mobymask-ui` repo, checkout to v3 branch and install dependencies:
```bash
git clone git@github.com:cerc-io/mobymask-ui.git
cd mobymask-ui
git checkout lxdao-v3
yarn install
```
* Run a script to fix modules in `mobymask-ui`:
```bash
# In mobymask-ui
yarn build:fix-modules
```
* Clone `ts-nitro` for browser setup and install dependencies:
```bash
git clone git@github.com:cerc-io/ts-nitro.git browser-ts-nitro
cd browser-ts-nitro
yarn install
```
* Run the hardhat chain:
```bash
# In browser-ts-nitro
yarn chain
```
* Deploy the Nitro protocol contracts and copy over the generated JSON file to required places:
```bash
# In browser-ts-nitro (another window)
yarn test:deploy-contracts
yarn test:copy-addresses
```
* Build packages in `browser-ts-nitro`:
```bash
# In browser-ts-nitro
yarn build:browser --ignore @cerc-io/example-web-app
```
* Copy over the contract addresses to the `mobymask-ui` repo:
```bash
# In browser-ts-nitro
cp packages/nitro-util/nitro-addresses.json ../mobymask-ui/src/utils/nitro-addresses.json
```
* Clone `mobymask-v2-watcher-ts` to `mobymask-v3-watcher-ts` and checkout to `v3` branch for running v3 watcher with nitro client
```bash
git clone git@github.com:cerc-io/mobymask-v2-watcher-ts.git mobymask-v3-watcher-ts
cd mobymask-v3-watcher-ts
git checkout v3
```
* Copy over the contract addresses deployed:
```bash
# In mobymask-v3-watcher-ts
cp ../browser-ts-nitro/packages/nitro-util/nitro-addresses.json ./src/nitro-addresses.json
```
* Install dependencies and build:
```bash
# In mobymask-v3-watcher-ts
yarn && yarn build
```
* Setup postgres databases for the watcher servers:
```bash
sudo su - postgres
createdb mobymask-v2-watcher
createdb mobymask-v2-watcher-job-queue
```
* Create a JSON file `relay-id.json` in `mobymask-v3-watcher-ts` for relay node's peer id:
```json
{
"id": "12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8",
"privKey": "CAESQKxvh6+M84l1u28rNdYf/a/FjWpplmiGjVSl+oIdiIVQhQsfZtQnpJzcdTFE4sirkmxvMuy8pbee8Om28YCqyCk=",
"pubKey": "CAESIIULH2bUJ6Sc3HUxROLIq5JsbzLsvKW3nvDptvGAqsgp"
}
```
* Create a JSON file `peer-id.json` in `mobymask-v3-watcher-ts` for watcher peer's peer id:
```json
{
"id": "12D3KooWGEaoVcaFMwZve8ReXQgiucoPrjdwHPVbBgc35acHpL9u",
"privKey": "CAESQMbtIzsdVa6il9JamQeA1xM2UB5u/qcUwB/Pg4Sm90LLX1pYiakFrxUIa3Wg07evhaPwVXAaCNDQDY9xrQ/GQyg=",
"pubKey": "CAESIF9aWImpBa8VCGt1oNO3r4Wj8FVwGgjQ0A2Pca0PxkMo"
}
```
* Update the watcher config (`environments/local.toml`) to update the following fields:
```toml
...
[server.p2p]
enablePeer = true
...
[server.p2p.relay]
...
peerIdFile = './relay-id.json'
...
[server.p2p.peer]
relayMultiaddr = '/ip4/127.0.0.1/tcp/9090/ws/p2p/12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8'
...
peerIdFile = './peer-id.json'
...
[server.p2p.nitro]
store = './out/erin-db'
privateKey = '0aca28ba64679f63d71e671ab4dbb32aaa212d4789988e6ca47da47601c18fe2'
chainPrivateKey = '7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6'
...
[server.p2p.nitro.payments]
ratesFile = './environments/rates.toml'
```
* Clone `mobymask-v2-watcher-ts` to `watcher-relay` for running a watcher relay node:
```bash
git clone git@github.com:cerc-io/mobymask-v2-watcher-ts.git watcher-relay
cd watcher-relay
```
* Install dependencies and build packages in `watcher-relay`:
```bash
# In watcher-relay
yarn && yarn build
```
* Copy over the `relay-id.json` file:
```bash
# In watcher-relay
cp ../mobymask-v3-watcher-ts/relay-id.json ./relay-id.json
```
* Set `peerIdFile` for relay node in `environments/local.toml`:
```toml
...
[server.p2p]
enablePeer = false
...
...
[server.p2p.relay]
...
peerIdFile = './relay-id.json'
```
* Run the watcher server which will also run the relay node:
```bash
# In watcher-relay
yarn server
# Expected output:
# laconic:relay Listening on: +0ms
# laconic:relay /ip4/127.0.0.1/tcp/9090/ws/p2p/12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8 +1ms
```
* Set the relay address, chain ID and chain URL in mobymask-ui app config (`mobymask-ui/src/utils/config.json`) with the relay node address:
```json
{
...
"chainId": 31337,
"chainURL": "http://localhost:8545",
"relayNodes": ["/ip4/127.0.0.1/tcp/9090/ws/p2p/12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8"],
...
}
```
* Set watcher URL `mobymask-ui/.env` file
```bash
# In mobymask-ui
REACT_APP_WATCHER_URI=http://localhost:3010/graphql
```
* Set the relay address in browser-ts-nitro .env file (`browser-ts-nitro/packages/example-web-app/.env`):
```bash
# In browser-ts-nitro/packages/example-web-app/.env
REACT_APP_RELAY_MULTIADDR=/ip4/127.0.0.1/tcp/9090/ws/p2p/12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8
```
* Run the browser app in dev mode:
```bash
# In browser-ts-nitro
cd packages/example-web-app
yarn start
```
## Run
### browser <-> browser
* Open the [app](http://localhost:3000) in a browser tab; open the console in browser inspect and enable debug logs
```bash
localStorage.debug = 'ts-nitro:*'
# Refresh the tab for taking effect
```
* Setup a client:
```bash
const nitro = await setupClient('alice')
```
* Open the [app](http://localhost:3000) for another client in a different browser and enable logs:
```bash
localStorage.debug = 'ts-nitro:*'
# Refresh the tab for taking effect
```
* Setup a client:
```bash
const nitro = await setupClient('bob')
```
* Wait for `New peer found` log in console
* Check if Bob's client is dialable from Alice’s browser:
```bash
await nitro.isPeerDialable('0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94')
# Expected output:
# [true, 'Peer with address 0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94 is dialable']
```
* From Alice, create a ledger channel by calling the `directFund` method:
```bash
await nitro.directFund('0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94', 1_000_000_000_000_000_000)
# Expected output:
# ..
# ts-nitro:engine Objective DirectFunding-0x0a59b50c2869d6952906d7ee295423adfdd1659cc8f48f9087ca7cfd629f3eae is complete & returned to API +12ms
# ts-nitro:util:nitro Ledger channel created with id 0x0a59b50c2869d6952906d7ee295423adfdd1659cc8f48f9087ca7cfd629f3eae +0ms
```
* Assign ledger channel id from the log above to a variable in the console:
```bash
let LEDGER_CHANNEL_ID = "<LEDGER_CHANNEL_ID>"
```
* Check the ledger channel's status:
```bash
out(await nitro.getLedgerChannel(LEDGER_CHANNEL_ID))
```
* The funding amount can be seen deducted from the chain accounts; check chain account balances:
```bash
# In browser-ts-nitro/packages/nitro-util
export ALICE_CHAIN_PK=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
export BOB_CHAIN_PK=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
yarn balance --key $ALICE_CHAIN_PK
# Expected output:
# ts-nitro:util Balance of account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 is 9998990425919170977798 +0ms
yarn balance --key $BOB_CHAIN_PK
# Expected output:
# ts-nitro:util Balance of account 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 is 9998999938277338495680 +0ms
```
* Create a virtual payment channel by calling the `virtualFund` method:
```bash
await nitro.virtualFund('0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94', 1_000)
# Expected output:
# ...
# ts-nitro:engine Objective VirtualFund-0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5 is complete & returned to API +1ms
# ts-nitro:util:nitro Virtual payment channel created with id 0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5 +4m
```
* Assign payment channel id from the log above to a variable in the console:
```bash
let PAYMENT_CHANNEL_ID = "<PAYMENT_CHANNEL_ID>"
```
* Check the payment channel's status:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "Payer": "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
# "PaidSoFar": 0,
# "RemainingFunds": 1000
# }
# }
```
* Make a payment on the created payment channel:
```bash
await nitro.pay(PAYMENT_CHANNEL_ID, 50)
# The payment can be done multiple times
```
* Check status of the payment channel:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "Payer": "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
# "PaidSoFar": 50,
# "RemainingFunds": 950
# }
# }
```
* Close the virtual payment channel:
```bash
await nitro.virtualDefund(PAYMENT_CHANNEL_ID)
# Expected output:
# ts-nitro:util:nitro Virtual payment channel with id 0xba0d64f7a0e91cf1aecb70a90ebf7341286f9f840785b4360675d44c8d5fd55e closed +49s
```
* Check status of the payment and ledger channels:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5",
# "Status": "Complete",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "Payer": "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
# "PaidSoFar": 50,
# "RemainingFunds": 950
# }
# }
out(await nitro.getLedgerChannel(LEDGER_CHANNEL_ID))
# Expected output:
# {
# "ID": "0x0a59b50c2869d6952906d7ee295423adfdd1659cc8f48f9087ca7cfd629f3eae",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Hub": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "Client": "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
# "HubBalance": 1000000000000000050,
# "ClientBalance": 999999999999999950
# }
# }
```
* Close the ledger channel:
```bash
await nitro.directDefund(LEDGER_CHANNEL_ID)
# Expected output:
# ts-nitro:util:nitro Ledger channel with id 0x0a1d20c9574bd61e75e6ae44fb2b67e8ede5f08dea9ff2e62cb22cbfb88631fd closed +1m
```
* Check balance of accounts on chain
```bash
# In browser-ts-nitro/packages/nitro-util
export ALICE_ADDRESS=0xAAA6628Ec44A8a742987EF3A114dDFE2D4F7aDCE
export BOB_ADDRESS=0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94
# Alice's (browser peer) balance
yarn balance --address $ALICE_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0xAAA6628Ec44A8a742987EF3A114dDFE2D4F7aDCE is 999999999999999950 +0ms
# Bob's (watcher) balance
yarn balance --address $BOB_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94 is 1000000000000000050 +0ms
```
* Close Alice's browser client and keep Bob's open for further demonstration
### browser <-> go-nitro
* In go-nitro repo, assign private keys for Charlie and contract addresses to variables:
```bash
# In go-nitro
export CHARLIE_PK=58368d20ff12f17669c06158c21d885897aa56f9be430edc789614bf9851d53f
export CHARLIE_CHAIN_PK=47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
export NA_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
export VPA_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
export CA_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
```
* Run a client for Charlie:
```bash
# In go-nitro
go run . -msgport 3006 -wsmsgport 5006 -rpcport 4006 -pk $CHARLIE_PK -chainpk $CHARLIE_CHAIN_PK -naaddress $NA_ADDRESS -vpaaddress $VPA_ADDRESS -caaddress $CA_ADDRESS
# Expected output:
# Initialising mem store...
# Initializing chain service and connecting to ws://127.0.0.1:8545...
# Initializing message service on tcp port 3006 and websocket port 5006...
# P2PMessageService started with Peer Id: 16Uiu2HAmRKvrFMTFYr7bocpsyZhBaTx6wLLa8D9VnkiEvAmUZimm
# Address: 0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d
# {"level":"debug","engine":"0x67D5b5","time":1689317732882,"caller":"engine.go:151","message":"Constructed Engine"}
# Initializing websocket RPC transport...
# Nitro as a Service listening on port 4006
```
* Check that Charlie's client is not dialable from Bob’s browser:
```bash
await nitro.isPeerDialable('0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d')
# Expected output:
# [false, 'Not able to dial peer with address 0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d: peer info not found']
```
* In Bob's browser console, call the `nitro.addPeerByMultiaddr` method to connect to Charlie's go-nitro client:
```bash
await nitro.addPeerByMultiaddr('0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d', '/ip4/127.0.0.1/tcp/5006/ws/p2p/16Uiu2HAmRKvrFMTFYr7bocpsyZhBaTx6wLLa8D9VnkiEvAmUZimm')
```
* Check if Charlie's client is dialable from Bob’s browser now:
```bash
await nitro.isPeerDialable('0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d')
# Expected output:
# [true, 'Peer with address 0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d is dialable']
```
* Create a ledger channel by calling the `directFund` method:
```bash
await nitro.directFund('0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d', 1_000_000_000_000_000_000)
# Expected output:
# ..
# ts-nitro:engine Objective DirectFunding-0x0a59b50c2869d6952906d7ee295423adfdd1659cc8f48f9087ca7cfd629f3eae is complete & returned to API +12ms
# ts-nitro:util:nitro Ledger channel created with id 0x0a59b50c2869d6952906d7ee295423adfdd1659cc8f48f9087ca7cfd629f3eae +0ms
```
* Assign ledger channel id from the log above to a variable in the console:
```bash
let LEDGER_CHANNEL_ID = "<LEDGER_CHANNEL_ID>"
```
* Check the ledger channel's status:
```bash
out(await nitro.getLedgerChannel(LEDGER_CHANNEL_ID))
```
* The funding amount can be seen deducted from the chain accounts; check chain account balances:
```bash
# In browser-ts-nitro/packages/nitro-util
export BOB_CHAIN_PK=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
export CHARLIE_CHAIN_PK=47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
yarn balance --key $BOB_CHAIN_PK
# Expected output:
# ts-nitro:util Balance of account 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 is 9997999847780203211120 +0ms
yarn balance --key $CHARLIE_CHAIN_PK
# Expected output:
# ts-nitro:util Balance of account 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 is 9998999957217078558360 +0ms
```
* Create a virtual payment channel by calling the `virtualFund` method:
```bash
await nitro.virtualFund('0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d', 1_000)
# Expected output:
# ...
# ts-nitro:engine Objective VirtualFund-0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5 is complete & returned to API +1ms
# ts-nitro:util:nitro Virtual payment channel created with id 0x7cc3065fa683647b3ae38178fb4b32ad1584b524772bd442252d108be8b194f5 +4m
```
* Assign payment channel id from the log above to a variable in the console:
```bash
let PAYMENT_CHANNEL_ID = "<PAYMENT_CHANNEL_ID>"
```
* Check the payment channel's status:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0xcad9f588ab79e3e50cd8f5738b01c9c07747bf75c6c9eb2fa02ca620dba5ec34",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0x67d5b55604d1af90074fcb69b8c51838fff84f8d",
# "Payer": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "PaidSoFar": 0,
# "RemainingFunds": 1000
# }
# }
```
* Make a payment on the created payment channel:
```bash
await nitro.pay(PAYMENT_CHANNEL_ID, 50)
# The payment can be done multiple times
```
* Check status of the payment channel:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0xcad9f588ab79e3e50cd8f5738b01c9c07747bf75c6c9eb2fa02ca620dba5ec34",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0x67d5b55604d1af90074fcb69b8c51838fff84f8d",
# "Payer": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "PaidSoFar": 50,
# "RemainingFunds": 950
# }
# }
```
* Close the virtual payment channel:
```bash
await nitro.virtualDefund(PAYMENT_CHANNEL_ID)
```
* Check status of the payment and ledger channels:
```bash
out(await nitro.getPaymentChannel(PAYMENT_CHANNEL_ID))
# Expected output:
# {
# "ID": "0xcad9f588ab79e3e50cd8f5738b01c9c07747bf75c6c9eb2fa02ca620dba5ec34",
# "Status": "Complete",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Payee": "0x67d5b55604d1af90074fcb69b8c51838fff84f8d",
# "Payer": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "PaidSoFar": 50,
# "RemainingFunds": 950
# }
# }
out(await nitro.getLedgerChannel(LEDGER_CHANNEL_ID))
# Expected output:
# {
# "ID": "0xba6312502c3efaeafa861b7d9ef520fda9de3eaba77916dff38b64b74875bbde",
# "Status": "Open",
# "Balance": {
# "AssetAddress": "0x0000000000000000000000000000000000000000",
# "Hub": "0x67d5b55604d1af90074fcb69b8c51838fff84f8d",
# "Client": "0xbbb676f9cff8d242e9eac39d063848807d3d1d94",
# "HubBalance": 1000000000000000050,
# "ClientBalance": 999999999999999950
# }
# }
```
* Close the ledger channel:
```bash
await nitro.directDefund(LEDGER_CHANNEL_ID)
```
* Check balance of accounts on chain
```bash
# In browser-ts-nitro/packages/nitro-util
export BOB_ADDRESS=0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94
export CHARLIE_ADDRESS=0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d
# Bob's (browser peer) balance
yarn balance --address $BOB_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0xBBB676f9cFF8D242e9eaC39D063848807d3D1D94 is 2000000000000000000 +0ms
# Charlie's (go-nitro peer) balance
yarn balance --address $CHARLIE_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0x67D5b55604d1aF90074FcB69b8C51838FFF84f8d is 1000000000000000050 +0ms
```
* Close the browser client along with the example-web-app dev server, the go-nitro client and the watcher server (relay node)
### mobymask-v3 (browser <-> watcher)
* Check that hardhat chain is running and nitro contract are deployed
* Start the MobyMask v3 watcher:
```bash
# In mobymask-v3-watcher-ts
yarn server
# Expected output:
# laconic:relay Relay node started with id 12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8 (rotten-gold-ardelle) +0ms
# laconic:relay Listening on: +0ms
# laconic:relay /ip4/127.0.0.1/tcp/9090/ws/p2p/12D3KooWJmiEGTzqBpLSmjNkux1aaHZfara47ggQGsvAC9atfJC8 +1ms
# .
# .
# vulcanize:server Peer ID: 12D3KooWGEaoVcaFMwZve8ReXQgiucoPrjdwHPVbBgc35acHpL9u +0ms
# .
# .
# ts-nitro:engine Constructed Engine +0ms
# vulcanize:server Starting server... +0ms
```
* Run the lxdao-mobymask app in dev mode:
```bash
# In mobymask-ui
yarn start
# Should open the app in a browser tab at http://localhost:3000/
```
* Open the console in browser inspect and enable debug logs:
```bash
localStorage.debug = 'ts-nitro:*'
# Refresh the tab for taking effect
```
* In the app's debug panel, check that the peer gets connected to relay node and watcher peer
* Wait for at least 5 seconds after restart to connect properly with watcher peer
* Open the `NITRO` tab in debug panel
* Click on Connect Wallet button to connect to MetaMask
* Import private key that is already funded in hardhat
```bash
47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
```
* Reset accounts in MetaMask if chain is restarted
* Click on `DIRECT FUND` button against known client (which is the watcher peer) to create a ledger channel
* A MetaMask notification appears, click on the `Confirm` button
* Creating ledger channel takes some time
* Click on `VIRTUAL FUND` button to create a virtual payment channel
* Payment channel details would be shown below
* Click on `PAY` button to make a payment on the created payment channel
* Check that balance has updated in payment channel details
* Click on `VIRTUAL DEFUND` to close the virtual payment channel
* Payment channel details dissapear as it completes
* Click on `DIRECT DEFUND` to close the ledger channel
* A MetaMask notification appears, click on the `Confirm` button
* Requires some time to close ledger channel
* Check balances of accounts on chain
```bash
# In browser-ts-nitro/packages/nitro-util
# Get CLIENT_ADDRESS from NITRO tab in the debug panel
export DAVID_ADDRESS=<CLIENT_ADDRESS>
export ERIN_ADDRESS=0xB2B22ec3889d11f2ddb1A1Db11e80D20EF367c01
# Alice's (browser peer) balance
yarn balance --address $DAVID_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0x6eaAD2a3a587c25c46D920b2fF2eE883cd75b54e is 999999999999999950 +0ms
# Bob's (watcher) balance
yarn balance --address $ERIN_ADDRESS
# Expected output:
# ts-nitro:util Balance of account 0xB2B22ec3889d11f2ddb1A1Db11e80D20EF367c01 is 1000000000000000050 +0ms
```
## Clean Up
* After stopping the watcher, clear storage by deleting the DB directory:
```bash
# In mobymask-v3-watcher-ts
rm -r ./out
```
* In browser apps call `clearClientStorage` method to delete all indexedDBs:
```bash
await clearClientStorage()
```