# Smart contracts: Liquidity for bone marrow donor registries
(This document was started on 4 Mar 2019)
[TOC]
## Motivation
**Main bottleneck**: Registries have to invest significant resources and time in raising money to recruit donors. This increases barrier to entry for small registries in underdeveloped areas. Lack of liquidity also tends to decrease efficiency of daily operations.
**Solution**: Allow lenders to provide funding for donor recruitment, in exchange for a "license fee" on each successful donor-patient match.
**Method**: Smart contracts with funding and licensing capabilities, programmatically disbursing license fees to lenders when a donor-patient match is made.
## Design
### Funding request and fulfilment
![](https://i.imgur.com/63ELDuP.png)
1. `Registry` initiates `fundingRequest` by escrowing `licenseFee` for `n` donors into `License Contract`, with the promise of registering them before a certain `_timeout`. Before a lender funds their request, `Registry` can withdraw these escrowed fees.
2. `Lender` fulfils the funding request by depositing `recruitFund` to `License Contract`, addressed to `Registry` on the condition that `n` donors are recruited before `_timeout`.
3. `License Contract` sends `recruitFund` to `Registry`, on the condition that `n` donors are registered before `_timeout`. If this condition is not met, the escrowed `licenseFee` is sent to `Lender`.
4. `Registry` registers `n` donors (before `_timeout`) on `License Contract`, assigning the license rights for these donors to the `Lender`.
5. When a donor-patient match is made, the `Patient` pays a `matchingFee` to the `License Contract`.
6. `License Contract` disburses `matchingFee` + `licenseFee` to `Lender`.
### Donor registration and approval
![](https://i.imgur.com/eN9WGqK.png)
1. `Registry` sends `donorHash` to `License Contract`. This is a hash of a JSON file (or other format) containing donors' information, and stored off-chain.
2. `Registry` sends the raw JSON file to `Lender` off-chain, who verifies that it meets certain pre-agreed criteria (e.g. age, gender, race) and hashes to the on-chain `donorHash`.
3. `Lender` approves `donorHash` on `LicenseContract`.
4. For a certain approved `donorHash`, when a donor-patient match is made, the `Patient` pays a `matchingFee` to the `License Contract`.
5. `License Contract` disburses `matchingFee` + `licenseFee` to `Lender`.
## Specification
(Solidity pseudocode)
### Contract state
The `License Contract` should keep track of whitelisted registries and lenders, as well as funding requests and registered donors.
```javascript=
contract LicenseContract(){
//mapping from whitelisted registries' Ethereum
//addresses to registry names
mapping(address=>string) public registryAddresses;
//mapping from whitelisted registries' Ethereum
//addresses to amount escrowed by them
mapping(address=>string) public registryEscrows;
//mapping from whitelisted lenders' Ethereum
//addresses to lender names
mapping(address=>string) public lenderAddresses;
//mapping from indices to funding requests
mapping (uint=>FundingRequest) public fundingRequests;
![Uploading file..._0anx2horv]()
//number of funding requests
uint public numRequests;
//mapping from hashed information of registered donors to
//registries who recruited them under a given funding request
mapping(bytes=>(address=>uint)) public donorHashToRegistry;
//mapping from hashed information of registered donors to
//patients who matched with them
mapping(bytes=>address) public donorHashToPatient;
//donor registration event, initiated by registry
event RegisterDonor(bytes donorHash, uint requestIdx);
//donor approval event, done by lender
event ApproveDonor(bytes donorHash, uint requestIdx);
//...
}
```
(Which authority/authorities will whitelist registries and lenders?)
```javascript=
//TODO: set whitelisting permissions
function whitelistRegistry(address _registryAddr, string _registryName){
registryAddresses[_registryAddr] = _registryName;
}
//TODO: set whitelisting permissions
function whitelistLender(address _lenderAddr, string _lenderName){
lenderAddresses[_lenderAddr] = _lenderName;
}
```
### Contract methods
#### Requests for funding
Each request for funding should specify the `Registry` from which it originates, the no. of donors it promises to recruit, the license fee paid out per successful donor match, and the timeout within which these donors must be registered.
```javascript=
//requests made by registries for funding
struct FundingRequest{
address registry; //registry making the funding request
uint promisedDonors; //no. of donors promised
uint licenseFee; //license fees promised per donor
uint amt; //funding amount requested
uint timeout; //timeout for promised recruitment in seconds
uint startTime; //time when request was fulfilled
uint numDonors; //no. of donors recruited
string status; //status of funding request
address lender; //lender fulfilling the funding request
}
```
A registry must escrow its promised licensing fees (which are far less than the funds borrowed from lenders) to create a funding request. This provides some degree of collateral for the request: the escrowed fees are sent to the lender in case of failure to recruit the promised number of donors.
```javascript=
//registries escrow licenseFee to create FundingRequest
function requestFunding(
address _registry,
uint _promisedDonors,
uint _licenseFee,
uint _amt,
uint _timeout
) public payable {
require(msg.value >= _numDonors * _licenseFee,
"registry stake must at least equal license fees");
require(registryAddresses[msg.sender] != address(0),
"registry must be whitelisted");
//create FundingRequest object
FundingRequest _fundingRequest = FundingRequest(
_registry,
_promisedDonors,
_licenseFee,
_amt,
_timeout,
0, //timer has not started yet
0, //no donors recruited yet
"pending", //request has pending status
address(0) //no lender has yet fulfilled request
);
//add request to fundingRequests mapping
fundingRequests[numRequests] = _fundingRequest;
//increase counter for no. or requests made
numRequests++;
//record amount escrowed by registry
registryEscrows[msg.sender] = msg.value;
}
```
#### Fulfilling requests for funding
```javascript=
//lenders fulfil a funding request
function fulfilFunding(uint _requestIdx) public payable{
require(registryAddresses[msg.sender] != address(0),
"lender must be whitelisted");
FundingRequest request = fundingRequests[_requestIdx]
require(msg.value >= request.amt,
"funds must at least equal requested funds");
//send funds to registry who made the request
request.registry.send(msg.value);
//start timer
request.startTime = block.timestamp;
//mark request as fulfilled
request.status = "fulfilled";
//set lender address
request.lender = msg.sender;
}
```
#### Registering donors
Most of donors' information should be stored offchain; a hash of it (maybe IPFS hash?) can be committed on-chain by the registry.
```javascript=
function registerDonor(bytes _donorHash, uint _requestIdx){
FundingRequest request = fundingRequests[_requestIdx];
require(registryAddresses[msg.sender] == request.registry,
"registry did not make this request");
emit RegisterDonor(_donorHash, _requestIdx)
}
```
Lenders should be able to vet the quality of donors and only acknowledge them if they meet certain pre-agreed criteria (e.g. age, gender, race, etc.); this verification is done off-chain.
```javascript=
function approveDonor(bytes _donorHash, uint _requestIdx){
FundingRequest request = fundingRequests[_requestIdx];
require(registryAddresses[msg.sender] == request.lender,
"lender did not fund this request");
//add donor to list recruited by registry, for given funding request
donorHashToRegistry[_donorHash][msg.sender] = _requestIdx;
//add to donor count under given funding request
fundingRequests[_requestIdx].numDonors++;
emit ApproveDonor(_donorHash, _requestIdx);
}
```
#### Lender payout
When a match is made, the patient (or any party on behalf of the patient) should pay a matching fee to the smart contract. This matching fee is then transferred to the lender who funded the donor recruitment, along with the promised license fee.
```javascript=
function payForMatch(bytes _donorHash) public payable{
require(donorHashToPatient[_donorHash] != address(0),
"donor already has a match");
//registry which recruited donor
address registry = donorHashToPatient[_donorHash];
//amt left in registry escrow
uint escrow = registryEscrows[registry]
//funding request under which donor was recruited
uint requestIdx = donorHashToPatient[_donorHash][registry];
FundingRequest request = fundingRequests[_requestIdx];
require(escrow >= request.licenseFee,
"registry does not have enough escrowed to cover license fee");
//lender which funded request
address lender = request.lender;
//send matching fee and license fee to lender
lender.send(msg.value);
lender.send(request.licenseFee);
//deduct license fee from registry escrow
registryEscrows[registry] = escrow - request.licenseFee;
}
```
#### Slashing escrow
If funded registries do not fulfil their recruitment promises in time, the lender can initiate slashEscrow to collect the registry's escrowed license fees. (Of course, the lender still loses the funds they lent; any possible better designs?)
```javascript=
function slashEscrow(uint _requestIdx) public {
FundingRequest request = fundingRequests[_requestIdx];
require(registryAddresses[msg.sender] == request.lender,
"lender did not fund this request");
if (request.startTime > block.timestamp - request.timeout
&& request.numDonors < request.promisedDonors){
//send escrowed license fees to lender
msg.sender.send(registryEscrows[registry]);
//deduct license fee from registry escrow
registryEscrows[registry] = 0;
}
}
```