DeFiPy 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DeFiPy-0.0.1/DeFiPy.egg-info/PKG-INFO +28 -0
- DeFiPy-0.0.1/DeFiPy.egg-info/SOURCES.txt +18 -0
- DeFiPy-0.0.1/DeFiPy.egg-info/dependency_links.txt +1 -0
- DeFiPy-0.0.1/DeFiPy.egg-info/not-zip-safe +1 -0
- DeFiPy-0.0.1/DeFiPy.egg-info/top_level.txt +2 -0
- DeFiPy-0.0.1/LICENSE.txt +21 -0
- DeFiPy-0.0.1/PKG-INFO +28 -0
- DeFiPy-0.0.1/README.md +17 -0
- DeFiPy-0.0.1/python/prod/cst/exchg/StableswapExchange.py +489 -0
- DeFiPy-0.0.1/python/prod/cst/exchg/StableswapPoolMath.py +674 -0
- DeFiPy-0.0.1/python/prod/cst/exchg/__init__.py +2 -0
- DeFiPy-0.0.1/python/prod/cst/factory/StableswapFactory.py +40 -0
- DeFiPy-0.0.1/python/prod/cst/factory/__init__.py +1 -0
- DeFiPy-0.0.1/python/prod/erc/ERC20.py +63 -0
- DeFiPy-0.0.1/python/prod/erc/__init__.py +1 -0
- DeFiPy-0.0.1/python/prod/group/StableswapERC20Group.py +83 -0
- DeFiPy-0.0.1/python/prod/group/__init__.py +1 -0
- DeFiPy-0.0.1/setup.cfg +7 -0
- DeFiPy-0.0.1/setup.py +26 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: DeFiPy
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: DeFi for Python
|
|
5
|
+
Home-page: http://github.com/icmoore/defipy
|
|
6
|
+
Author: icmoore
|
|
7
|
+
Author-email: utiliwire@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
|
|
12
|
+
# defipy
|
|
13
|
+
Python package for DeFi modelling
|
|
14
|
+
* Currently in Beta (version 0.0.1) until fully tested and analyzed
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
To install package:
|
|
18
|
+
```
|
|
19
|
+
> git clone https://github.com/icmoore/defipy
|
|
20
|
+
> pip install .
|
|
21
|
+
```
|
|
22
|
+
or
|
|
23
|
+
```
|
|
24
|
+
> pip install DeFiPy
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## DeFi Overview
|
|
28
|
+
* Python for DeFi analytics
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
setup.cfg
|
|
4
|
+
setup.py
|
|
5
|
+
DeFiPy.egg-info/PKG-INFO
|
|
6
|
+
DeFiPy.egg-info/SOURCES.txt
|
|
7
|
+
DeFiPy.egg-info/dependency_links.txt
|
|
8
|
+
DeFiPy.egg-info/not-zip-safe
|
|
9
|
+
DeFiPy.egg-info/top_level.txt
|
|
10
|
+
python/prod/cst/exchg/StableswapExchange.py
|
|
11
|
+
python/prod/cst/exchg/StableswapPoolMath.py
|
|
12
|
+
python/prod/cst/exchg/__init__.py
|
|
13
|
+
python/prod/cst/factory/StableswapFactory.py
|
|
14
|
+
python/prod/cst/factory/__init__.py
|
|
15
|
+
python/prod/erc/ERC20.py
|
|
16
|
+
python/prod/erc/__init__.py
|
|
17
|
+
python/prod/group/StableswapERC20Group.py
|
|
18
|
+
python/prod/group/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
DeFiPy-0.0.1/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Ian Moore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
DeFiPy-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: DeFiPy
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: DeFi for Python
|
|
5
|
+
Home-page: http://github.com/icmoore/defipy
|
|
6
|
+
Author: icmoore
|
|
7
|
+
Author-email: utiliwire@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
|
|
12
|
+
# defipy
|
|
13
|
+
Python package for DeFi modelling
|
|
14
|
+
* Currently in Beta (version 0.0.1) until fully tested and analyzed
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
To install package:
|
|
18
|
+
```
|
|
19
|
+
> git clone https://github.com/icmoore/defipy
|
|
20
|
+
> pip install .
|
|
21
|
+
```
|
|
22
|
+
or
|
|
23
|
+
```
|
|
24
|
+
> pip install DeFiPy
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## DeFi Overview
|
|
28
|
+
* Python for DeFi analytics
|
DeFiPy-0.0.1/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# defipy
|
|
2
|
+
Python package for DeFi modelling
|
|
3
|
+
* Currently in Beta (version 0.0.1) until fully tested and analyzed
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
To install package:
|
|
7
|
+
```
|
|
8
|
+
> git clone https://github.com/icmoore/defipy
|
|
9
|
+
> pip install .
|
|
10
|
+
```
|
|
11
|
+
or
|
|
12
|
+
```
|
|
13
|
+
> pip install DeFiPy
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## DeFi Overview
|
|
17
|
+
* Python for DeFi analytics
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# StableswapExchange.py
|
|
2
|
+
# Author: Ian Moore ( imoore@syscoin.org )
|
|
3
|
+
# Date: Oct 2023
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from python.prod.group import StableswapERC20Group
|
|
7
|
+
from python.prod.cst.factory import StableswapFactory
|
|
8
|
+
from python.prod.cst.exchg.StableswapPoolMath import StableswapPoolMath
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
|
|
12
|
+
GWEI_PRECISION = 18
|
|
13
|
+
GWEI_SWAP_FEE = 1000000
|
|
14
|
+
MINIMUM_LIQUIDITY = 1e-15
|
|
15
|
+
EXIT_FEE = 0
|
|
16
|
+
|
|
17
|
+
class StableswapExchange():
|
|
18
|
+
|
|
19
|
+
def __init__(self, creator: StableswapFactory, tkn_group : StableswapERC20Group, symbol: str, addr : str) -> None:
|
|
20
|
+
self.factory = creator
|
|
21
|
+
self.tkn_group = tkn_group
|
|
22
|
+
self.name = tkn_group.get_name()
|
|
23
|
+
self.symbol = symbol
|
|
24
|
+
self.addr = addr
|
|
25
|
+
self.tkn_reserves = {}
|
|
26
|
+
self.tkn_decimals = {}
|
|
27
|
+
self.tkn_fees = {}
|
|
28
|
+
self.collected_fees = {}
|
|
29
|
+
self.liquidity_providers = {}
|
|
30
|
+
self.total_supply = 0
|
|
31
|
+
self.last_liquidity_deposit = 0
|
|
32
|
+
self.math_pool = None
|
|
33
|
+
self.joined = False
|
|
34
|
+
|
|
35
|
+
def info(self):
|
|
36
|
+
if(self.total_supply == 0):
|
|
37
|
+
print(f"Stableswap Exchange: {self.name} ({self.symbol})")
|
|
38
|
+
print(f"Coins: {self.tkn_group.get_coins_str()}")
|
|
39
|
+
print(f"Liquidity: {self.total_supply} \n")
|
|
40
|
+
else:
|
|
41
|
+
reserve_str = " | ".join([f'{tkn_nm} = {self.tkn_reserves[tkn_nm]:.2f}' for tkn_nm in self.tkn_reserves])
|
|
42
|
+
print(f"Stableswap Exchange: {self.name} ({self.symbol})")
|
|
43
|
+
print(f"Coins: {self.tkn_group.get_coins_str()}")
|
|
44
|
+
print(f"Reserves: {reserve_str}")
|
|
45
|
+
print(f"Liquidity: {self.total_supply} \n")
|
|
46
|
+
|
|
47
|
+
def join_pool(self, tkn_group : StableswapERC20Group, ampl_coeff : float, to: str):
|
|
48
|
+
|
|
49
|
+
""" join_pool
|
|
50
|
+
|
|
51
|
+
Initialize a Stableswap pool, and add liquidity for all asset deposit
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
-------
|
|
55
|
+
tkn_group : StableswapERC20Group
|
|
56
|
+
Group of ERC20 objects
|
|
57
|
+
amt_shares_in : float
|
|
58
|
+
Amount of pool shares in
|
|
59
|
+
to : str
|
|
60
|
+
User name/address
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
if(not self.joined):
|
|
64
|
+
self.tkn_group = tkn_group
|
|
65
|
+
self.tkn_reserves = tkn_group.get_balances().copy()
|
|
66
|
+
self.tkn_decimals = tkn_group.get_decimals().copy()
|
|
67
|
+
|
|
68
|
+
decimal_amts = list(self.tkn_group.get_decimal_amts().values())
|
|
69
|
+
rates = list(self.tkn_group.get_rates().values())
|
|
70
|
+
|
|
71
|
+
self.math_pool = StableswapPoolMath(A = ampl_coeff, D = decimal_amts, n = len(rates),
|
|
72
|
+
rates = rates, fee = GWEI_SWAP_FEE)
|
|
73
|
+
amt_liquidity_in = self._dec2amt(self.math_pool.tokens, GWEI_PRECISION)
|
|
74
|
+
|
|
75
|
+
if self.total_supply == 0:
|
|
76
|
+
self._mint('init_account', MINIMUM_LIQUIDITY)
|
|
77
|
+
amt_liquidity_in = amt_liquidity_in - MINIMUM_LIQUIDITY
|
|
78
|
+
|
|
79
|
+
self._mint(to, amt_liquidity_in)
|
|
80
|
+
self.joined = True
|
|
81
|
+
else:
|
|
82
|
+
assert not self.joined, 'Stableswap V1: POOL ALREADY JOINED'
|
|
83
|
+
|
|
84
|
+
def swap(self, amt_tkn_in, tkn_in, tkn_out, to):
|
|
85
|
+
|
|
86
|
+
""" swap
|
|
87
|
+
|
|
88
|
+
Swap output token given input token
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
-------
|
|
92
|
+
amt_tkn_in : float
|
|
93
|
+
Amount of input token requested for swaping
|
|
94
|
+
tkn_in : ERC20
|
|
95
|
+
Input token
|
|
96
|
+
tkn_out : ERC20
|
|
97
|
+
Output token
|
|
98
|
+
to : str
|
|
99
|
+
User name/address
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
assert self.tkn_group.get_token(tkn_in.token_name) and self.tkn_group.get_token(tkn_out.token_name), "Stableswap: TOKEN NOT PART OF GROUP"
|
|
103
|
+
amount_out = self.get_amount_out(amt_tkn_in, tkn_in, tkn_out)
|
|
104
|
+
assert amount_out['tkn_out_amt'] <= tkn_out.token_total, 'Stableswap V1: INSUFFICIENT_OUTPUT_AMOUNT'
|
|
105
|
+
|
|
106
|
+
self._swap_math_pool(amt_tkn_in, tkn_in, tkn_out)
|
|
107
|
+
self.tkn_group.get_token(tkn_in.token_name).deposit(to, amt_tkn_in)
|
|
108
|
+
self._swap(amount_out['tkn_out_amt'], amount_out['tkn_in_fee'], tkn_out, tkn_in, to)
|
|
109
|
+
self._tally_fees(tkn_in, amount_out['tkn_in_fee'])
|
|
110
|
+
|
|
111
|
+
return amount_out
|
|
112
|
+
|
|
113
|
+
def add_liquidity(self, tkn_amt_in, tkn_in, to):
|
|
114
|
+
|
|
115
|
+
""" add_liquidity
|
|
116
|
+
|
|
117
|
+
Add token amounts for LP token
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
-------
|
|
121
|
+
Parameters
|
|
122
|
+
-------
|
|
123
|
+
amt_shares_in : float
|
|
124
|
+
Amount of pool shares coming in
|
|
125
|
+
tkn_in : ERC20
|
|
126
|
+
Input token
|
|
127
|
+
to : str
|
|
128
|
+
User name/address
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
assert self.tkn_group.get_token(tkn_in.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
132
|
+
|
|
133
|
+
tkn_amts_in = [len(self.tkn_reserves)]*5
|
|
134
|
+
|
|
135
|
+
tkn_in_index = self._get_tkn_index(tkn_in.token_name)
|
|
136
|
+
dec_tkn_in = self.tkn_decimals[tkn_in.token_name]
|
|
137
|
+
tkn_in_dec = self._amt2dec(tkn_amt_in, dec_tkn_in)
|
|
138
|
+
|
|
139
|
+
tkn_amts_in[tkn_in_index] = tkn_in_dec
|
|
140
|
+
out = self.math_pool.add_liquidity(tkn_amts_in)
|
|
141
|
+
liquidity_amt_in = self._dec2amt(out, GWEI_PRECISION)
|
|
142
|
+
|
|
143
|
+
self.tkn_group.get_token(tkn_in.token_name).deposit(to, tkn_amt_in)
|
|
144
|
+
self.mint(liquidity_amt_in, tkn_amt_in, tkn_in, to)
|
|
145
|
+
self._tally_fees(tkn_in, 0)
|
|
146
|
+
|
|
147
|
+
return {'liquidity_amt_in': liquidity_amt_in, 'tkn_in_nm': tkn_in.token_name}
|
|
148
|
+
|
|
149
|
+
def remove_liquidity(self, liquidity_amt_out, tkn_out, to):
|
|
150
|
+
|
|
151
|
+
""" remove_liquidity
|
|
152
|
+
|
|
153
|
+
Remove liquidity from specified token in the pool based on lp amount
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
-------
|
|
157
|
+
to : str
|
|
158
|
+
receiving user address
|
|
159
|
+
liquidity_amt_out : float
|
|
160
|
+
lp amount to removed
|
|
161
|
+
tkn_out : ERC20
|
|
162
|
+
Output token
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
assert self.tkn_group.get_token(tkn_out.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
166
|
+
|
|
167
|
+
total_liquidity = self.liquidity_providers[to]
|
|
168
|
+
if liquidity_amt_out >= total_liquidity:
|
|
169
|
+
liquidity_amt_out = total_liquidity
|
|
170
|
+
|
|
171
|
+
tkn_out_index = self._get_tkn_index(tkn_out.token_name)
|
|
172
|
+
dec_tkn_out = self.tkn_decimals[tkn_out.token_name]
|
|
173
|
+
liquidity_out_dec = self._amt2dec(liquidity_amt_out, GWEI_PRECISION)
|
|
174
|
+
|
|
175
|
+
dout, fee = self.math_pool.calc_withdraw_one_coin(liquidity_out_dec, tkn_out_index, True)
|
|
176
|
+
tkn_amt_out_min = self._dec2amt(dout, dec_tkn_out)
|
|
177
|
+
|
|
178
|
+
assert tkn_amt_out_min <= self.tkn_reserves[tkn_out.token_name], 'Stableswap V1: INSUFFICIENT TKN AMOUNT'
|
|
179
|
+
|
|
180
|
+
tkn_out_dec, tkn_out_fee_dec = self.math_pool.remove_liquidity_one_coin(liquidity_out_dec, tkn_out_index, True)
|
|
181
|
+
tkn_out_amt = self._dec2amt(tkn_out_dec, dec_tkn_out)
|
|
182
|
+
tkn_out_fee = self._dec2amt(tkn_out_fee_dec, dec_tkn_out)
|
|
183
|
+
|
|
184
|
+
self.burn(liquidity_amt_out, tkn_out_amt, tkn_out, to)
|
|
185
|
+
self._tally_fees(tkn_out, tkn_out_fee)
|
|
186
|
+
|
|
187
|
+
return {'tkn_out_amt': tkn_out_amt, 'tkn_out_nm': tkn_out.token_name, 'tkn_out_fee':tkn_out_fee}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def burn(self, liquidity_out, tkn_out_amt, tkn_out, _from):
|
|
191
|
+
|
|
192
|
+
""" burn
|
|
193
|
+
|
|
194
|
+
Burn liquidity from token based on amount of liquidity and amount of token
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
-------
|
|
198
|
+
liquidity_out : float
|
|
199
|
+
Amount of liquidity requested for burn
|
|
200
|
+
amt_tkn_out : float
|
|
201
|
+
Amount of token requested for burn
|
|
202
|
+
tkn_out : ERC20
|
|
203
|
+
Output token
|
|
204
|
+
_from : str
|
|
205
|
+
User name/address
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
assert self.tkn_group.get_token(tkn_out.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
209
|
+
|
|
210
|
+
self._burn(_from, liquidity_out)
|
|
211
|
+
self.tkn_group.get_token(tkn_out.token_name).transfer(_from, tkn_out_amt)
|
|
212
|
+
new_balance = self.tkn_group.get_token(tkn_out.token_name).token_total
|
|
213
|
+
self._update(new_balance, tkn_out.token_name)
|
|
214
|
+
|
|
215
|
+
def _burn(self, _from, liquidity_out):
|
|
216
|
+
|
|
217
|
+
""" burn
|
|
218
|
+
|
|
219
|
+
Burn liquidity from token based on amount of liquidity
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
-------
|
|
223
|
+
liquidity_out : float
|
|
224
|
+
Amount of liquidity requested for burn
|
|
225
|
+
_from : str
|
|
226
|
+
User name/address
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
exit_fee = liquidity_out * EXIT_FEE
|
|
230
|
+
available_liquidity = self.liquidity_providers.get(_from)
|
|
231
|
+
self.liquidity_providers[_from] = available_liquidity - liquidity_out - exit_fee
|
|
232
|
+
self.total_supply -= liquidity_out - exit_fee
|
|
233
|
+
|
|
234
|
+
def _swap(self, amt_swap, amt_fee, tkn_out, tkn_in, to):
|
|
235
|
+
|
|
236
|
+
""" _swap
|
|
237
|
+
|
|
238
|
+
Swap output token given input token
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
-------
|
|
242
|
+
amt_out : float
|
|
243
|
+
Amount of input token requested for swaping
|
|
244
|
+
tkn_in : ERC20
|
|
245
|
+
Input token
|
|
246
|
+
tkn_out : ERC20
|
|
247
|
+
Output token
|
|
248
|
+
to : str
|
|
249
|
+
User name/address
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
self.tkn_group.get_token(tkn_out.token_name).transfer(to, amt_swap)
|
|
253
|
+
new_tkn_balances = self.tkn_group.get_balances()
|
|
254
|
+
|
|
255
|
+
res_balance_in = self.tkn_reserves[tkn_in.token_name]
|
|
256
|
+
res_balance_out = self.tkn_reserves[tkn_out.token_name]
|
|
257
|
+
|
|
258
|
+
new_balance_in = new_tkn_balances[tkn_in.token_name]
|
|
259
|
+
new_balance_out = new_tkn_balances[tkn_out.token_name]
|
|
260
|
+
|
|
261
|
+
if new_balance_in > res_balance_in - amt_swap:
|
|
262
|
+
amount_in = new_balance_in - res_balance_in
|
|
263
|
+
else:
|
|
264
|
+
amount_in = 0
|
|
265
|
+
|
|
266
|
+
assert amount_in > 0, 'Stableswap V1: INSUFFICIENT_INPUT_AMOUNT'
|
|
267
|
+
|
|
268
|
+
res_balance_in_adjusted = res_balance_in + amount_in
|
|
269
|
+
res_balance_out_adjusted = res_balance_out - amt_swap
|
|
270
|
+
|
|
271
|
+
lside = round(math.ceil(res_balance_in_adjusted * res_balance_out_adjusted), 8)
|
|
272
|
+
rside = round(math.ceil(new_balance_in * new_balance_out), 8)
|
|
273
|
+
|
|
274
|
+
assert lside == rside , 'Stableswap V1: LP BALANCES NOT ALIGNED TO TKN BALANCES'
|
|
275
|
+
|
|
276
|
+
self._update(new_balance_in, tkn_in.token_name)
|
|
277
|
+
self._update(new_balance_out, tkn_out.token_name)
|
|
278
|
+
self._tally_fees(tkn_in, amt_fee)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _swap_math_pool(self, amt_tkn_in, tkn_in, tkn_out):
|
|
282
|
+
|
|
283
|
+
""" swap_math_pool
|
|
284
|
+
|
|
285
|
+
Given some amount of an asset, quotes an equivalent amount of the other asset
|
|
286
|
+
|
|
287
|
+
Parameters
|
|
288
|
+
-------
|
|
289
|
+
amt_tkn_in : float
|
|
290
|
+
Amount of token requested for quote
|
|
291
|
+
tkn_in : ERC20
|
|
292
|
+
Input token
|
|
293
|
+
tkn_out : ERC20
|
|
294
|
+
Output token
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
assert self.tkn_group.get_token(tkn_in.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
298
|
+
|
|
299
|
+
ind_tkn_in = self._get_tkn_index(tkn_in.token_name)
|
|
300
|
+
ind_tkn_out = self._get_tkn_index(tkn_out.token_name)
|
|
301
|
+
|
|
302
|
+
dec_tkn_in = self.tkn_decimals[tkn_in.token_name]
|
|
303
|
+
dec_tkn_out = self.tkn_decimals[tkn_out.token_name]
|
|
304
|
+
|
|
305
|
+
dx_tkn_in_dec = self._amt2dec(amt_tkn_in, dec_tkn_in)
|
|
306
|
+
out = self.math_pool.exchange(ind_tkn_in, ind_tkn_out, dx_tkn_in_dec)
|
|
307
|
+
|
|
308
|
+
tkn_out_amt = self._dec2amt(out[0], dec_tkn_out)
|
|
309
|
+
tkn_in_fee = self._dec2amt(out[1], dec_tkn_out)
|
|
310
|
+
|
|
311
|
+
return {'tkn_out_amt': tkn_out_amt, 'tkn_in_nm': tkn_in.token_name, 'tkn_in_fee': tkn_in_fee}
|
|
312
|
+
|
|
313
|
+
def get_amount_out(self, amt_tkn_in, tkn_in, tkn_out):
|
|
314
|
+
|
|
315
|
+
""" get_amount_out
|
|
316
|
+
|
|
317
|
+
Given some amount of an asset, quotes an equivalent amount of the other asset
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
-------
|
|
321
|
+
amt_tkn_in : float
|
|
322
|
+
Amount of token requested for quote
|
|
323
|
+
tkn_in : ERC20
|
|
324
|
+
Input token
|
|
325
|
+
tkn_out : ERC20
|
|
326
|
+
Output token
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
assert self.tkn_group.get_token(tkn_in.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
330
|
+
|
|
331
|
+
ind_tkn_in = self._get_tkn_index(tkn_in.token_name)
|
|
332
|
+
ind_tkn_out = self._get_tkn_index(tkn_out.token_name)
|
|
333
|
+
|
|
334
|
+
dec_tkn_in = self.tkn_decimals[tkn_in.token_name]
|
|
335
|
+
dec_tkn_out = self.tkn_decimals[tkn_out.token_name]
|
|
336
|
+
|
|
337
|
+
dx_tkn_in_dec = self._amt2dec(amt_tkn_in, dec_tkn_in)
|
|
338
|
+
out = self.math_pool.get_amount_out(ind_tkn_in, ind_tkn_out, dx_tkn_in_dec)
|
|
339
|
+
|
|
340
|
+
tkn_out_amt = self._dec2amt(out[0], dec_tkn_out)
|
|
341
|
+
tkn_in_fee = self._dec2amt(out[1], dec_tkn_out)
|
|
342
|
+
|
|
343
|
+
return {'tkn_out_amt': tkn_out_amt, 'tkn_in_nm': tkn_in.token_name, 'tkn_in_fee': tkn_in_fee}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def mint(self, new_liquidity, amt_tkn_in, tkn_in, to):
|
|
347
|
+
|
|
348
|
+
""" mint
|
|
349
|
+
|
|
350
|
+
Update reserve amount for specific token in the pool
|
|
351
|
+
|
|
352
|
+
Parameters
|
|
353
|
+
-------
|
|
354
|
+
new_shares : float
|
|
355
|
+
Amount of new pool shares requested for minting
|
|
356
|
+
amt_tkn_in : float
|
|
357
|
+
Input token
|
|
358
|
+
tkn_in : ERC20
|
|
359
|
+
Output token
|
|
360
|
+
to : str
|
|
361
|
+
User name/address
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
tkn_balance = self.tkn_group.get_token(tkn_in.token_name).token_total
|
|
365
|
+
_amt_tkn_in = tkn_balance - self.tkn_reserves[tkn_in.token_name]
|
|
366
|
+
|
|
367
|
+
assert round(_amt_tkn_in,5) == round(amt_tkn_in,5), 'Stableswap V1: MINT ERROR'
|
|
368
|
+
|
|
369
|
+
new_balance = tkn_in.token_total
|
|
370
|
+
self._update(new_balance, tkn_in.token_name)
|
|
371
|
+
self._mint(to, new_liquidity)
|
|
372
|
+
|
|
373
|
+
def _tally_fees(self, tkn, fee):
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
""" _tally_fees
|
|
377
|
+
|
|
378
|
+
Tally fee from swap and record last collected fee
|
|
379
|
+
|
|
380
|
+
Parameters
|
|
381
|
+
-------
|
|
382
|
+
tkn : ERC20
|
|
383
|
+
Token where fees are being collected for
|
|
384
|
+
fee : float
|
|
385
|
+
Fee being collected
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
if tkn.token_name not in self.tkn_fees:
|
|
389
|
+
self.tkn_fees[tkn.token_name] = 0
|
|
390
|
+
|
|
391
|
+
self.tkn_fees[tkn.token_name] += fee
|
|
392
|
+
|
|
393
|
+
def _mint(self, to, value):
|
|
394
|
+
|
|
395
|
+
""" _mint
|
|
396
|
+
|
|
397
|
+
Update reserve amount for specific token in the pool
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
-------
|
|
401
|
+
value : float
|
|
402
|
+
Amount of new pool shares requested for minting
|
|
403
|
+
to : str
|
|
404
|
+
User name/address
|
|
405
|
+
"""
|
|
406
|
+
|
|
407
|
+
if self.liquidity_providers.get(to):
|
|
408
|
+
self.liquidity_providers[to] += value
|
|
409
|
+
else:
|
|
410
|
+
self.liquidity_providers[to] = value
|
|
411
|
+
|
|
412
|
+
self.last_liquidity_deposit = value
|
|
413
|
+
self.total_supply += value
|
|
414
|
+
|
|
415
|
+
def _update(self, new_balance, tkn_nm):
|
|
416
|
+
|
|
417
|
+
""" _update
|
|
418
|
+
|
|
419
|
+
Update reserve amounts specified token
|
|
420
|
+
|
|
421
|
+
Parameters
|
|
422
|
+
-------
|
|
423
|
+
new_balance : float
|
|
424
|
+
New reserve amount of token
|
|
425
|
+
tkn_nm : ERC20
|
|
426
|
+
Name of token being updated
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
self.tkn_reserves[tkn_nm] = new_balance
|
|
430
|
+
|
|
431
|
+
def _get_tkn_index(self, tkn_nm):
|
|
432
|
+
return list(self.tkn_reserves.keys()).index(tkn_nm)
|
|
433
|
+
|
|
434
|
+
def _amt2dec(self, tkn_amt, decimal):
|
|
435
|
+
return int(Decimal(str(tkn_amt))*Decimal(str(10**decimal)))
|
|
436
|
+
|
|
437
|
+
def _dec2amt(self, dec_amt, decimal):
|
|
438
|
+
return float(Decimal(str(dec_amt))/Decimal(str(10**decimal)))
|
|
439
|
+
|
|
440
|
+
def get_math_pool(self):
|
|
441
|
+
return self.math_pool
|
|
442
|
+
|
|
443
|
+
def get_reserve_decimal_amts(self):
|
|
444
|
+
decimal_amts = {}
|
|
445
|
+
token_decimals = self.tkn_group.get_decimals()
|
|
446
|
+
for tkn_nm in token_decimals:
|
|
447
|
+
tkn_amt = self.tkn_reserves[tkn_nm]
|
|
448
|
+
decimal_amts[tkn.token_name] = self._amt2dec(tkn_amt, tkn.token_decimal)
|
|
449
|
+
return decimal_amts
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def get_price(self, base_tkn, opp_tkn, fee = False):
|
|
453
|
+
|
|
454
|
+
""" get_price
|
|
455
|
+
|
|
456
|
+
Get price of select token in the exchange pair
|
|
457
|
+
|
|
458
|
+
Parameters
|
|
459
|
+
-------
|
|
460
|
+
base_tkn : float
|
|
461
|
+
Base token request for price quote
|
|
462
|
+
opp_tkn : ERC20
|
|
463
|
+
Denomination token of price quote
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
assert self.tkn_group.get_token(base_tkn.token_name), 'Stableswap V1: TOKEN NOT PART OF GROUP'
|
|
467
|
+
|
|
468
|
+
base_tkn_index = self._get_tkn_index(base_tkn.token_name)
|
|
469
|
+
opp_tkn_index = self._get_tkn_index(opp_tkn.token_name)
|
|
470
|
+
|
|
471
|
+
return self.math_pool.dydx(base_tkn_index, opp_tkn_index, use_fee=fee)
|
|
472
|
+
|
|
473
|
+
def get_reserve(self, token):
|
|
474
|
+
|
|
475
|
+
""" get_reserve
|
|
476
|
+
|
|
477
|
+
Get reserve amount of select token in the pool
|
|
478
|
+
|
|
479
|
+
Parameters
|
|
480
|
+
-------
|
|
481
|
+
token : ERC20
|
|
482
|
+
ERC20 token
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
return self.tkn_reserves[token.token_name]
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|