-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSemilogMonetaryPolicyWPower.vy
196 lines (159 loc) · 6.26 KB
/
SemilogMonetaryPolicyWPower.vy
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
188
189
190
191
192
193
194
195
196
# @version 0.3.10
"""
@title SemiLog monetary policy
@notice Monetary policy to calculate borrow rates in lending markets depending on utilization.
Unlike "core" policies, it does not depend on crvUSD price.
Calculated as:
log(rate) = utilization * (log(rate_max) - log(rate_min)) + log(rate_min)
e.g.
rate = rate_min * (rate_max / rate_min)**utilization
@author Curve.fi
@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved
"""
from vyper.interfaces import ERC20
interface Controller:
def total_debt() -> uint256: view
interface Factory:
def admin() -> address: view
event SetRates:
min_rate: uint256
max_rate: uint256
MAX_EXP: constant(uint256) = 1000 * 10**18
MIN_RATE: public(constant(uint256)) = 10**15 / (365 * 86400) # 0.1%
MAX_RATE: public(constant(uint256)) = 10**19 / (365 * 86400) # 1000%
BORROWED_TOKEN: public(immutable(ERC20))
FACTORY: public(immutable(Factory))
min_rate: public(uint256)
max_rate: public(uint256)
log_min_rate: public(int256)
log_max_rate: public(int256)
exponent: public(int256)
@external
def __init__(borrowed_token: ERC20, min_rate: uint256, max_rate: uint256, exponent: int256):
assert min_rate >= MIN_RATE and max_rate <= MAX_RATE and min_rate <= max_rate, "Wrong rates"
assert exponent >= 1 and exponent <= 4, "Exponent must be between 1 and 4"
BORROWED_TOKEN = borrowed_token
self.min_rate = min_rate
self.max_rate = max_rate
self.log_min_rate = self.ln_int(min_rate)
self.log_max_rate = self.ln_int(max_rate)
self.exponent = exponent
FACTORY = Factory(msg.sender)
### MATH ###
@internal
@pure
def exp(power: int256) -> uint256:
if power <= -41446531673892821376:
return 0
if power >= 135305999368893231589:
# Return MAX_EXP when we are in overflow mode
return MAX_EXP
x: int256 = unsafe_div(unsafe_mul(power, 2**96), 10**18)
k: int256 = unsafe_div(
unsafe_add(
unsafe_div(unsafe_mul(x, 2**96), 54916777467707473351141471128),
2**95),
2**96)
x = unsafe_sub(x, unsafe_mul(k, 54916777467707473351141471128))
y: int256 = unsafe_add(x, 1346386616545796478920950773328)
y = unsafe_add(unsafe_div(unsafe_mul(y, x), 2**96), 57155421227552351082224309758442)
p: int256 = unsafe_sub(unsafe_add(y, x), 94201549194550492254356042504812)
p = unsafe_add(unsafe_div(unsafe_mul(p, y), 2**96), 28719021644029726153956944680412240)
p = unsafe_add(unsafe_mul(p, x), (4385272521454847904659076985693276 * 2**96))
q: int256 = x - 2855989394907223263936484059900
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 50020603652535783019961831881945)
q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 533845033583426703283633433725380)
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 3604857256930695427073651918091429)
q = unsafe_sub(unsafe_div(unsafe_mul(q, x), 2**96), 14423608567350463180887372962807573)
q = unsafe_add(unsafe_div(unsafe_mul(q, x), 2**96), 26449188498355588339934803723976023)
return shift(
unsafe_mul(convert(unsafe_div(p, q), uint256), 3822833074963236453042738258902158003155416615667),
unsafe_sub(k, 195))
@internal
@pure
def ln_int(_x: uint256) -> int256:
"""
@notice Logarithm ln() function based on log2. Not very gas-efficient but brief
"""
# adapted from: https://medium.com/coinmonks/9aef8515136e
# and vyper log implementation
# This can be much more optimal but that's not important here
x: uint256 = _x
if _x < 10**18:
x = 10**36 / _x
res: uint256 = 0
for i in range(8):
t: uint256 = 2**(7 - i)
p: uint256 = 2**t
if x >= p * 10**18:
x /= p
res += t * 10**18
d: uint256 = 10**18
for i in range(59): # 18 decimals: math.log2(10**18) == 59.7
if (x >= 2 * 10**18):
res += d
x /= 2
x = x * x / 10**18
d /= 2
# Now res = log2(x)
# ln(x) = log2(x) / log2(e)
result: int256 = convert(res * 10**18 / 1442695040888963328, int256)
if _x >= 10**18:
return result
else:
return -result
@internal
@pure
def power(base: int256, exponent: int256) -> int256:
assert exponent >= 2 and exponent <= 4, "Exponent must be 2, 3, or 4"
if exponent == 2:
return base * base / 10**18
elif exponent == 3:
return base * base * base / 10**36
else: # exponent == 4
squared: int256 = base * base / 10**18
return squared * squared / 10**18
### END MATH ###
@internal
@view
def calculate_rate(_for: address, d_reserves: int256, d_debt: int256) -> uint256:
total_debt: int256 = convert(Controller(_for).total_debt(), int256)
total_reserves: int256 = convert(BORROWED_TOKEN.balanceOf(_for), int256) + total_debt + d_reserves
total_debt += d_debt
assert total_debt >= 0, "Negative debt"
assert total_reserves >= total_debt, "Reserves too small"
if total_debt == 0:
return self.min_rate
else:
log_min_rate: int256 = self.log_min_rate
log_max_rate: int256 = self.log_max_rate
if self.exponent == 1:
return self.exp(total_debt * (log_max_rate - log_min_rate) / total_reserves + log_min_rate)
else:
utilization: int256 = total_debt * 10**18 / total_reserves
utilization_powered: int256 = self.power(utilization, self.exponent)
return self.exp(utilization_powered * (log_max_rate - log_min_rate) / 10**18 + log_min_rate)
@view
@external
def rate(_for: address = msg.sender) -> uint256:
return self.calculate_rate(_for, 0, 0)
@external
def rate_write(_for: address = msg.sender) -> uint256:
return self.calculate_rate(_for, 0, 0)
@external
def set_rates(min_rate: uint256, max_rate: uint256):
assert msg.sender == FACTORY.admin()
assert max_rate >= min_rate
assert min_rate >= MIN_RATE
assert max_rate <= MAX_RATE
if min_rate != self.min_rate:
self.log_min_rate = self.ln_int(min_rate)
if max_rate != self.max_rate:
self.log_max_rate = self.ln_int(max_rate)
self.min_rate = min_rate
self.max_rate = max_rate
log SetRates(min_rate, max_rate)
@view
@external
def future_rate(_for: address, d_reserves: int256, d_debt: int256) -> uint256:
return self.calculate_rate(_for, d_reserves, d_debt)