-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdcsSimple.sol
184 lines (145 loc) · 6.47 KB
/
dcsSimple.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SimplifiedLoanAndRiskPoolContract is Ownable, ReentrancyGuard {
struct Loan {
address borrower;
uint256 amount;
uint256 dueDate;
bool isRepaid;
uint256 poolId;
}
struct CreditScore {
uint256 score;
uint256 lastUpdateTimestamp;
}
struct RiskPool {
uint256 totalFunds;
uint256 availableFunds;
uint256 riskLevel;
// uint256 customValue;
}
mapping(uint256 => Loan) public loans;
mapping(address => CreditScore) private creditScores;
mapping(uint256 => RiskPool) public riskPools;
uint256 public nextLoanId;
uint256 public poolCount;
uint256 public constant MIN_CREDIT_SCORE = 300;
uint256 public constant INITIAL_CREDIT_SCORE = 500;
event LoanCreated(uint256 indexed loanId, address indexed borrower, uint256 amount, uint256 dueDate, uint256 poolId);
event LoanRepaid(uint256 indexed loanId, address indexed borrower, uint256 amount);
event CreditScoreUpdated(address indexed user, uint256 newScore);
event PoolCreated(uint256 indexed poolId, uint256 riskLevel, uint256 initialFunds);
event FundsAdded(uint256 indexed poolId, uint256 amount);
constructor() Ownable(msg.sender) {
nextLoanId = 0;
poolCount = 0;
}
function createRiskPool(uint256 _riskLevel, uint256 _initialFunds) external payable onlyOwner {
require(_riskLevel > 0 && _riskLevel <= 100, "Risk level must be between 1 and 100");
uint256 initialFunds = _initialFunds;
if (msg.value > 0) {
require(msg.value == _initialFunds, "Sent value does not match specified initial funds");
initialFunds = msg.value;
}
uint256 poolId = poolCount++;
riskPools[poolId] = RiskPool({
totalFunds: initialFunds,
availableFunds: initialFunds,
riskLevel: _riskLevel
// customValue: _customValue
});
emit PoolCreated(poolId, _riskLevel, initialFunds);
}
function addFundsToPool(uint256 _poolId, uint256 _amount) external payable {
require(_poolId < poolCount, "Invalid pool ID");
// require(msg.value == _amount, "Sent value does not match specified amount");
require(_amount > 0, "Must send funds");
RiskPool storage pool = riskPools[_poolId];
pool.totalFunds += _amount;
pool.availableFunds += _amount;
emit FundsAdded(_poolId, _amount);
}
function requestLoan(uint256 loanAmount, uint256 duration) external nonReentrant {
require(loanAmount > 0, "Loan amount must be greater than 0");
require(duration > 0, "Loan duration must be greater than 0");
uint256 creditScore = getCreditScore(msg.sender);
require(creditScore >= MIN_CREDIT_SCORE, "Credit score too low for a loan");
uint256 poolId = assignRiskPool(creditScore, loanAmount);
require(poolId < poolCount, "No suitable risk pool found");
RiskPool storage pool = riskPools[poolId];
require(pool.availableFunds >= loanAmount, "Insufficient funds in the selected pool");
uint256 loanId = nextLoanId++;
loans[loanId] = Loan({
borrower: msg.sender,
amount: loanAmount,
dueDate: block.timestamp + duration,
isRepaid: false,
poolId: poolId
});
pool.availableFunds -= loanAmount;
emit LoanCreated(loanId, msg.sender, loanAmount, block.timestamp + duration, poolId);
// Transfer the loan amount to the borrower
payable(msg.sender).transfer(loanAmount);
}
function repayLoan(uint256 loanId) external payable nonReentrant {
Loan storage loan = loans[loanId];
require(msg.sender == loan.borrower, "Only borrower can repay the loan");
require(!loan.isRepaid, "Loan already repaid");
require(msg.value >= loan.amount, "Insufficient repayment amount");
loan.isRepaid = true;
updateCreditScore(msg.sender, true);
RiskPool storage pool = riskPools[loan.poolId];
pool.availableFunds += loan.amount;
emit LoanRepaid(loanId, msg.sender, loan.amount);
if (msg.value > loan.amount) {
payable(msg.sender).transfer(msg.value - loan.amount);
}
}
function getCreditScore(address user) public view returns (uint256) {
if (creditScores[user].score == 0) {
return INITIAL_CREDIT_SCORE;
}
return creditScores[user].score;
}
function updateCreditScore(address user, bool isPositive) internal {
CreditScore storage userScore = creditScores[user];
if (userScore.score == 0) {
userScore.score = INITIAL_CREDIT_SCORE;
}
if (isPositive) {
userScore.score = min(userScore.score + 10, 850);
} else {
userScore.score = max(userScore.score - 50, 300);
}
userScore.lastUpdateTimestamp = block.timestamp;
emit CreditScoreUpdated(user, userScore.score);
}
function assignRiskPool(uint256 creditScore, uint256 amount) internal view returns (uint256) {
uint256 selectedPoolId = 0;
uint256 bestMatch = type(uint256).max;
for (uint256 i = 0; i < poolCount; i++) {
RiskPool storage pool = riskPools[i];
if (pool.availableFunds >= amount) {
uint256 scoreDiff = creditScore > pool.riskLevel ? creditScore - pool.riskLevel : pool.riskLevel - creditScore;
if (scoreDiff < bestMatch) {
bestMatch = scoreDiff;
selectedPoolId = i;
}
}
}
return selectedPoolId;
}
function getPoolDetails(uint256 _poolId) external view returns (uint256, uint256, uint256) {
require(_poolId < poolCount, "Invalid pool ID");
RiskPool storage pool = riskPools[_poolId];
return (pool.totalFunds, pool.availableFunds, pool.riskLevel);
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
}