-
Notifications
You must be signed in to change notification settings - Fork 6
/
client.ts
164 lines (146 loc) · 6.51 KB
/
client.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import {BigNumber} from 'bignumber.js';
import * as moment from 'moment';
import * as Web3 from 'web3';
import {MetabackendClient, MetabackendService, pb} from './metabackend';
import {GetlineBlockchain, Blockchain} from './blockchain';
import {Loan} from './loan';
import {Address, PrintableToken, LOAN_CONTRACT} from './common';
/**
* Getline client library.
*
* This library lets you view and manage Getline.in loans programatically.
* It runs under node.js and in Chrome with the Metamask extension. You will
* be automatically logged in as the first address from your web3 provider.
*
* The library is fully async/await compatible, which means you can use it
* both with `Promise.then/.catch` and `await` blocks. All calls that interact
* with the blockchain will block until the result propagates.
*
* Currently this library **only allows you to create loans on the Rinkeby
* testnet** - this is by design until we go out of demo.
*
*/
export class Client {
private metabackend: MetabackendClient;
private network: string;
private blockchain: GetlineBlockchain;
/**
* Token that is used for collateral and loans in the demo.
*/
public readonly testToken: PrintableToken;
/**
* Creates a new Getline client.
*
* @param metabackend Address of metabackend. Production address is
* `https://0.api.getline.in`.
* @param network Network identifier. Currently only `"4"` (Rinkeby) is
* supported.
* @param provider Web3 provider to use. If not given, the client will try
* to find an injected one from Metamask. Otherwise, it
* will fall back to http://localhost:8545/.
*/
constructor(metabackend: string, network: string, provider?: Web3.Provider) {
this.metabackend = new MetabackendClient(metabackend, network);
this.network = network;
this.blockchain = new GetlineBlockchain(this.metabackend, network, provider);
this.testToken = new PrintableToken(this.blockchain, "0x02c9ccaa1034a64e3a83df9ddce30e6d4bc40515");
}
public async currentUser(): Promise<Address> {
return this.blockchain.coinbase();
}
/**
* Creates a new Getline Loan on the blockchain and indexes it in the
* Getline system.
*
* Currently these loans use `TEST_TOKEN` as both the collateral and loan
* token. This will be changed in the future.
*
* @param description Human-readable description of loan. Markdown.
* @param amount Amount of loan requested.
* @param interestPermil Loan interest in permil.
* @param fundraisingDelta Number of seconds from loan creation until
* fundraising ends.
* @param paybackDelta Number of seconds from loan creation until loan
* must be paid back. This cannot be earlier than
* `fundraisingDelta`.
* @returns Newly created loan.
*/
public async newLoan(description: string, amount: BigNumber, interestPermil: number,
fundraisingEnd: moment.Moment, paybackEnd: moment.Moment): Promise<Loan> {
// TODO(q3k) change this when we're not on rinkeby and we have a better loan SC
if (this.network != "4") {
throw new Error("cannot place loan on non-rinkeby chains");
}
let now = moment();
if (fundraisingEnd.isBefore(now)) {
throw new Error("cannot place loan with fundraising deadline in the past");
}
if (paybackEnd.isBefore(now)) {
throw new Error("cannot place loan with payback deadline in the past");
}
if (paybackEnd.isBefore(fundraisingEnd)) {
throw new Error("cannot place loan with payback deadline before fundraising deadline");
}
let currentBlock = (await this.blockchain.currentBlock()).toNumber();
let blocksPerSecond = (1.0) / 15;
let fundraisingDelta = fundraisingEnd.diff(now, 'seconds');
let paybackDelta = paybackEnd.diff(now, 'seconds');
let fundraisingEndBlocks = currentBlock + blocksPerSecond * fundraisingDelta;
let paybackEndBlocks = currentBlock + blocksPerSecond * paybackDelta;
let loan = await this.blockchain.deploy(LOAN_CONTRACT,
this.testToken.ascii, this.testToken.ascii,
(await this.currentUser()).ascii,
amount, interestPermil, fundraisingEndBlocks, paybackEndBlocks);
let req = new pb.IndexLoanRequest();
req.setNetworkId(this.network);
req.setDescription(description);
req.setLoan(loan.address.proto());
let res = await this.metabackend.invoke(MetabackendService.IndexLoan, req);
console.log("getline.ts: indexed loan as " + res.getShortId());
return this.loan(res.getShortId());
}
/**
* Returns loan identifier by a given short identifier.
*
* @param shortId Short identifier of loan (`shortId` member of a `Loan`
* object).
* @returns Loan identifier by shortId.
*/
public async loan(shortId: string): Promise<Loan> {
let req = new pb.GetLoansRequest();
req.setNetworkId(this.network);
req.setShortId(shortId);
let res = await this.metabackend.invoke(MetabackendService.GetLoans, req);
if (res.getNetworkId() != this.network) {
throw new Error("Invalid network ID in response.");
}
let loan = new Loan(this.blockchain);
await loan.loadFromProto(res.getLoanCacheList()[0]);
return loan;
}
/**
* Returns all loans owned by a given address, regardless of their state.
*
* @param owner Ethereum address of owner/liege.
* @returns Loans owned by `owner`.
*/
public async loansByOwner(owner: Address): Promise<Array<Loan>> {
let req = new pb.GetLoansRequest();
req.setNetworkId(this.network);
req.setOwner(owner.proto());
let res = await this.metabackend.invoke(MetabackendService.GetLoans, req);
if (res.getNetworkId() != this.network) {
throw new Error("Invalid network ID in response.");
}
let loans : Array<Loan> = [];
let promises : Array<Promise<void>> = [];
let currentBlock = await this.blockchain.currentBlock();
res.getLoanCacheList().forEach((elem) => {
let loan = new Loan(this.blockchain);
promises.push(loan.loadFromProto(elem));
loans.push(loan);
});
await Promise.all(promises);
return loans;
}
}