DeFiPy 1.0.7__tar.gz → 1.0.9__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.
Files changed (54) hide show
  1. {defipy-1.0.7 → defipy-1.0.9}/DeFiPy.egg-info/PKG-INFO +23 -6
  2. {defipy-1.0.7 → defipy-1.0.9}/DeFiPy.egg-info/SOURCES.txt +13 -6
  3. defipy-1.0.9/DeFiPy.egg-info/requires.txt +6 -0
  4. defipy-1.0.9/NOTICE +16 -0
  5. {defipy-1.0.7 → defipy-1.0.9}/PKG-INFO +23 -6
  6. {defipy-1.0.7 → defipy-1.0.9}/README.md +6 -1
  7. {defipy-1.0.7 → defipy-1.0.9}/python/prod/__init__.py +5 -0
  8. defipy-1.0.9/python/prod/agents/ImpermanentLossAgent.py +182 -0
  9. defipy-1.0.9/python/prod/agents/PriceThresholdSwapAgent.py +169 -0
  10. defipy-1.0.9/python/prod/agents/TVLBasedLiquidityExitAgent.py +174 -0
  11. defipy-1.0.9/python/prod/agents/VolumeSpikeNotifierAgent.py +190 -0
  12. defipy-1.0.9/python/prod/agents/__init__.py +4 -0
  13. defipy-1.0.9/python/prod/agents/config/ImpermanentLossConfig.py +28 -0
  14. defipy-1.0.9/python/prod/agents/config/PriceThresholdConfig.py +27 -0
  15. defipy-1.0.9/python/prod/agents/config/TVLExitConfig.py +28 -0
  16. defipy-1.0.9/python/prod/agents/config/VolumeSpikeConfig.py +27 -0
  17. defipy-1.0.9/python/prod/agents/config/__init__.py +7 -0
  18. defipy-1.0.9/python/prod/agents/data/UniswapPoolData.py +26 -0
  19. defipy-1.0.9/python/prod/agents/data/__init__.py +1 -0
  20. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/join/Join.py +3 -0
  21. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/liquidity/AddLiquidity.py +3 -0
  22. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/liquidity/RemoveLiquidity.py +3 -0
  23. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/swap/Swap.py +3 -0
  24. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/client/contract/ExecuteScript.py +16 -2
  25. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/tools/UniswapScriptHelper.py +16 -2
  26. {defipy-1.0.7 → defipy-1.0.9}/setup.py +9 -5
  27. defipy-1.0.7/DeFiPy.egg-info/requires.txt +0 -5
  28. {defipy-1.0.7 → defipy-1.0.9}/DeFiPy.egg-info/dependency_links.txt +0 -0
  29. {defipy-1.0.7 → defipy-1.0.9}/DeFiPy.egg-info/not-zip-safe +0 -0
  30. {defipy-1.0.7 → defipy-1.0.9}/DeFiPy.egg-info/top_level.txt +0 -0
  31. {defipy-1.0.7 → defipy-1.0.9}/LICENSE +0 -0
  32. {defipy-1.0.7 → defipy-1.0.9}/python/prod/analytics/risk/__init__.py +0 -0
  33. {defipy-1.0.7 → defipy-1.0.9}/python/prod/analytics/simulate/__init__.py +0 -0
  34. {defipy-1.0.7 → defipy-1.0.9}/python/prod/erc/__init__.py +0 -0
  35. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/basic/__init__.py +0 -0
  36. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/interest/__init__.py +0 -0
  37. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/interest/ips/__init__.py +0 -0
  38. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/interest/ips/aggregate/__init__.py +0 -0
  39. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/model/__init__.py +0 -0
  40. {defipy-1.0.7 → defipy-1.0.9}/python/prod/math/risk/__init__.py +0 -0
  41. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/__init__.py +0 -0
  42. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/burn/__init__.py +0 -0
  43. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/deposit/__init__.py +0 -0
  44. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/join/__init__.py +0 -0
  45. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/liquidity/__init__.py +0 -0
  46. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/mint/__init__.py +0 -0
  47. {defipy-1.0.7 → defipy-1.0.9}/python/prod/process/swap/__init__.py +0 -0
  48. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/client/__init__.py +0 -0
  49. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/client/contract/__init__.py +0 -0
  50. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/data/__init__.py +0 -0
  51. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/interfaces/__init__.py +0 -0
  52. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/tools/__init__.py +0 -0
  53. {defipy-1.0.7 → defipy-1.0.9}/python/prod/utils/tools/v3/__init__.py +0 -0
  54. {defipy-1.0.7 → defipy-1.0.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: DeFiPy
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: Python SDK for DeFi Analytics, Simulation, and Agents
5
5
  Home-page: http://github.com/defipy-devs/defipy
6
6
  Author: icmoore
@@ -15,11 +15,23 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
15
15
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
+ License-File: NOTICE
18
19
  Requires-Dist: scipy>=1.7.3
20
+ Requires-Dist: pydantic>=2.11.0
19
21
  Requires-Dist: bokeh==3.3.4
20
- Requires-Dist: uniswappy==1.7.4
21
- Requires-Dist: stableswappy==1.0.3
22
- Requires-Dist: balancerpy==1.0.4
22
+ Requires-Dist: uniswappy>=1.7.4
23
+ Requires-Dist: stableswappy>=1.0.3
24
+ Requires-Dist: balancerpy>=1.0.4
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: requires-dist
34
+ Dynamic: summary
23
35
 
24
36
  # DeFiPy: Python SDK for DeFi Analytics and Agents
25
37
 
@@ -227,4 +239,9 @@ Click [dashboard.defipy.org](https://dashboard.defipy.org/) for live link; for m
227
239
 
228
240
  ```
229
241
  > bokeh serve --show python/application/quant_terminal/bokeh_server.py
230
- ```
242
+ ```
243
+
244
+ ## License
245
+ Licensed under the Apache License, Version 2.0.
246
+ See [LICENSE](./LICENSE) and [NOTICE](./NOTICE) for details.
247
+ Portions of this project may include code from third-party projects under compatible open-source licenses.
@@ -1,4 +1,5 @@
1
1
  LICENSE
2
+ NOTICE
2
3
  README.md
3
4
  setup.cfg
4
5
  setup.py
@@ -8,13 +9,19 @@ DeFiPy.egg-info/dependency_links.txt
8
9
  DeFiPy.egg-info/not-zip-safe
9
10
  DeFiPy.egg-info/requires.txt
10
11
  DeFiPy.egg-info/top_level.txt
11
- defipy.egg-info/PKG-INFO
12
- defipy.egg-info/SOURCES.txt
13
- defipy.egg-info/dependency_links.txt
14
- defipy.egg-info/not-zip-safe
15
- defipy.egg-info/requires.txt
16
- defipy.egg-info/top_level.txt
17
12
  python/prod/__init__.py
13
+ python/prod/agents/ImpermanentLossAgent.py
14
+ python/prod/agents/PriceThresholdSwapAgent.py
15
+ python/prod/agents/TVLBasedLiquidityExitAgent.py
16
+ python/prod/agents/VolumeSpikeNotifierAgent.py
17
+ python/prod/agents/__init__.py
18
+ python/prod/agents/config/ImpermanentLossConfig.py
19
+ python/prod/agents/config/PriceThresholdConfig.py
20
+ python/prod/agents/config/TVLExitConfig.py
21
+ python/prod/agents/config/VolumeSpikeConfig.py
22
+ python/prod/agents/config/__init__.py
23
+ python/prod/agents/data/UniswapPoolData.py
24
+ python/prod/agents/data/__init__.py
18
25
  python/prod/analytics/risk/__init__.py
19
26
  python/prod/analytics/simulate/__init__.py
20
27
  python/prod/erc/__init__.py
@@ -0,0 +1,6 @@
1
+ scipy>=1.7.3
2
+ pydantic>=2.11.0
3
+ bokeh==3.3.4
4
+ uniswappy>=1.7.4
5
+ stableswappy>=1.0.3
6
+ balancerpy>=1.0.4
defipy-1.0.9/NOTICE ADDED
@@ -0,0 +1,16 @@
1
+ This software includes code developed by the DeFiPy contributors (https://defipy.org)
2
+
3
+ Copyright 2023–2025 DeFiPy contributors
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+
15
+ We kindly request that if you use DeFiPy in your project, you credit us by
16
+ retaining this notice and optionally linking to https://defipy.org.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: DeFiPy
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: Python SDK for DeFi Analytics, Simulation, and Agents
5
5
  Home-page: http://github.com/defipy-devs/defipy
6
6
  Author: icmoore
@@ -15,11 +15,23 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
15
15
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
+ License-File: NOTICE
18
19
  Requires-Dist: scipy>=1.7.3
20
+ Requires-Dist: pydantic>=2.11.0
19
21
  Requires-Dist: bokeh==3.3.4
20
- Requires-Dist: uniswappy==1.7.4
21
- Requires-Dist: stableswappy==1.0.3
22
- Requires-Dist: balancerpy==1.0.4
22
+ Requires-Dist: uniswappy>=1.7.4
23
+ Requires-Dist: stableswappy>=1.0.3
24
+ Requires-Dist: balancerpy>=1.0.4
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: requires-dist
34
+ Dynamic: summary
23
35
 
24
36
  # DeFiPy: Python SDK for DeFi Analytics and Agents
25
37
 
@@ -227,4 +239,9 @@ Click [dashboard.defipy.org](https://dashboard.defipy.org/) for live link; for m
227
239
 
228
240
  ```
229
241
  > bokeh serve --show python/application/quant_terminal/bokeh_server.py
230
- ```
242
+ ```
243
+
244
+ ## License
245
+ Licensed under the Apache License, Version 2.0.
246
+ See [LICENSE](./LICENSE) and [NOTICE](./NOTICE) for details.
247
+ Portions of this project may include code from third-party projects under compatible open-source licenses.
@@ -204,4 +204,9 @@ Click [dashboard.defipy.org](https://dashboard.defipy.org/) for live link; for m
204
204
 
205
205
  ```
206
206
  > bokeh serve --show python/application/quant_terminal/bokeh_server.py
207
- ```
207
+ ```
208
+
209
+ ## License
210
+ Licensed under the Apache License, Version 2.0.
211
+ See [LICENSE](./LICENSE) and [NOTICE](./NOTICE) for details.
212
+ Portions of this project may include code from third-party projects under compatible open-source licenses.
@@ -1,3 +1,5 @@
1
+ # This file participates in a symbolic cognition substrate.
2
+
1
3
  from defipy.erc import *
2
4
  from defipy.math.basic import *
3
5
  from defipy.math.interest import *
@@ -19,6 +21,9 @@ from defipy.utils.data import *
19
21
  from defipy.utils.client import *
20
22
  from defipy.utils.client.contract import *
21
23
  from defipy.utils.tools import *
24
+ from defipy.agents.config import *
25
+ from defipy.agents.data import *
26
+ from defipy.agents import *
22
27
 
23
28
  from uniswappy.cpt.exchg import *
24
29
  from uniswappy.cpt.factory import *
@@ -0,0 +1,182 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+ from web3scout.utils.connect import ConnectW3
21
+ from web3scout.abi.abi_load import ABILoad
22
+ from web3scout.event.process.retrieve_events import RetrieveEvents
23
+ from web3scout.token.fetch.fetch_token import FetchToken
24
+ from .config import ImpermanentLossConfig
25
+ from .data import UniswapPoolData
26
+ from uniswappy import *
27
+ from web3 import Web3
28
+
29
+ class ImpermanentLossAgent:
30
+ def __init__(self, config: ImpermanentLossConfig, verbose: bool = False):
31
+ self.config = config
32
+ self.abi = ABILoad(self.config.platform, self.config.abi_name) # Load ABI here
33
+ self.connector = ConnectW3(self.config.provider_url) # Web3Scout setup
34
+ self.connector.apply()
35
+ self.verbose = verbose
36
+ self.user_position = config.user_position
37
+ self.exit_percentage = config.exit_percentage
38
+ self.iLoss = None
39
+ self.lp_contract = None
40
+ self.lp_data = None
41
+ self.lp_state = None
42
+
43
+ def init(self):
44
+ self.lp_contract = self._init_lp_contract()
45
+
46
+ reserves = self.lp_contract.functions.getReserves().call()
47
+ token0_address = self.lp_contract.functions.token0().call()
48
+ token1_address = self.lp_contract.functions.token1().call()
49
+ reserve0 = reserves[0]; reserve1 = reserves[1]
50
+
51
+ w3 = self.connector.get_w3()
52
+ FetchERC20 = FetchToken(w3)
53
+ TKN0 = FetchERC20.apply(token0_address)
54
+ TKN1 = FetchERC20.apply(token1_address)
55
+
56
+ self.lp_data = UniswapPoolData(TKN0, TKN1, reserves)
57
+
58
+ def get_connector(self):
59
+ return self.connector
60
+
61
+ def get_abi(self):
62
+ return self.abi
63
+
64
+ def get_w3(self):
65
+ return self.connector.get_w3()
66
+
67
+ def get_contract_instance(self):
68
+ return self.lp_contract
69
+
70
+ def get_lp_data(self):
71
+ return self.lp_data
72
+
73
+ def get_iloss(self):
74
+ return self.iLoss
75
+
76
+ def prime_mock_pool(self, start_block, user_nm = None):
77
+ w3 = self.get_w3()
78
+ fetch_tkn = FetchToken(w3)
79
+
80
+ lp_contract = self._init_lp_contract()
81
+ tkn0_addr = lp_contract.functions.token0().call()
82
+ tkn1_addr = lp_contract.functions.token1().call()
83
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=start_block)
84
+ reserves = lp_contract.functions.getReserves().call(block_identifier=start_block)
85
+
86
+ # Step 2: Define tokens
87
+ tkn0 = fetch_tkn.apply(tkn0_addr)
88
+ tkn1 = fetch_tkn.apply(tkn1_addr)
89
+
90
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
91
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
92
+
93
+ # Step 3: Initialize factory
94
+ factory = UniswapFactory("Pool factory", "0x2")
95
+
96
+ # Step 4: Set up exchange data for V2
97
+ exch_data = UniswapExchangeData(tkn0=tkn0, tkn1=tkn1, symbol="LP", address=self.config.pool_address)
98
+
99
+ # Step 5: Deploy pool
100
+ self.lp_state = factory.deploy(exch_data)
101
+
102
+ # Step 6: Add initial liquidity
103
+ join = Join()
104
+ join.apply(self.lp_state, user_nm, amt0, amt1)
105
+ self.lp_state.total_supply = total_supply # override total supply
106
+
107
+ return self.lp_state
108
+
109
+ def update_mock_pool(self, lp, cur_block):
110
+ w3 = self.get_w3()
111
+ fetch_tkn = FetchToken(w3)
112
+
113
+ lp_contract = self._init_lp_contract()
114
+ tkn0_addr = lp_contract.functions.token0().call()
115
+ tkn1_addr = lp_contract.functions.token1().call()
116
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=int(cur_block))
117
+ reserves = lp_contract.functions.getReserves().call(block_identifier=int(cur_block))
118
+
119
+ tkn0 = self.get_lp_data().tkn0
120
+ tkn1 = self.get_lp_data().tkn1
121
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
122
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
123
+
124
+ prev_total_supply = lp.total_supply
125
+ lp.reserve0 = lp.convert_to_machine(amt0) # override reserve0
126
+ lp.reserve1 = lp.convert_to_machine(amt1) # override reserve1
127
+ lp.total_supply = total_supply # override total supply
128
+ lp.last_liquidity_deposit = abs(prev_total_supply - lp.total_supply)
129
+
130
+ def run_batch(self, lp, tkn, user_nm, events: dict):
131
+ """Process batched Sync events to check TVL and trigger exits."""
132
+ if not events:
133
+ print("No Sync events found in range.")
134
+ return
135
+ for k in events:
136
+ block_num = events[k]['blockNumber']
137
+ self.apply(lp, tkn, user_nm, block_num)
138
+
139
+ def apply(self, lp, tkn, user_nm, block_num):
140
+ """Execute liquidity exit if condition met."""
141
+ self.update_mock_pool(lp, block_num)
142
+ if self.check_condition(tkn, self.config.il_threshold):
143
+ val = self.get_current_position_value(tkn)
144
+ print(f"Block {block_num}: Value ({tkn.token_name}) = {val}, outside loss threshold {self.config.il_threshold}")
145
+ return val
146
+ else:
147
+ print(f"Block {block_num}: Value threshold condition met for {lp.name} LP")
148
+ return None
149
+
150
+ def take_mock_position(self, lp, tkn, user_nm, amt):
151
+ SwapDeposit().apply(lp, tkn, user_nm, amt)
152
+ self.mock_lp_pos_amt = lp.get_last_liquidity_deposit()
153
+ self.iLoss = UniswapImpLoss(lp, self.mock_lp_pos_amt)
154
+ return self.mock_lp_pos_amt
155
+
156
+ def get_impermanent_loss(self) -> float:
157
+ """Calculate impermanent loss percentage based on initial and current reserves."""
158
+ returns_calc = self.iLoss.apply(fees = True)
159
+ return returns_calc
160
+
161
+ def get_current_position_value(self, tkn) -> float:
162
+ current_position_value = self.iLoss.current_position_value(tkn)
163
+ return current_position_value
164
+
165
+ def check_condition(self, tkn, threshold):
166
+ """Check if TVL is below threshold."""
167
+ position_value = self.get_current_position_value(tkn)
168
+ return position_value < threshold
169
+
170
+ def withdraw_mock_position(self, lp, tkn, user_nm, lp_amt = None):
171
+ assert self.mock_lp_pos_amt != None, 'TVLBasedLiquidityExitAgent: MOCK_POSITION_UNAVAILABLE'
172
+ lp_amt = self.mock_lp_pos_amt if lp_amt == None else lp_amt
173
+ tkn_amt = LPQuote(False).get_amount_from_lp(lp, tkn0, lp_amt)
174
+ amount_out = WithdrawSwap().apply(lp, tkn0, user_nm, tkn_amt)
175
+ return amount_out
176
+
177
+ def _init_lp_contract(self):
178
+ pair_address = self.config.pool_address
179
+ w3 = self.get_w3()
180
+ abi_obj = self.get_abi()
181
+ lp_contract = abi_obj.apply(w3, pair_address)
182
+ return lp_contract
@@ -0,0 +1,169 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from web3scout.event.process.retrieve_events import RetrieveEvents
20
+ from web3scout.utils.connect import ConnectW3
21
+ from web3scout.abi.abi_load import ABILoad
22
+ from web3scout.event.process.retrieve_events import RetrieveEvents
23
+ from web3scout.token.fetch.fetch_token import FetchToken
24
+ from web3scout.enums.event_type_enum import EventTypeEnum as EventType
25
+ from .config import PriceThresholdConfig
26
+ from .data import UniswapPoolData
27
+ from uniswappy import *
28
+
29
+ class PriceThresholdSwapAgent:
30
+ def __init__(self, config: PriceThresholdConfig, verbose: bool = False):
31
+ self.config = config
32
+ self.abi = ABILoad(self.config.platform, self.config.abi_name) # Load ABI here
33
+ self.connector = ConnectW3(self.config.provider_url) # Web3Scout setup
34
+ self.connector.apply()
35
+ self.verbose = verbose
36
+ self.lp_contract = None
37
+ self.lp_data = None
38
+ self.lp_state = None
39
+
40
+ def apply(self):
41
+ self.lp_contract = self._init_lp_contract()
42
+
43
+ reserves = self.lp_contract.functions.getReserves().call()
44
+ token0_address = self.lp_contract.functions.token0().call()
45
+ token1_address = self.lp_contract.functions.token1().call()
46
+ reserve0 = reserves[0]; reserve1 = reserves[1]
47
+
48
+ w3 = self.connector.get_w3()
49
+ FetchERC20 = FetchToken(w3)
50
+ TKN0 = FetchERC20.apply(token0_address)
51
+ TKN1 = FetchERC20.apply(token1_address)
52
+
53
+ self.lp_data = UniswapPoolData(TKN0, TKN1, reserves)
54
+
55
+ def run_batch(self, tkn, events):
56
+ start_block = events[0]['blockNumber']
57
+ lp = self.prime_pool_state(start_block, 'user')
58
+
59
+ """Fetch batch of Sync events and process sequentially."""
60
+ if not events:
61
+ print("No Sync events found in range.")
62
+ return
63
+ for k in events:
64
+ reserve0 = events[k]['args']['reserve0']
65
+ reserve1 = events[k]['args']['reserve1']
66
+ block_num = events[k]['blockNumber']
67
+ event_price = self.calc_price(reserve0, reserve1, tkn1_over_tkn0 = True)
68
+ self.execute_action(lp, tkn, event_price, block_num)
69
+
70
+ def prime_pool_state(self, start_block, user_nm = None):
71
+ w3 = self.get_w3()
72
+ fetch_tkn = FetchToken(w3)
73
+
74
+ lp_contract = self._init_lp_contract()
75
+ tkn0_addr = lp_contract.functions.token0().call()
76
+ tkn1_addr = lp_contract.functions.token1().call()
77
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=start_block)
78
+ reserves = lp_contract.functions.getReserves().call(block_identifier=start_block)
79
+
80
+ # Step 2: Define tokens
81
+ tkn0 = fetch_tkn.apply(tkn0_addr)
82
+ tkn1 = fetch_tkn.apply(tkn1_addr)
83
+
84
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
85
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
86
+
87
+ # Step 3: Initialize factory
88
+ factory = UniswapFactory("Pool factory", "0x2")
89
+
90
+ # Step 4: Set up exchange data for V2
91
+ exch_data = UniswapExchangeData(tkn0=tkn0, tkn1=tkn1, symbol="LP", address=self.config.pool_address)
92
+
93
+ # Step 5: Deploy pool
94
+ self.lp_state = factory.deploy(exch_data)
95
+
96
+ # Step 6: Add initial liquidity
97
+ join = Join()
98
+ join.apply(self.lp_state, user_nm, amt0, amt1)
99
+ self.lp_state.total_supply = total_supply # override total supply
100
+
101
+ return self.lp_state
102
+
103
+ def execute_action(self, lp, tkn, price, block_num, tkn1_over_tkn0 = True):
104
+
105
+ tkn0 = self.lp_data.tkn0
106
+ tkn1 = self.lp_data.tkn1
107
+
108
+ """Execute swap if condition met (simulated or live)."""
109
+ if self.check_condition(block_num = block_num, tkn1_over_tkn0 = tkn1_over_tkn0):
110
+ try:
111
+ out = Swap().apply(lp, tkn, "test_action", self.config.swap_amount)
112
+ print(f"Block {block_num}: Swapped {self.config.swap_amount} {tkn0.token_name} for {out} {tkn1.token_name}")
113
+ except Exception as e:
114
+ print(f"Block {block_number}: Swap failed: {e}")
115
+
116
+ def get_token_price(self, tkn1_over_tkn0 = True, block_num = None):
117
+
118
+ if(block_num == None):
119
+ reserves = self.lp_data.reserves
120
+ else:
121
+ lp_contract = self._init_lp_contract()
122
+ reserves = lp_contract.functions.getReserves().call(block_identifier=block_num)
123
+
124
+ price = self.calc_price(reserves[0], reserves[1], tkn1_over_tkn0)
125
+
126
+ return price
127
+
128
+ def calc_price(self, reserve0, reserve1, tkn1_over_tkn0 = True):
129
+ tkn0 = self.lp_data.tkn0
130
+ tkn1 = self.lp_data.tkn1
131
+ tkn0_decimal = tkn0.token_decimal
132
+ tkn1_decimal = tkn1.token_decimal
133
+
134
+ if(tkn1_over_tkn0):
135
+ price = (reserve0 / reserve1) * (10 ** (tkn1_decimal - tkn0_decimal))
136
+ if(self.verbose): print(f"{tkn1.token_name} Price in {tkn0.token_name}: {price}")
137
+ else:
138
+ price = (reserve1 / reserve0) * (10 ** (tkn0_decimal - tkn1_decimal))
139
+ if(self.verbose): print(f"{tkn0.token_name} Price in {tkn1.token_name}: {price}")
140
+
141
+ return price
142
+
143
+ def check_condition(self, threshold = None, tkn1_over_tkn0 = True, block_num = None):
144
+ self.config.threshold = self.config.threshold if threshold == None else threshold;
145
+ self.apply()
146
+ price = self.get_token_price(tkn1_over_tkn0, block_num)
147
+ return price > self.config.threshold
148
+
149
+ def get_connector(self):
150
+ return self.connector
151
+
152
+ def get_abi(self):
153
+ return self.abi
154
+
155
+ def get_w3(self):
156
+ return self.connector.get_w3()
157
+
158
+ def get_contract_instance(self):
159
+ return self.lp_contract
160
+
161
+ def get_lp_data(self):
162
+ return self.lp_data
163
+
164
+ def _init_lp_contract(self):
165
+ pair_address = self.config.pool_address
166
+ w3 = self.get_w3()
167
+ abi_obj = self.get_abi()
168
+ lp_contract = abi_obj.apply(w3, pair_address)
169
+ return lp_contract
@@ -0,0 +1,174 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+ from web3scout.utils.connect import ConnectW3
21
+ from web3scout.abi.abi_load import ABILoad
22
+ from web3scout.event.process.retrieve_events import RetrieveEvents
23
+ from web3scout.token.fetch.fetch_token import FetchToken
24
+ from .config import TVLExitConfig
25
+ from .data import UniswapPoolData
26
+ from uniswappy import *
27
+ from web3 import Web3
28
+
29
+ class TVLBasedLiquidityExitAgent:
30
+ def __init__(self, config: TVLExitConfig, verbose: bool = False):
31
+ self.config = config
32
+ self.abi = ABILoad(self.config.platform, self.config.abi_name) # Load ABI here
33
+ self.connector = ConnectW3(self.config.provider_url) # Web3Scout setup
34
+ self.connector.apply()
35
+ self.verbose = verbose
36
+ self.lp_contract = None
37
+ self.mock_lp_pos_amt = None
38
+ self.lp_data = None
39
+ self.lp_state = None
40
+
41
+ def init(self):
42
+ self.lp_contract = self._init_lp_contract()
43
+
44
+ reserves = self.lp_contract.functions.getReserves().call()
45
+ token0_address = self.lp_contract.functions.token0().call()
46
+ token1_address = self.lp_contract.functions.token1().call()
47
+ reserve0 = reserves[0]; reserve1 = reserves[1]
48
+
49
+ w3 = self.connector.get_w3()
50
+ FetchERC20 = FetchToken(w3)
51
+ TKN0 = FetchERC20.apply(token0_address)
52
+ TKN1 = FetchERC20.apply(token1_address)
53
+
54
+ self.lp_data = UniswapPoolData(TKN0, TKN1, reserves)
55
+
56
+ def run_batch(self, lp, tkn, user_nm, events: dict):
57
+ """Process batched Sync events to check TVL and trigger exits."""
58
+ if not events:
59
+ print("No Sync events found in range.")
60
+ return
61
+ for k in events:
62
+ block_num = events[k]['blockNumber']
63
+ self.apply(lp, tkn, user_nm, block_num)
64
+
65
+ def apply(self, lp, tkn, user_nm, block_num):
66
+ """Execute liquidity exit if condition met."""
67
+ if self.check_condition(lp, tkn, self.config.tvl_threshold, block_num):
68
+ amount_out = self.withdraw_mock_position(lp, tkn, user_nm)
69
+ print(f"Block {block_num}: Withdrawing {amount_out} {tkn.token_name} from {lp.name} LP")
70
+ return amount_out
71
+ else:
72
+ print(f"Block {block_num}: TVL threshold condition met for {lp.name} LP")
73
+ return None
74
+
75
+ def check_condition(self, lp, tkn, threshold, block_num = None):
76
+ """Check if TVL is below threshold."""
77
+ block_num = self.get_w3().eth.block_number if block_num == None else block_num
78
+ tvl = self.get_pool_tvl(lp, tkn, block_num)
79
+ return tvl < threshold
80
+
81
+ def get_pool_tvl(self, lp, tkn, block_num):
82
+ """Calculate TVL from reserves (sum in USD, assuming base_token normalization)."""
83
+ lp = self.update_mock_pool(lp, block_num)
84
+ tot_lp = lp.get_liquidity()
85
+ tvl = LPQuote(False).get_amount_from_lp(lp, tkn, tot_lp)
86
+ return tvl
87
+
88
+ def take_mock_position(self, lp, tkn, user_nm, amt):
89
+ SwapDeposit().apply(lp, tkn, user_nm, amt)
90
+ self.mock_lp_pos_amt = lp.get_last_liquidity_deposit()
91
+ return self.mock_lp_pos_amt
92
+
93
+ def withdraw_mock_position(self, lp, tkn, user_nm, lp_amt = None):
94
+ assert self.mock_lp_pos_amt != None, 'TVLBasedLiquidityExitAgent: MOCK_POSITION_UNAVAILABLE'
95
+ lp_amt = self.mock_lp_pos_amt if lp_amt == None else lp_amt
96
+ tkn_amt = LPQuote(False).get_amount_from_lp(lp, tkn0, lp_amt)
97
+ amount_out = WithdrawSwap().apply(lp, tkn0, user_nm, tkn_amt)
98
+ return amount_out
99
+
100
+ def update_mock_pool(self, lp, cur_block):
101
+ w3 = self.get_w3()
102
+ fetch_tkn = FetchToken(w3)
103
+
104
+ lp_contract = self._init_lp_contract()
105
+ tkn0_addr = lp_contract.functions.token0().call()
106
+ tkn1_addr = lp_contract.functions.token1().call()
107
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=cur_block)
108
+ reserves = lp_contract.functions.getReserves().call(block_identifier=cur_block)
109
+
110
+ tkn0 = self.get_lp_data().tkn0
111
+ tkn1 = self.get_lp_data().tkn1
112
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
113
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
114
+
115
+ lp.reserve0 = lp.convert_to_machine(amt0) # override reserve0
116
+ lp.reserve1 = lp.convert_to_machine(amt1) # override reserve1
117
+ lp.total_supply = total_supply # override total supply
118
+
119
+ return lp
120
+
121
+ def prime_mock_pool(self, start_block, user_nm = None):
122
+ w3 = self.get_w3()
123
+ fetch_tkn = FetchToken(w3)
124
+
125
+ lp_contract = self._init_lp_contract()
126
+ tkn0_addr = lp_contract.functions.token0().call()
127
+ tkn1_addr = lp_contract.functions.token1().call()
128
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=start_block)
129
+ reserves = lp_contract.functions.getReserves().call(block_identifier=start_block)
130
+
131
+ # Step 2: Define tokens
132
+ tkn0 = fetch_tkn.apply(tkn0_addr)
133
+ tkn1 = fetch_tkn.apply(tkn1_addr)
134
+
135
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
136
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
137
+
138
+ # Step 3: Initialize factory
139
+ factory = UniswapFactory("Pool factory", "0x2")
140
+
141
+ # Step 4: Set up exchange data for V2
142
+ exch_data = UniswapExchangeData(tkn0=tkn0, tkn1=tkn1, symbol="LP", address=self.config.pool_address)
143
+
144
+ # Step 5: Deploy pool
145
+ self.lp_state = factory.deploy(exch_data)
146
+
147
+ # Step 6: Add initial liquidity
148
+ join = Join()
149
+ join.apply(self.lp_state, user_nm, amt0, amt1)
150
+ self.lp_state.total_supply = total_supply # override total supply
151
+
152
+ return self.lp_state
153
+
154
+ def get_connector(self):
155
+ return self.connector
156
+
157
+ def get_abi(self):
158
+ return self.abi
159
+
160
+ def get_w3(self):
161
+ return self.connector.get_w3()
162
+
163
+ def get_contract_instance(self):
164
+ return self.lp_contract
165
+
166
+ def get_lp_data(self):
167
+ return self.lp_data
168
+
169
+ def _init_lp_contract(self):
170
+ pair_address = self.config.pool_address
171
+ w3 = self.get_w3()
172
+ abi_obj = self.get_abi()
173
+ lp_contract = abi_obj.apply(w3, pair_address)
174
+ return lp_contract
@@ -0,0 +1,190 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+ from web3scout.utils.connect import ConnectW3
21
+ from web3scout.abi.abi_load import ABILoad
22
+ from web3scout.event.process.retrieve_events import RetrieveEvents
23
+ from web3scout.token.fetch.fetch_token import FetchToken
24
+ from .config import VolumeSpikeConfig
25
+ from .data import UniswapPoolData
26
+ from uniswappy import *
27
+ from web3 import Web3
28
+
29
+ class VolumeSpikeNotifierAgent:
30
+ def __init__(self, config: VolumeSpikeConfig, verbose: bool = False):
31
+ self.config = config
32
+ self.abi = ABILoad(self.config.platform, self.config.abi_name) # Load ABI here
33
+ self.connector = ConnectW3(self.config.provider_url) # Web3Scout setup
34
+ self.connector.apply()
35
+ self.verbose = verbose
36
+ self.pool_volume = None
37
+ self.lp_contract = None
38
+ self.lp_data = None
39
+ self.lp_state = None
40
+
41
+ def init(self):
42
+ self.lp_contract = self._init_lp_contract()
43
+
44
+ reserves = self.lp_contract.functions.getReserves().call()
45
+ token0_address = self.lp_contract.functions.token0().call()
46
+ token1_address = self.lp_contract.functions.token1().call()
47
+ reserve0 = reserves[0]; reserve1 = reserves[1]
48
+
49
+ w3 = self.connector.get_w3()
50
+ FetchERC20 = FetchToken(w3)
51
+ TKN0 = FetchERC20.apply(token0_address)
52
+ TKN1 = FetchERC20.apply(token1_address)
53
+
54
+ self.lp_data = UniswapPoolData(TKN0, TKN1, reserves)
55
+
56
+ def get_connector(self):
57
+ return self.connector
58
+
59
+ def get_abi(self):
60
+ return self.abi
61
+
62
+ def get_w3(self):
63
+ return self.connector.get_w3()
64
+
65
+ def get_contract_instance(self):
66
+ return self.lp_contract
67
+
68
+ def get_lp_data(self):
69
+ return self.lp_data
70
+
71
+ def prime_mock_pool(self, start_block, user_nm = None):
72
+ w3 = self.get_w3()
73
+ fetch_tkn = FetchToken(w3)
74
+
75
+ lp_contract = self._init_lp_contract()
76
+ tkn0_addr = lp_contract.functions.token0().call()
77
+ tkn1_addr = lp_contract.functions.token1().call()
78
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=start_block)
79
+ reserves = lp_contract.functions.getReserves().call(block_identifier=start_block)
80
+
81
+ # Step 2: Define tokens
82
+ tkn0 = fetch_tkn.apply(tkn0_addr)
83
+ tkn1 = fetch_tkn.apply(tkn1_addr)
84
+
85
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
86
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
87
+
88
+ # Step 3: Initialize factory
89
+ factory = UniswapFactory("Pool factory", "0x2")
90
+
91
+ # Step 4: Set up exchange data for V2
92
+ exch_data = UniswapExchangeData(tkn0=tkn0, tkn1=tkn1, symbol="LP", address=self.config.pool_address)
93
+
94
+ # Step 5: Deploy pool
95
+ self.lp_state = factory.deploy(exch_data)
96
+
97
+ # Step 6: Add initial liquidity
98
+ join = Join()
99
+ join.apply(self.lp_state, user_nm, amt0, amt1)
100
+ self.lp_state.total_supply = total_supply # override total supply
101
+
102
+ return self.lp_state
103
+
104
+ def update_mock_pool(self, lp, cur_block):
105
+ w3 = self.get_w3()
106
+ fetch_tkn = FetchToken(w3)
107
+
108
+ lp_contract = self._init_lp_contract()
109
+ tkn0_addr = lp_contract.functions.token0().call()
110
+ tkn1_addr = lp_contract.functions.token1().call()
111
+ total_supply = lp_contract.functions.totalSupply().call(block_identifier=int(cur_block))
112
+ reserves = lp_contract.functions.getReserves().call(block_identifier=int(cur_block))
113
+
114
+ tkn0 = self.get_lp_data().tkn0
115
+ tkn1 = self.get_lp_data().tkn1
116
+ amt0 = fetch_tkn.amt_to_decimal(tkn0, reserves[0])
117
+ amt1 = fetch_tkn.amt_to_decimal(tkn1, reserves[1])
118
+
119
+ prev_total_supply = lp.total_supply
120
+ lp.reserve0 = lp.convert_to_machine(amt0) # override reserve0
121
+ lp.reserve1 = lp.convert_to_machine(amt1) # override reserve1
122
+ lp.total_supply = total_supply # override total supply
123
+ lp.last_liquidity_deposit = abs(prev_total_supply - lp.total_supply)
124
+
125
+ return lp
126
+
127
+ def run_batch(self, lp, tkn, user_nm, events: dict):
128
+ """Process batched Sync events to check TVL and trigger exits."""
129
+ if not events:
130
+ print("No Sync events found in range.")
131
+ return
132
+ for k in events:
133
+ block_num = events[k]['blockNumber']
134
+ self.apply(lp, tkn, user_nm, block_num)
135
+
136
+ def apply(self, lp, tkn, user_nm, block_num):
137
+ """Execute liquidity exit if condition met."""
138
+ if self.check_condition(lp, tkn, self.config.volume_threshold, block_num):
139
+ vol = self.pool_volume
140
+ print(f"Block {block_num}: Volume ({tkn.token_name}) = {vol}, outside threshold {self.config.volume_threshold}")
141
+ return vol
142
+ else:
143
+ print(f"Block {block_num}: Volume threshold condition met for {lp.name} LP")
144
+ return None
145
+
146
+ def take_mock_position(self, lp, tkn, user_nm, amt):
147
+ SwapDeposit().apply(lp, tkn, user_nm, amt)
148
+ self.mock_lp_pos_amt = lp.get_last_liquidity_deposit()
149
+ return self.mock_lp_pos_amt
150
+
151
+ def withdraw_mock_position(self, lp, tkn, user_nm, lp_amt = None):
152
+ assert self.mock_lp_pos_amt != None, 'TVLBasedLiquidityExitAgent: MOCK_POSITION_UNAVAILABLE'
153
+ lp_amt = self.mock_lp_pos_amt if lp_amt == None else lp_amt
154
+ tkn_amt = LPQuote(False).get_amount_from_lp(lp, tkn0, lp_amt)
155
+ amount_out = WithdrawSwap().apply(lp, tkn0, user_nm, tkn_amt)
156
+ return amount_out
157
+
158
+ def get_pool_volume(self, lp, tkn, block_num):
159
+ """Calculate TVL from reserves (sum in USD, assuming base_token normalization)."""
160
+
161
+ tkn0 = self.get_lp_data().tkn0
162
+ tkn1 = self.get_lp_data().tkn1
163
+ prev_tkn0 = lp.get_reserve(tkn0)
164
+ prev_tkn1 = lp.get_reserve(tkn1)
165
+
166
+ lp = self.update_mock_pool(lp, block_num)
167
+
168
+ dtkn0 = abs(lp.get_reserve(tkn0) - prev_tkn0)
169
+ dtkn1 = abs(lp.get_reserve(tkn1) - prev_tkn1)
170
+
171
+ if(tkn.token_name == tkn0.token_name):
172
+ volume = dtkn0 + LPQuote().get_amount(lp, tkn1, dtkn1)
173
+ elif(tkn.token_name == tkn1.token_name):
174
+ volume = dtkn1 + LPQuote().get_amount(lp, tkn0, dtkn0)
175
+
176
+ self.pool_volume = volume
177
+ return volume
178
+
179
+ def check_condition(self, lp, tkn, threshold, block_num = None):
180
+ """Check if TVL is below threshold."""
181
+ block_num = self.get_w3().eth.block_number if block_num == None else block_num
182
+ volume = self.get_pool_volume(lp, tkn, block_num)
183
+ return volume > threshold
184
+
185
+ def _init_lp_contract(self):
186
+ pair_address = self.config.pool_address
187
+ w3 = self.get_w3()
188
+ abi_obj = self.get_abi()
189
+ lp_contract = abi_obj.apply(w3, pair_address)
190
+ return lp_contract
@@ -0,0 +1,4 @@
1
+ from .PriceThresholdSwapAgent import PriceThresholdSwapAgent
2
+ from .TVLBasedLiquidityExitAgent import TVLBasedLiquidityExitAgent
3
+ from .VolumeSpikeNotifierAgent import VolumeSpikeNotifierAgent
4
+ from .ImpermanentLossAgent import ImpermanentLossAgent
@@ -0,0 +1,28 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+
21
+ class ImpermanentLossConfig(BaseModel):
22
+ il_threshold: float # Impermanent loss threshold percentage (e.g., 5.0 for 5%)
23
+ pool_address: str # Uniswap V2 pool address
24
+ provider_url: str # Web3 provider URL (e.g., Infura)
25
+ abi_name: str # e.g., 'UniswapV2Pair' (new field for ABI identifier)
26
+ platform: str # e.g., 'UNI' or 'SUSHI' for the protocoll
27
+ user_position: float # Initial mock position amount for off-chain testing
28
+ exit_percentage: float # Percentage of position to exit upon trigger (e.g., 1.0 for 100%)
@@ -0,0 +1,27 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+
21
+ class PriceThresholdConfig(BaseModel):
22
+ threshold: float # e.g., 3000.0 (price above which to swap)
23
+ swap_amount: float # e.g., 1.0 (swap amount if threshold met)
24
+ pool_address: str # Uniswap V2 pool
25
+ provider_url: str # e.g., Infura for Web3Scout
26
+ abi_name: str # e.g., 'UniswapV2Pair' (new field for ABI identifier)
27
+ platform: str # e.g., 'UNI' or 'SUSHI' for the protocoll
@@ -0,0 +1,28 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+
21
+ class TVLExitConfig(BaseModel):
22
+ tvl_threshold: float # e.g., 1000000.0 (minimum TVL in USD)
23
+ exit_percentage: float # e.g., 1.0 (full exit) or 0.5 (half)
24
+ pool_address: str # Pool contract address
25
+ provider_url: str # Web3 provider URL
26
+ abi_name: str # e.g., 'UniswapV2Pair' (new field for ABI identifier)
27
+ platform: str # e.g., 'UNI' or 'SUSHI' for the protocoll
28
+ user_position: float # User's LP shares or amount
@@ -0,0 +1,27 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from pydantic import BaseModel
20
+
21
+ class VolumeSpikeConfig(BaseModel):
22
+ volume_threshold: float # Volume threshold for notification (e.g., USD value of trades)
23
+ pool_address: str # Uniswap V2 pool address
24
+ provider_url: str # Web3 provider URL (e.g., Infura)
25
+ abi_name: str # e.g., 'UniswapV2Pair' (new field for ABI identifier)
26
+ platform: str # e.g., 'UNI' or 'SUSHI' for the protocoll
27
+ user_position: float # User's LP shares or amount
@@ -0,0 +1,7 @@
1
+ from .PriceThresholdConfig import PriceThresholdConfig
2
+ from .TVLExitConfig import TVLExitConfig
3
+ from .VolumeSpikeConfig import VolumeSpikeConfig
4
+ from .ImpermanentLossConfig import ImpermanentLossConfig
5
+
6
+
7
+
@@ -0,0 +1,26 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
5
+ # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ from dataclasses import dataclass
20
+ from uniswappy.erc import ERC20
21
+
22
+ @dataclass
23
+ class UniswapPoolData:
24
+ tkn0: ERC20 = None
25
+ tkn1: ERC20 = None
26
+ reserves: list = None
@@ -0,0 +1 @@
1
+ from .UniswapPoolData import UniswapPoolData
@@ -1,3 +1,6 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
1
4
  # Copyright 2023–2025 Ian Moore
2
5
  # Email: defipy.devs@gmail.com
3
6
  #
@@ -1,3 +1,6 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
1
4
  # Copyright 2023–2025 Ian Moore
2
5
  # Email: defipy.devs@gmail.com
3
6
  #
@@ -1,3 +1,6 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
1
4
  # Copyright 2023–2025 Ian Moore
2
5
  # Email: defipy.devs@gmail.com
3
6
  #
@@ -1,3 +1,6 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
1
4
  # Copyright 2023–2025 Ian Moore
2
5
  # Email: defipy.devs@gmail.com
3
6
  #
@@ -1,6 +1,20 @@
1
- # Copyright [2025] [Ian Moore]
2
- # Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
3
5
  # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
4
18
 
5
19
  import subprocess
6
20
 
@@ -1,6 +1,20 @@
1
- # Copyright [2025] [Ian Moore]
2
- # Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # Apache 2.0 License (DeFiPy)
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # Copyright 2023–2025 Ian Moore
3
5
  # Email: defipy.devs@gmail.com
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
4
18
 
5
19
  from uniswappy.utils.tools.v3 import UniV3Helper
6
20
  from uniswappy.process.swap import WithdrawSwap
@@ -4,7 +4,7 @@ with open('README.md') as f:
4
4
  long_description = f.read()
5
5
 
6
6
  setup(name='DeFiPy',
7
- version='1.0.7',
7
+ version='1.0.9',
8
8
  description='Python SDK for DeFi Analytics, Simulation, and Agents',
9
9
  long_description=long_description,
10
10
  long_description_content_type="text/markdown",
@@ -45,13 +45,17 @@ setup(name='DeFiPy',
45
45
  'defipy.utils.client',
46
46
  'defipy.utils.client.contract',
47
47
  'defipy.utils.tools',
48
- 'defipy.utils.tools.v3'
48
+ 'defipy.utils.tools.v3',
49
+ 'defipy.agents.config',
50
+ 'defipy.agents.data',
51
+ 'defipy.agents',
49
52
  ],
50
53
  install_requires=[
51
54
  'scipy >= 1.7.3',
55
+ 'pydantic >= 2.11.0',
52
56
  'bokeh == 3.3.4',
53
- 'uniswappy == 1.7.4',
54
- 'stableswappy == 1.0.3',
55
- 'balancerpy == 1.0.4'
57
+ 'uniswappy >= 1.7.4',
58
+ 'stableswappy >= 1.0.3',
59
+ 'balancerpy >= 1.0.4'
56
60
  ],
57
61
  zip_safe=False)
@@ -1,5 +0,0 @@
1
- scipy>=1.7.3
2
- bokeh==3.3.4
3
- uniswappy==1.7.4
4
- stableswappy==1.0.3
5
- balancerpy==1.0.4
File without changes
File without changes