-
Notifications
You must be signed in to change notification settings - Fork 411
Add a Custom Curve Pool Implementation Handling RAI #144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
stefanionescu
wants to merge
27
commits into
curvefi:master
Choose a base branch
from
reflexer-labs:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
218168b
feat: RAI pool, new meta pool customised to handle redemption rate
stobiewan a0ee346
test: RAI pool, add pool specific tests focused on moving redemption …
stobiewan 314dde1
docs: RAI pool, add temporary doc detailing changes made for RAI pool
stobiewan a6a29eb
Merge pull request #3 from stobiewan/pool_rai
stefanionescu f6cf407
Remove RAI readme
stefanionescu 6c5d52e
locking pragma for RAI pool
fabiohild 3104128
Merge pull request #4 from reflexer-labs/audit
stefanionescu f793ac7
changing state vars to uint256
fabiohild 291b16f
Uniting _rates instead of single vars for each rate
fabiohild 016c7a6
removing unreachable code on get_dy_underlying
fabiohild 6657b28
Improving invariant_check_virtual_price test on RAI integration tests
fabiohild 60824a3
Curve review points
fabiohild f6492b4
updating virtual_price_2 formula
fabiohild 0046953
include sqrt in the contract, remove donate_admin_fees
fabiohild 73397ec
fixing scaling issue on get_virtual_price_2
fabiohild d52df33
Merge pull request #5 from reflexer-labs/curve-review
stefanionescu d3654b7
Code: Raiust pool, Two Coin Pool with peggie & non-peggie
twpony d8799bf
Test: Raiust pool, add specific test for moving redemption price
twpony 43f0ae7
Test: Raiust pool, add specific test for moving redemption price
twpony 19365c5
Doc: Raiust pool, readme to describe implementation
twpony 9d44747
Merge pull request #7 from fs1028/master
fabiohild f1954a8
renaming RATES, adding virtual_price_2
fabiohild 763fa6f
removing temp README
fabiohild 087915e
Merge pull request #8 from reflexer-labs/gitcoin-submission-review
fabiohild b60d49c
Rename StableSwapRaiust.vy to StableSwapRaiUst.vy
stefanionescu c612bcf
Update README.md
stefanionescu 2297ab0
Update README.md
stefanionescu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| __pycache__ | ||
| .history | ||
| .hypothesis/ | ||
| .idea/ | ||
| build/ | ||
| reports/ | ||
| .venv/ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,312 @@ | ||
| # @version 0.2.12 | ||
| # (c) Curve.Fi, 2021 | ||
| # Math for crypto pools | ||
| # | ||
| # Unless otherwise agreed on, only contracts owned by Curve DAO or | ||
| # Swiss Stake GmbH are allowed to call this contract. | ||
|
|
||
| N_COINS: constant(int128) = 3 # <- change | ||
| A_MULTIPLIER: constant(uint256) = 10000 | ||
|
|
||
| MIN_GAMMA: constant(uint256) = 10**10 | ||
| MAX_GAMMA: constant(uint256) = 5 * 10**16 | ||
|
|
||
| MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 100 | ||
| MAX_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER * 1000 | ||
|
|
||
|
|
||
| @internal | ||
| @pure | ||
| def sort(A0: uint256[N_COINS]) -> uint256[N_COINS]: | ||
| """ | ||
| Insertion sort from high to low | ||
| """ | ||
| A: uint256[N_COINS] = A0 | ||
| for i in range(1, N_COINS): | ||
| x: uint256 = A[i] | ||
| cur: uint256 = i | ||
| for j in range(N_COINS): | ||
| y: uint256 = A[cur-1] | ||
| if y > x: | ||
| break | ||
| A[cur] = y | ||
| cur -= 1 | ||
| if cur == 0: | ||
| break | ||
| A[cur] = x | ||
| return A | ||
|
|
||
|
|
||
| @internal | ||
| @view | ||
| def _geometric_mean(unsorted_x: uint256[N_COINS], sort: bool = True) -> uint256: | ||
| """ | ||
| (x[0] * x[1] * ...) ** (1/N) | ||
| """ | ||
| x: uint256[N_COINS] = unsorted_x | ||
| if sort: | ||
| x = self.sort(x) | ||
| D: uint256 = x[0] | ||
| diff: uint256 = 0 | ||
| for i in range(255): | ||
| D_prev: uint256 = D | ||
| tmp: uint256 = 10**18 | ||
| for _x in x: | ||
| tmp = tmp * _x / D | ||
| D = D * ((N_COINS - 1) * 10**18 + tmp) / (N_COINS * 10**18) | ||
| if D > D_prev: | ||
| diff = D - D_prev | ||
| else: | ||
| diff = D_prev - D | ||
| if diff <= 1 or diff * 10**18 < D: | ||
| return D | ||
| raise "Did not converge" | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def geometric_mean(unsorted_x: uint256[N_COINS], sort: bool = True) -> uint256: | ||
| return self._geometric_mean(unsorted_x, sort) | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def reduction_coefficient(x: uint256[N_COINS], fee_gamma: uint256) -> uint256: | ||
| """ | ||
| fee_gamma / (fee_gamma + (1 - K)) | ||
| where | ||
| K = prod(x) / (sum(x) / N)**N | ||
| (all normalized to 1e18) | ||
| """ | ||
| K: uint256 = 10**18 | ||
| S: uint256 = 0 | ||
| for x_i in x: | ||
| S += x_i | ||
| # Could be good to pre-sort x, but it is used only for dynamic fee, | ||
| # so that is not so important | ||
| for x_i in x: | ||
| K = K * N_COINS * x_i / S | ||
| if fee_gamma > 0: | ||
| K = fee_gamma * 10**18 / (fee_gamma + 10**18 - K) | ||
| return K | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def newton_D(ANN: uint256, gamma: uint256, x_unsorted: uint256[N_COINS]) -> uint256: | ||
| """ | ||
| Finding the invariant using Newton method. | ||
| ANN is higher by the factor A_MULTIPLIER | ||
| ANN is already A * N**N | ||
|
|
||
| Currently uses 60k gas | ||
| """ | ||
| # Safety checks | ||
| assert ANN > MIN_A - 1 and ANN < MAX_A + 1 # dev: unsafe values A | ||
| assert gamma > MIN_GAMMA - 1 and gamma < MAX_GAMMA + 1 # dev: unsafe values gamma | ||
|
|
||
| # Initial value of invariant D is that for constant-product invariant | ||
| x: uint256[N_COINS] = self.sort(x_unsorted) | ||
|
|
||
| assert x[0] > 10**9 - 1 and x[0] < 10**15 * 10**18 + 1 # dev: unsafe values x[0] | ||
| for i in range(1, N_COINS): | ||
| frac: uint256 = x[i] * 10**18 / x[0] | ||
| assert frac > 10**11-1 # dev: unsafe values x[i] | ||
|
|
||
| D: uint256 = N_COINS * self._geometric_mean(x, False) | ||
| S: uint256 = 0 | ||
| for x_i in x: | ||
| S += x_i | ||
|
|
||
| for i in range(255): | ||
| D_prev: uint256 = D | ||
|
|
||
| K0: uint256 = 10**18 | ||
| for _x in x: | ||
| K0 = K0 * _x * N_COINS / D | ||
|
|
||
| _g1k0: uint256 = gamma + 10**18 | ||
| if _g1k0 > K0: | ||
| _g1k0 = _g1k0 - K0 + 1 | ||
| else: | ||
| _g1k0 = K0 - _g1k0 + 1 | ||
|
|
||
| # D / (A * N**N) * _g1k0**2 / gamma**2 | ||
| mul1: uint256 = 10**18 * D / gamma * _g1k0 / gamma * _g1k0 * A_MULTIPLIER / ANN | ||
|
|
||
| # 2*N*K0 / _g1k0 | ||
| mul2: uint256 = (2 * 10**18) * N_COINS * K0 / _g1k0 | ||
|
|
||
| neg_fprime: uint256 = (S + S * mul2 / 10**18) + mul1 * N_COINS / K0 - mul2 * D / 10**18 | ||
|
|
||
| # D -= f / fprime | ||
| D_plus: uint256 = D * (neg_fprime + S) / neg_fprime | ||
| D_minus: uint256 = D*D / neg_fprime | ||
| if 10**18 > K0: | ||
| D_minus += D * (mul1 / neg_fprime) / 10**18 * (10**18 - K0) / K0 | ||
| else: | ||
| D_minus -= D * (mul1 / neg_fprime) / 10**18 * (K0 - 10**18) / K0 | ||
|
|
||
| if D_plus > D_minus: | ||
| D = D_plus - D_minus | ||
| else: | ||
| D = (D_minus - D_plus) / 2 | ||
|
|
||
| diff: uint256 = 0 | ||
| if D > D_prev: | ||
| diff = D - D_prev | ||
| else: | ||
| diff = D_prev - D | ||
| if diff * 10**14 < max(10**16, D): # Could reduce precision for gas efficiency here | ||
| # Test that we are safe with the next newton_y | ||
| for _x in x: | ||
| frac: uint256 = _x * 10**18 / D | ||
| assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe values x[i] | ||
| return D | ||
|
|
||
| raise "Did not converge" | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def newton_y(ANN: uint256, gamma: uint256, x: uint256[N_COINS], D: uint256, i: uint256) -> uint256: | ||
| """ | ||
| Calculating x[i] given other balances x[0..N_COINS-1] and invariant D | ||
| ANN = A * N**N | ||
| """ | ||
| # Safety checks | ||
| assert ANN > MIN_A - 1 and ANN < MAX_A + 1 # dev: unsafe values A | ||
| assert gamma > MIN_GAMMA - 1 and gamma < MAX_GAMMA + 1 # dev: unsafe values gamma | ||
| assert D > 10**17 - 1 and D < 10**15 * 10**18 + 1 # dev: unsafe values D | ||
| for k in range(3): | ||
| if k != i: | ||
| frac: uint256 = x[k] * 10**18 / D | ||
| assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe values x[i] | ||
|
|
||
| y: uint256 = D / N_COINS | ||
| K0_i: uint256 = 10**18 | ||
| S_i: uint256 = 0 | ||
|
|
||
| x_sorted: uint256[N_COINS] = x | ||
| x_sorted[i] = 0 | ||
| x_sorted = self.sort(x_sorted) # From high to low | ||
|
|
||
| convergence_limit: uint256 = max(max(x_sorted[0] / 10**14, D / 10**14), 100) | ||
| for j in range(2, N_COINS+1): | ||
| _x: uint256 = x_sorted[N_COINS-j] | ||
| y = y * D / (_x * N_COINS) # Small _x first | ||
| S_i += _x | ||
| for j in range(N_COINS-1): | ||
| K0_i = K0_i * x_sorted[j] * N_COINS / D # Large _x first | ||
|
|
||
| for j in range(255): | ||
| y_prev: uint256 = y | ||
|
|
||
| K0: uint256 = K0_i * y * N_COINS / D | ||
| S: uint256 = S_i + y | ||
|
|
||
| _g1k0: uint256 = gamma + 10**18 | ||
| if _g1k0 > K0: | ||
| _g1k0 = _g1k0 - K0 + 1 | ||
| else: | ||
| _g1k0 = K0 - _g1k0 + 1 | ||
|
|
||
| # D / (A * N**N) * _g1k0**2 / gamma**2 | ||
| mul1: uint256 = 10**18 * D / gamma * _g1k0 / gamma * _g1k0 * A_MULTIPLIER / ANN | ||
|
|
||
| # 2*K0 / _g1k0 | ||
| mul2: uint256 = 10**18 + (2 * 10**18) * K0 / _g1k0 | ||
|
|
||
| yfprime: uint256 = 10**18 * y + S * mul2 + mul1 | ||
| _dyfprime: uint256 = D * mul2 | ||
| if yfprime < _dyfprime: | ||
| y = y_prev / 2 | ||
| continue | ||
| else: | ||
| yfprime -= _dyfprime | ||
| fprime: uint256 = yfprime / y | ||
|
|
||
| # y -= f / f_prime; y = (y * fprime - f) / fprime | ||
| # y = (yfprime + 10**18 * D - 10**18 * S) // fprime + mul1 // fprime * (10**18 - K0) // K0 | ||
| y_minus: uint256 = mul1 / fprime | ||
| y_plus: uint256 = (yfprime + 10**18 * D) / fprime + y_minus * 10**18 / K0 | ||
| y_minus += 10**18 * S / fprime | ||
|
|
||
| if y_plus < y_minus: | ||
| y = y_prev / 2 | ||
| else: | ||
| y = y_plus - y_minus | ||
|
|
||
| diff: uint256 = 0 | ||
| if y > y_prev: | ||
| diff = y - y_prev | ||
| else: | ||
| diff = y_prev - y | ||
| if diff < max(convergence_limit, y / 10**14): | ||
| frac: uint256 = y * 10**18 / D | ||
| assert (frac > 10**16 - 1) and (frac < 10**20 + 1) # dev: unsafe value for y | ||
| return y | ||
|
|
||
| raise "Did not converge" | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def halfpow(power: uint256, precision: uint256) -> uint256: | ||
| """ | ||
| 1e18 * 0.5 ** (power/1e18) | ||
|
|
||
| Inspired by: https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L128 | ||
| """ | ||
| intpow: uint256 = power / 10**18 | ||
| otherpow: uint256 = power - intpow * 10**18 | ||
| if intpow > 59: | ||
| return 0 | ||
| result: uint256 = 10**18 / (2**intpow) | ||
| if otherpow == 0: | ||
| return result | ||
|
|
||
| term: uint256 = 10**18 | ||
| x: uint256 = 5 * 10**17 | ||
| S: uint256 = 10**18 | ||
| neg: bool = False | ||
|
|
||
| for i in range(1, 256): | ||
| K: uint256 = i * 10**18 | ||
| c: uint256 = K - 10**18 | ||
| if otherpow > c: | ||
| c = otherpow - c | ||
| neg = not neg | ||
| else: | ||
| c -= otherpow | ||
| term = term * (c * x / 10**18) / K | ||
| if neg: | ||
| S -= term | ||
| else: | ||
| S += term | ||
| if term < precision: | ||
| return result * S / 10**18 | ||
|
|
||
| raise "Did not converge" | ||
|
|
||
|
|
||
| @external | ||
| @view | ||
| def sqrt_int(x: uint256) -> uint256: | ||
| """ | ||
| Originating from: https://github.com/vyperlang/vyper/issues/1266 | ||
| """ | ||
|
|
||
| if x == 0: | ||
| return 0 | ||
|
|
||
| z: uint256 = (x + 10**18) / 2 | ||
| y: uint256 = x | ||
|
|
||
| for i in range(256): | ||
| if z == y: | ||
| return y | ||
| y = z | ||
| z = (x * 10**18 / z + z) / 2 | ||
|
|
||
| raise "Did not converge" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you