forked from ethereum/dapp-bin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWeiLend.sol
187 lines (160 loc) · 7.75 KB
/
WeiLend.sol
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// WeiLend System
// Start, lend, payout and return interest yield to funders
// @creator of the original WeiFund code:
// Nick Dodson <[email protected]>
// @forked to implement the WeiLend by:
// M. Terzi <[email protected]> ===> http://github.com/terzim
// If goal is not reached and campaign is expired, contributers can get their donation refunded individually
// If goal is reached by alloted time, contributions can still be made
// After a grace period, the beneficiary returns funds to the funders in installments at a given interest rate
contract WeiLend {
// builds a variable (struct) called User which has two sub-variables, an integer number called numLoans and a mapping of
// loans.
struct User
{
uint numLoans;
mapping(uint => uint) loans;
}
// builds a variable (struct) called Funder which is identified by an address (his public "signature"), and an amount which he has lent
// which will define his/her entitlement to the installments repayments.
struct Funder
{
address addr;
uint amount;
}
// builds a variable (struct) called Loan. This is the most complex variable of the set. The most important here are the sub-variables:
// timelimit [the time for which the Loan is open to funding], fundingGoal [the amount that the guy who gets the
// loan wants to collect], and the key contractual features of the loan which are the interest_rate, the
// gracePeriod and the tenor.
// importantly, this variable contains a map of the funders and a pointer to the funders.
struct Loan
{
bytes32 operationName;
address beneficiary;
uint timelimit;
uint fundingGoal;
uint amount;
uint balance;
uint category;
uint status;
uint numFunders;
uint interestRateM; // monthly
uint gracePeriod;
uint tenorM; // monthly
uint installment; // monthly
mapping (uint => Funder) funders;
mapping (address => uint) toFunder;
}
uint public numLoans;
mapping (uint => Loan) public loans;
mapping (address => User) public users;
//events are returned after functions are successfully called
event onNewLoan(address indexed from, uint indexed lid);
event onContribute(address indexed from, uint indexed lid, uint _value);
event onPayout(address indexed from, uint indexed lid, uint _value);
event onRefund(address indexed from, uint indexed lid, uint _value);
event onpayInstallment(address indexed from, uint indexed lid, uint _value);
function newLoan(bytes32 _operationName, address _beneficiary, uint _goal, uint _timelimit_m, uint _category, uint _interest_rate, uint _grace_period_m, uint _tenor_a)
{
// if the goal (one of the parameters of the function assigned by the person
// who runs the function is greater than zero [not sure this shoudl stay here though]
// and the timelimit is greater than now (that is, the campaign has not expired] do....
if(_goal > 0 ){
// initialize a new variable called lid, which is a counter for the numLoans (defined earlier as a public integer variable)
uint lid = numLoans++; // campaignID is return variable
// creates a loan called l which will corrispond the [position lid in the mapping of all loans]
Loan l = loans[lid]; // assigns reference
l.operationName = _operationName; // vanity
// again assign to the loan l the beneficiary defined by the argument of the function
l.beneficiary = _beneficiary;
// again assign to the loan l the fundingGoal defined by the argument of the function
l.fundingGoal = _goal;
// again assign to the loan l the timelimit defined by the argument of the function
l.timelimit = block.timestamp + (_timelimit_m * 43200);
// again assign to the loan l the category defined by the argument of the function
l.category = _category;
// again assign to the loan l the interest rate defined by the argument of the function
l.interestRateM = _interest_rate / 12;
// again assign to the loan l the grace period defined by the argument of the function
l.gracePeriod = _grace_period_m * 43200;
// again assign to the loan l the tenor defined by the argument of the function (note, tenor meant to be after grace period)
l.tenorM = _tenor_a * 12;
// now creates a user called u, which is the sender of the transaction
User u = users[msg.sender];
// creates a variable called u_lid, which adds a one to the number of loans the user has made.
uint u_lid = u.numLoans++;
u.loans[u_lid] = lid;
// calls the event onNewLoan
onNewLoan(msg.sender, lid);
}
}
function contribute(uint _lid)
{
Loan l = loans[_lid]; // Cannot be expired.
// if the raising is not yet terminated
if(l.timelimit >= block.timestamp) {
uint fid = l.numFunders++;
Funder f = l.funders[fid];
f.addr = msg.sender;
f.amount = msg.value;
//increases the loan amount by the contribution
l.amount += f.amount;
//sends a funder id to the funder
l.toFunder[msg.sender] = fid;
onContribute(msg.sender, _lid, l.amount);
}
}
function refund(uint _lid)
{
Loan l = loans[_lid];
if (block.timestamp > l.timelimit
&& l.amount < l.fundingGoal && l.amount > 0){
Funder f = l.funders[l.toFunder[msg.sender]];
if(f.amount > 0){
f.addr.send(f.amount);
l.amount -= f.amount;
onRefund(msg.sender, _lid, f.amount);
f.amount = 0;
}
}
}
function payout(uint _lid)
{
Loan l = loans[_lid];
if (l.amount >= l.fundingGoal){
l.beneficiary.send(l.amount);
onPayout(msg.sender, _lid, l.amount);
l.status = 1;
l.timelimit = l.timelimit + l.gracePeriod;
//calculates the monthly installment amount. Constant installment loan
l.installment = l.amount * ((l.interestRateM*((1+l.interestRateM)**(l.tenorM)))/(((1+l.interestRateM)**(l.tenorM))-1));
l.balance = l.amount;
}
}
function payInstallment(uint _lid)
{
Loan l = loans[_lid];
//returns if the borrower is paying a wrong installment amount (error handling)
if (msg.value != l.installment){
msg.sender.send(msg.value);
return;
}
//returns if the grace period is not over
if (block.timestamp < l.timelimit) return;
//returns if the loan balance is below zero (error handling)
if (l.balance < 0) return;
uint i = 0;
uint n = l.numFunders;
//maybe the while is not needed here. Could use the toFunder functionality?
//in essence this loops returns to each funder his/her quota of the total installment
while(i<n){
uint entitlement = l.installment*l.funders[i].amount/l.amount;
l.funders[i].addr.send(entitlement);
i++; //added counter. Need to check with Ken if needed
}
//updates the balance of the loan. The installment remains constant throughout the duration of the loan but the balance
//decreases of the principal amount paid along with the installment.
l.balance -= (l.installment -(l.balance*l.interestRateM));
onpayInstallment(msg.sender, _lid, l.balance);
}
}