** WARNING: This code is not intended for production but serves as a good example of how to integrate the Tokens SDK with real bank accounts for projects that require "cash on ledger".
This code works with Monzo and Starling bank accounts. For those that do not have
Monzo or Starling bank accounts you can use the MockMonzo
client to generate
fake transaction data.
This repo contains an example of how to implement a cash issuer/cash tokenizer as described in the accompanying design document.
The code is more instructive than anything else. If you do want to use this code
and get stuck then e-mail [email protected]
.
The repo is split into a number of modules:
- client - code which should be run by participants in a cash business network.
- common - code which is shared by the cash issuer and users of cash states issued by the cash issuer. E.g. abstract flow initiator definitions and types.
- daemon - a process which polls bank APIs for new transactions, transforms the data into a common format and sends it to the issuer node for processing
- service - the cash issuer node. Contains folows for processing data provided by the daemon as well as flows for issuing and redeeming cash states
- service-ui - a basic JavaFx app that provides a view on the cash issuer node.
- Three bank accounts. One for the Issuer, one for PartyA and one for partyB.
- The bank holding the Issuer's bank account needs to offer a public API which allows clients to get account information, balance information and transaction information in real time.
- You will need a working API key for the bank's API. If you don't have this
then you must start ed
daemon
inmock-mode
.
Add your own bank API clients:
-
The daemon is extensible. Support for any bank HTTP API can be added by sub-classing
OpenBankingApiClient
and providing an interface definition for the API that can be used by Retrofit. Look at the Monzo and Starling implementations as examples. -
You will notice that the Starling and Monzo implementations differ a little, this is due to the different API interfaces offered.
-
You will need to add a config file to the resources folder, For, example to add support for "Foo Bank", add a "FooBankClient" class that sub-classes
OpenBankingApiClient
and add afoobank.conf
config file (omit 'Client' from the config file name). Config files contain three key/value pairs:apiBaseUrl="[URL HERE]" apiVersion="" apiAccessToken="[ADD ACCESS TOKEN HERE]"
If you have your own Starling or Monzo account you'd like to use, then no additional work is required.
Starling and Monzo sometimes change their API, if they do then this code will break.
Using the Mock Monzo bank account:
-
Start the Daemon with the option
-mock-mode
. -
This way you can experiment with the functionality of the cash issuer without having to use a real bank account.
-
The MockMonzo bank will create realistic-ish transactions at random intervals.
-
The transactions created by the MockMonzo bank come from five pre-defined bank account numbers, which are:
Account number: 13371337, sort code: 442200 Account number: 12345678, sort code: 873456 Account number: 73510753, sort code: 059015 Account number: 34782115, sort code: 022346 Account number: 90143578, sort code: 040040
When following the instructions below, add one of these banks accounts to NodeA.
Start the corda nodes and issuer daemon:
- Assuming all the API clients you need are implemented and a working
config is present in the
resources
directory, then you are good to go! The repo comes with a config file for Monzo and Starling. You just need to add your API key which has permission to view accounts, view transactions and check balances. If you don't have a Starling or Monzo account then use the daemon in--mock-mode
- From the root of this repo run
./gradlew clean deployNodes
. The deployNodes script will buildNotary
Issuer
,PartyA
andPartyB
nodes. - Navigate to the node directories
cd build/nodes
. - Run the nodes
./runnodes
. - Wait for all the nodes to start up.
- Build the issuer daemon jar with
./gradlew :daemon:jar
the jar will be output todaemon/build/libs/daemon-0.1.jar
- Start the issuer daemon (See "Starting the issuer daemon" below).
- Start the issuer
service-ui
via IntelliJ. Run via the Green Arrow next to themain
function incom/r3/corda/finance/cash/issuer/Main.kt
. The app is defaulted to connect to the Issuer node on port 10006. This can be changed inMain.kt
if required.
At this point all the required processes are up and running. Next, you can perform a demo run of an issuance:
- From
PartyA
add a new bank account via the node shell:flow start AddBankAccount bankAccount: { accountId: 12345, accountName: Rogers Account, accountNumber: { sortCode: XXXXXX, accountNumber: XXXXXXXX, type: uk }, currency: { tokenIdentifier: GBP} }, verifier: Issuer
replacingXXXXXX
andYYYYYYYY
with your sort code and account number. This is the bank account that you will make a payment from, to the issuer's account. - Next, we need to send the bank account you have just added, to the
issuer node. First, we need to know the linear ID of the bank account
state which has just been added:
run vaultQuery contractStateType: com.r3.corda.finance.cash.issuer.common.states.BankAccountState
. You should see the linear ID in the data structure which is output to the shell. Send the account to the issuer withstart Send issuer: Issuer, linearId: LINEAR_ID
. - You should see the issuer's UI update with new bank account information. Note: the issuer's account should already be added.
- From the issuer daemon shell type
start
. The daemon should start polling for new transactions. - Make a payment (for a small amount!!) from
PartyA
s bank account to theIssuer
s bank account. Soon after the payment has been made, the daemon should pick up the transaction information and the Issuer UI should update in the "nostro transactions" pane and the "node transactions" pane. - Assuming the correct details for the bank account used by PartyA were added and successfully sent to the issuer, then the issuance record in the node transaction tab should be marked as complete.
- Run
run vaultQuery contractStateType: net.corda.finance.contracts.asset.Cash$State
from PartyA to inspect the amount of cash issued. It should be for the same amount of the payment sent to the issuer's account.
Next things to do are to transfer the cash from A to B, then send the cash from B to the Issuer node for redemption.
-
Start the daemon either via the main method in
Main.kt
from IntelliJ or from the JAR created above withjava -jar daemon-0.1.jar
. The daemon should start and present you with a simple command line interface. The daemon requires a number of command line parameters. The main ones to know are:host-port the host name and port of the code node to connect to rpcUser the RPC username for the corda node rpcPass the RPC password for the corda node
All three of the above arguments are required. As such, note that if no corda node is available to connect to on the specified hostname and port, then the daemon will not start successfully.
There are three other parameters to note:
mock-mode - use this if you don't want to use a real bank account. auto-mode - Use this to start polling the bank accounts for new transactions as soon as the daemon startes. start-from - Use this flag to ignore all the past transactions in the bank account. This is useful if you want to perform a demo and need to re-use the same account multiple times but give the impression that the demo is from "scratch".
-
When the daemon starts up, it requests bank account information for all the supplied API interfaces. It then uploads the account information to the issuer node, via RPC. Note: if the daemon is connected to a corda node which does not have the required flows, then the daemon and the corda node in question will throw an exception. Make sure that the daemon only connects to issuers nodes as defined in the
service
module! Once account information has been added. It requests the balance information for each of the added accounts. Lastly, it presents a basic shell. Current commands are:start starts polling the apis for new transactions with a 5 second interval stop stop polling help show help quit exit the daemon