-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathreserve.sol
More file actions
178 lines (150 loc) · 5.9 KB
/
Copy pathreserve.sol
File metadata and controls
178 lines (150 loc) · 5.9 KB
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
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.6.12;
import "tinlake-math/math.sol";
import "tinlake-auth/auth.sol";
interface ERC20Like {
function balanceOf(address) external view returns (uint256);
function transferFrom(address, address, uint) external returns (bool);
function mint(address, uint256) external;
function burn(address, uint256) external;
function totalSupply() external view returns (uint256);
function approve(address, uint) external;
}
interface ShelfLike {
function balanceRequest() external returns (bool requestWant, uint256 amount);
}
interface AssessorLike {
function repaymentUpdate(uint amount) external;
function borrowUpdate(uint amount) external;
}
interface LendingAdapter {
function remainingCredit() external view returns (uint);
function draw(uint amount) external;
function wipe(uint amount) external;
function debt() external returns(uint);
function activated() external view returns(bool);
}
// The reserve keeps track of the currency and the bookkeeping
// of the total balance
contract Reserve is Math, Auth {
ERC20Like public currency;
ShelfLike public shelf;
AssessorLike public assessor;
// additional currency from lending adapters
// for deactivating set to address(0)
LendingAdapter public lending;
// currency available for borrowing new loans
uint256 public currencyAvailable;
// address or contract which holds the currency
// by default it is address(this)
address pot;
// total currency in the reserve
uint public balance_;
constructor(address currency_) {
wards[msg.sender] = 1;
currency = ERC20Like(currency_);
pot = address(this);
currency.approve(pot, type(uint256).max);
}
function file(bytes32 what, uint amount) public auth {
if (what == "currencyAvailable") {
currencyAvailable = amount;
} else revert();
}
function depend(bytes32 contractName, address addr) public auth {
if (contractName == "shelf") {
shelf = ShelfLike(addr);
} else if (contractName == "currency") {
currency = ERC20Like(addr);
if (pot == address(this)) {
currency.approve(pot, type(uint256).max);
}
} else if (contractName == "assessor") {
assessor = AssessorLike(addr);
} else if (contractName == "pot") {
pot = addr;
} else if (contractName == "lending") {
lending = LendingAdapter(addr);
} else revert();
}
// returns the amount of currency currently in the reserve
function totalBalance() public view returns (uint) {
return balance_;
}
// return the amount of currency and the available currency from the lending adapter
function totalBalanceAvailable() public view returns (uint) {
if(address(lending) == address(0)) {
return balance_;
}
return safeAdd(balance_, lending.remainingCredit());
}
// deposits currency in the the reserve
function deposit(uint currencyAmount) public auth {
if(currencyAmount == 0) return;
_deposit(msg.sender, currencyAmount);
}
// hard deposit guarantees that the currency stays in the reserve
function hardDeposit(uint currencyAmount) public auth {
_depositAction(msg.sender, currencyAmount);
}
function _depositAction(address usr, uint currencyAmount) internal {
require(currency.transferFrom(usr, pot, currencyAmount), "reserve-deposit-failed");
balance_ = safeAdd(balance_, currencyAmount);
}
function _deposit(address usr, uint currencyAmount) internal {
_depositAction(usr, currencyAmount);
if(address(lending) != address(0) && lending.debt() > 0 && lending.activated()) {
uint wipeAmount = lending.debt();
uint available = currency.balanceOf(pot);
if(available < wipeAmount) {
wipeAmount = available;
}
lending.wipe(wipeAmount);
}
}
// remove currency from the reserve
function payout(uint currencyAmount) public auth {
if(currencyAmount == 0) return;
_payout(msg.sender, currencyAmount);
}
function _payoutAction(address usr, uint currencyAmount) internal {
require(currency.transferFrom(pot, usr, currencyAmount), "reserve-payout-failed");
balance_ = safeSub(balance_, currencyAmount);
}
// hard payout guarantees that the currency stays in the reserve
function hardPayout(uint currencyAmount) public auth {
_payoutAction(msg.sender, currencyAmount);
}
function _payout(address usr, uint currencyAmount) internal {
uint reserveBalance = currency.balanceOf(pot);
if (currencyAmount > reserveBalance && address(lending) != address(0) && lending.activated()) {
uint drawAmount = safeSub(currencyAmount, reserveBalance);
uint left = lending.remainingCredit();
if(drawAmount > left) {
drawAmount = left;
}
lending.draw(drawAmount);
}
_payoutAction(usr, currencyAmount);
}
// balance handles currency requests from the borrower side
// currency is moved between shelf and reserve if needed
function balance() public {
(bool requestWant, uint256 currencyAmount) = shelf.balanceRequest();
if(currencyAmount == 0) {
return;
}
if (requestWant) {
require(
currencyAvailable >= currencyAmount,
"not-enough-currency-reserve"
);
currencyAvailable = safeSub(currencyAvailable, currencyAmount);
_payout(address(shelf), currencyAmount);
assessor.borrowUpdate(currencyAmount);
return;
}
_deposit(address(shelf), currencyAmount);
assessor.repaymentUpdate(currencyAmount);
}
}