sally-defi-py-sdk 0.2.0__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.
- sally_defi_py_sdk-0.2.0/LICENSE +21 -0
- sally_defi_py_sdk-0.2.0/PKG-INFO +260 -0
- sally_defi_py_sdk-0.2.0/README.md +227 -0
- sally_defi_py_sdk-0.2.0/pyproject.toml +73 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/__init__.py +95 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/__init__.py +21 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/client.py +275 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/__init__.py +0 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/fees.py +51 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/liquidity.py +119 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/prices.py +53 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/swap.py +247 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/treasury.py +40 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/modules/wallet.py +36 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/aio/token.py +56 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/client.py +403 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/constants.py +46 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/data/deployment.json +1 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/deployment.py +127 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/errors.py +154 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/__init__.py +0 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/admin.py +149 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/fees.py +66 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/liquidity.py +357 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/prices.py +89 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/swap.py +416 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/treasury.py +73 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/modules/wallet.py +41 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/permit2.py +181 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/py.typed +0 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/safety.py +104 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/token.py +183 -0
- sally_defi_py_sdk-0.2.0/src/sally_defi/types.py +423 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sally (sally.tools)
|
|
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.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sally-defi-py-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Modern Python SDK for the Sally DeFi CrossHybrid protocol (Base + BSC) — swaps, pricing, liquidity, treasury.
|
|
5
|
+
Keywords: defi,ethereum,base,bsc,web3,uniswap,swap,liquidity,sally
|
|
6
|
+
Author: Sally.tools
|
|
7
|
+
Author-email: Sally.tools <support@sally.tools>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Dist: web3>=7.6,<8
|
|
22
|
+
Requires-Dist: eth-account>=0.13,<0.14
|
|
23
|
+
Requires-Dist: eth-abi>=5,<6
|
|
24
|
+
Requires-Dist: eth-utils>=5,<6
|
|
25
|
+
Requires-Dist: pydantic>=2.9,<3
|
|
26
|
+
Requires-Python: >=3.12
|
|
27
|
+
Project-URL: Homepage, https://sally.tools
|
|
28
|
+
Project-URL: Repository, https://github.com/SallyTools/sally-defi-py-sdk
|
|
29
|
+
Project-URL: Documentation, https://github.com/SallyTools/sally-defi-py-sdk/tree/main/docs
|
|
30
|
+
Project-URL: Issues, https://github.com/SallyTools/sally-defi-py-sdk/issues
|
|
31
|
+
Project-URL: Changelog, https://github.com/SallyTools/sally-defi-py-sdk/blob/main/CHANGELOG.md
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+
<img src="assets/header.svg" alt="sally-defi-py-sdk · Python SDK" width="100%">
|
|
37
|
+
|
|
38
|
+
# sally-defi-py-sdk · Python SDK
|
|
39
|
+
|
|
40
|
+
**One client for CrossHybrid swaps & liquidity on Base + BSC.**
|
|
41
|
+
|
|
42
|
+
Wraps the live [Sally](https://sally.tools) v2.0.2 protocol — best-route hybrid
|
|
43
|
+
swaps, V2·V3·V4·Slipstream liquidity, 1e18 USD pricing, honeypot screening, locks,
|
|
44
|
+
referral fees and the multi-owner treasury — behind one typed, beginner-friendly
|
|
45
|
+
and AI-agent-ready API.
|
|
46
|
+
|
|
47
|
+
[Website](https://sally.tools) · [Docs](docs/) · [Examples](examples/) · `support@sally.tools`
|
|
48
|
+
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Why sally-defi-py-sdk
|
|
54
|
+
|
|
55
|
+
Most Python DeFi code is raw `web3.py`: untyped tuples, hand-rolled approvals, a
|
|
56
|
+
quote you *hope* matches execution, and zero protection against honeypots or
|
|
57
|
+
sandwiches. sally-defi-py-sdk collapses all of that into **one safe call**.
|
|
58
|
+
|
|
59
|
+
`swap.execute` does the work a careful integrator would do by hand — approve →
|
|
60
|
+
enumerate routes → **simulate every route on-chain** → screen the output token for
|
|
61
|
+
honeypot/tax → set `minOut` from the *simulated* output → send → **assert you
|
|
62
|
+
actually received it**. That whole pipeline is the default, not an opt-in.
|
|
63
|
+
|
|
64
|
+
* 🛡️ **Safety in the hot path.** Simulate-before-execute + post-trade balance-delta
|
|
65
|
+
assertion — not just in your test suite. (The balance-delta check is something
|
|
66
|
+
even mature SDKs don't ship.)
|
|
67
|
+
* 🧱 **Typed end-to-end.** `pos.liquidity`, `plan.is_safe`, `quote.estimated_amount_out`
|
|
68
|
+
— dataclasses, autocomplete, JSON-able. No `tuple[7]`.
|
|
69
|
+
* 🤖 **AI-agent-ready by construction.** Every return serializes cleanly, so wiring
|
|
70
|
+
Sally into LLM tools is mechanical.
|
|
71
|
+
* ⚡ **Async + MEV-protection + permit** are constructor/keyword arguments, not projects.
|
|
72
|
+
* 🪶 **Light deps** (web3, eth-account, eth-abi, eth-utils, pydantic), Python ≥3.12, `py.typed`.
|
|
73
|
+
|
|
74
|
+
Honest about scope: Sally routes within **its own protocol** on **Base + BSC** — it
|
|
75
|
+
is not a 100-source aggregator. Full, cited comparison (vorteile *and* nachteile)
|
|
76
|
+
vs raw web3.py / uniswap-python / eth_defi / Ape / aggregator SDKs:
|
|
77
|
+
**[docs/comparison.md](docs/comparison.md)**.
|
|
78
|
+
|
|
79
|
+
## What you get
|
|
80
|
+
|
|
81
|
+
* **One object, every function.** `SallyClient` exposes the whole protocol through
|
|
82
|
+
feature namespaces (`prices`, `swap`, `wallet`, `liquidity`, `fees`, `treasury`,
|
|
83
|
+
`admin`). Reads need only an RPC; writes need a signer.
|
|
84
|
+
* **Safe by default.** `swap.execute` enumerates routes, **integrity-checks** each so
|
|
85
|
+
funds can never enter a wrong pool, **simulates** every candidate on-chain, picks
|
|
86
|
+
the best realized output, runs a **honeypot/tax probe** on the output token, sets
|
|
87
|
+
`minOut` from the simulation, then **asserts the received balance actually grew**.
|
|
88
|
+
* **Single source of truth.** All addresses + ABIs come from the bundled
|
|
89
|
+
`deployment.json` (addresses + ABIs only). Nothing hard-coded; drop in a new
|
|
90
|
+
deployment and the SDK follows.
|
|
91
|
+
* **Proxy-fallback aware.** v2.0.2 serves Lens views through the swap proxy and
|
|
92
|
+
Sidecar views through the liq proxy — the SDK wires this for you.
|
|
93
|
+
* **Typed returns.** Every struct is a dataclass (`SwapPath`, `V3Position`, `Lock`,
|
|
94
|
+
`SwapPlan`, …) — `pos.liquidity`, not `pos[7]`.
|
|
95
|
+
|
|
96
|
+
## Install
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv add sally-defi-py-sdk # or: pip install sally-defi-py-sdk
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> Install as **`sally-defi-py-sdk`**, import as **`sally_defi`**
|
|
103
|
+
> (like `pip install scikit-learn` → `import sklearn`).
|
|
104
|
+
|
|
105
|
+
Requires Python ≥ 3.12 and `web3 >= 7.6`.
|
|
106
|
+
|
|
107
|
+
## Quickstart (read-only)
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from sally_defi import SallyClient
|
|
111
|
+
from sally_defi.constants import Base
|
|
112
|
+
|
|
113
|
+
sally = SallyClient("base", "https://mainnet.base.org")
|
|
114
|
+
|
|
115
|
+
sally.prices.usd(Base.USDC).as_float # 1.0 (display / spot-mid)
|
|
116
|
+
sally.prices.usd_impact(Base.WETH).as_float # 1703.19 (execution-aware)
|
|
117
|
+
|
|
118
|
+
quote = sally.swap.quote(Base.WETH, Base.USDC, 10**18)
|
|
119
|
+
quote.estimated_amount_out / 1e6 # 1703.45 USDC
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Swapping — safe by default
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
sally = SallyClient("base", RPC, private_key="0x…") # or SALLY_PRIVATE_KEY env
|
|
126
|
+
|
|
127
|
+
# Inspect first: compares every route, simulates each, screens the output token.
|
|
128
|
+
plan = sally.swap.plan(Base.WETH, Base.USDC, 10**18)
|
|
129
|
+
plan.summary() # 'SwapPlan(… out~1702924727 sim=1702924727 min=1694410103 impact=13bps steps=2 SAFE)'
|
|
130
|
+
plan.is_safe # False if honeypot / excessive tax / >15% impact / bad route
|
|
131
|
+
plan.candidates # every route considered, with simulated output
|
|
132
|
+
|
|
133
|
+
# Execute: same vetting, then send + balance-delta assertion.
|
|
134
|
+
receipt = sally.swap.execute(Base.WETH, Base.USDC, 10**18, slippage_bps=50)
|
|
135
|
+
|
|
136
|
+
# Native ETH in — pass the NATIVE sentinel; value is attached for you.
|
|
137
|
+
from sally_defi.token import NATIVE
|
|
138
|
+
sally.swap.execute(NATIVE, Base.USDC, 10**17, slippage_bps=50)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
What `execute` does, in order: approve (exact amount) → enumerate routes →
|
|
142
|
+
integrity-check → simulate each on-chain → pick best realized output →
|
|
143
|
+
honeypot/tax preflight → `minOut` from the simulation → send →
|
|
144
|
+
**assert received ≥ minOut**. See [`docs/safety.md`](docs/safety.md).
|
|
145
|
+
|
|
146
|
+
Tune the guard rails:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from sally_defi import SafetyConfig
|
|
150
|
+
sally = SallyClient("base", RPC, private_key="0x…",
|
|
151
|
+
safety=SafetyConfig(slippage_bps=30, tax_block_bps=3000,
|
|
152
|
+
price_impact_block_bps=1000))
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Liquidity
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
sally.liquidity.add_v2(dex_id=4, token_a=Base.WETH, token_b=Base.USDC,
|
|
159
|
+
amount_a=10**15, amount_b=2_000_000, lock_days=0)
|
|
160
|
+
|
|
161
|
+
sally.liquidity.positions_v3(wallet, 0, 10) # list[V3Position]
|
|
162
|
+
sally.liquidity.locks(wallet) # list[Lock]
|
|
163
|
+
sally.liquidity.claimable_fees(npm, token_id) # ClaimableFees
|
|
164
|
+
# add_v3 / add_v4 / add_slipstream, increase/decrease, remove_v2,
|
|
165
|
+
# lock/unlock, claim_fees_v3/v4(+_locked), mass_claim_fees, harvest_op
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Referral system 💸
|
|
169
|
+
|
|
170
|
+
Every swap and liquidity action takes a `referral` address. Pass yours and the
|
|
171
|
+
protocol accrues referral fees to it; read and claim them anytime:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
sally.swap.execute(Base.WETH, Base.USDC, 10**18, referral="0xYourRef")
|
|
175
|
+
|
|
176
|
+
sally.fees.total_referral_owed("0xYourRef") # total owed across tokens
|
|
177
|
+
sally.fees.referral_tokens("0xYourRef") # per-token breakdown (paged)
|
|
178
|
+
sally.fees.claim_referral([Base.USDC, Base.WETH]) # claim selected tokens
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
See [`docs/referrals.md`](docs/referrals.md).
|
|
182
|
+
|
|
183
|
+
## Async · permit · private mempool
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
# Async (concurrent quoting + simulation)
|
|
187
|
+
from sally_defi.aio import AsyncSallyClient
|
|
188
|
+
sally = AsyncSallyClient("base", RPC, private_key="0x…")
|
|
189
|
+
await sally.swap.execute(Base.WETH, Base.USDC, 10**18, slippage_bps=50)
|
|
190
|
+
|
|
191
|
+
# EIP-2612 permit instead of approve
|
|
192
|
+
sally.swap.execute(Base.USDC, Base.WETH, 10**8, approval="permit")
|
|
193
|
+
|
|
194
|
+
# Private mempool (MEV protection)
|
|
195
|
+
from sally_defi.constants import PrivateRelays
|
|
196
|
+
SallyClient("base", RPC, private_key="0x…", private_rpc_url=PrivateRelays.FLASHBOTS_FAST)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Full guide: [`docs/advanced.md`](docs/advanced.md).
|
|
200
|
+
|
|
201
|
+
## Namespaces
|
|
202
|
+
|
|
203
|
+
| Namespace | What it covers |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `client.prices` | `usd` (display), `usd_impact` (exec), `spot`, `weth`, `usd_liquidity`, `token_info` |
|
|
206
|
+
| `client.swap` | `quote(_v2/v3/v4/deep)`, `candidates`, `plan`, `simulate_route`, `execute`, `token_info` |
|
|
207
|
+
| `client.wallet` | `balances`, `balances_raw`, `total_usd` |
|
|
208
|
+
| `client.liquidity` | add / increase / decrease / remove / lock / claim / harvest, positions, pool state, dex registry |
|
|
209
|
+
| `client.fees` | referral + batch fee reads & claims, `swap_fee` |
|
|
210
|
+
| `client.treasury` | owners, withdraw, `execute`, `swap_auto` |
|
|
211
|
+
| `client.admin` | owner-only setters, DEX registry, pause, rescue, UUPS upgrade |
|
|
212
|
+
|
|
213
|
+
Raw contracts are always reachable: `client.swap_contract`, `…liquidity_contract`,
|
|
214
|
+
`…lens_contract`, `…sidecar_contract`, `…treasury_contract`.
|
|
215
|
+
|
|
216
|
+
## AI agents
|
|
217
|
+
|
|
218
|
+
Every call returns a JSON-able dataclass with a clear docstring — wrapping Sally as
|
|
219
|
+
LLM tools is trivial. See [`docs/ai-agents.md`](docs/ai-agents.md) and
|
|
220
|
+
[`examples/10_ai_agent_tools.py`](examples/10_ai_agent_tools.py).
|
|
221
|
+
|
|
222
|
+
## Docs
|
|
223
|
+
|
|
224
|
+
| | |
|
|
225
|
+
|---|---|
|
|
226
|
+
| [Why sally-defi-py-sdk](docs/comparison.md) | comparison vs other DeFi SDKs, advantages & trade-offs |
|
|
227
|
+
| [Getting started](docs/getting-started.md) | install, connect, first read & swap |
|
|
228
|
+
| [Swap & safety](docs/safety.md) | route comparison, simulation, all guard rails |
|
|
229
|
+
| [Liquidity](docs/liquidity.md) | add/remove/lock/harvest across V2·V3·V4 |
|
|
230
|
+
| [Pricing](docs/pricing.md) | display vs execution price, spot, impact |
|
|
231
|
+
| [Referrals](docs/referrals.md) | earn & claim referral fees |
|
|
232
|
+
| [Advanced](docs/advanced.md) | async client, EIP-2612 permit, Permit2, private mempool, BSC |
|
|
233
|
+
| [AI agents](docs/ai-agents.md) | typed returns as tool functions |
|
|
234
|
+
| [API reference](docs/api-reference.md) | every namespace + method |
|
|
235
|
+
|
|
236
|
+
## Live deployment (v2.0.2, Base = BSC)
|
|
237
|
+
|
|
238
|
+
| Contract | Address |
|
|
239
|
+
|---|---|
|
|
240
|
+
| SwapController (proxy) | `0x7777D0e2e2d772b5D750540B3932261574e87777` |
|
|
241
|
+
| LiquidityController (proxy) | `0x7777d7fFF155B043CE0A0785dd8c8c42bEbe7777` |
|
|
242
|
+
| Treasury | `0x7777D7dC7ea65F33EC126165e03C6B6030887777` |
|
|
243
|
+
|
|
244
|
+
## Development & tests
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
uv sync
|
|
248
|
+
uv run pytest -m "not fork" # offline unit tests
|
|
249
|
+
anvil --fork-url $BASE_RPC --port 8547 --chain-id 8453 &
|
|
250
|
+
SALLY_RPC_URL=http://127.0.0.1:8547 uv run pytest # + fork smoke tests
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Support
|
|
254
|
+
|
|
255
|
+
Questions, integrations, partnerships → [`support@sally.tools`](mailto:support@sally.tools)
|
|
256
|
+
· [sally.tools](https://sally.tools)
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/header.svg" alt="sally-defi-py-sdk · Python SDK" width="100%">
|
|
4
|
+
|
|
5
|
+
# sally-defi-py-sdk · Python SDK
|
|
6
|
+
|
|
7
|
+
**One client for CrossHybrid swaps & liquidity on Base + BSC.**
|
|
8
|
+
|
|
9
|
+
Wraps the live [Sally](https://sally.tools) v2.0.2 protocol — best-route hybrid
|
|
10
|
+
swaps, V2·V3·V4·Slipstream liquidity, 1e18 USD pricing, honeypot screening, locks,
|
|
11
|
+
referral fees and the multi-owner treasury — behind one typed, beginner-friendly
|
|
12
|
+
and AI-agent-ready API.
|
|
13
|
+
|
|
14
|
+
[Website](https://sally.tools) · [Docs](docs/) · [Examples](examples/) · `support@sally.tools`
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Why sally-defi-py-sdk
|
|
21
|
+
|
|
22
|
+
Most Python DeFi code is raw `web3.py`: untyped tuples, hand-rolled approvals, a
|
|
23
|
+
quote you *hope* matches execution, and zero protection against honeypots or
|
|
24
|
+
sandwiches. sally-defi-py-sdk collapses all of that into **one safe call**.
|
|
25
|
+
|
|
26
|
+
`swap.execute` does the work a careful integrator would do by hand — approve →
|
|
27
|
+
enumerate routes → **simulate every route on-chain** → screen the output token for
|
|
28
|
+
honeypot/tax → set `minOut` from the *simulated* output → send → **assert you
|
|
29
|
+
actually received it**. That whole pipeline is the default, not an opt-in.
|
|
30
|
+
|
|
31
|
+
* 🛡️ **Safety in the hot path.** Simulate-before-execute + post-trade balance-delta
|
|
32
|
+
assertion — not just in your test suite. (The balance-delta check is something
|
|
33
|
+
even mature SDKs don't ship.)
|
|
34
|
+
* 🧱 **Typed end-to-end.** `pos.liquidity`, `plan.is_safe`, `quote.estimated_amount_out`
|
|
35
|
+
— dataclasses, autocomplete, JSON-able. No `tuple[7]`.
|
|
36
|
+
* 🤖 **AI-agent-ready by construction.** Every return serializes cleanly, so wiring
|
|
37
|
+
Sally into LLM tools is mechanical.
|
|
38
|
+
* ⚡ **Async + MEV-protection + permit** are constructor/keyword arguments, not projects.
|
|
39
|
+
* 🪶 **Light deps** (web3, eth-account, eth-abi, eth-utils, pydantic), Python ≥3.12, `py.typed`.
|
|
40
|
+
|
|
41
|
+
Honest about scope: Sally routes within **its own protocol** on **Base + BSC** — it
|
|
42
|
+
is not a 100-source aggregator. Full, cited comparison (vorteile *and* nachteile)
|
|
43
|
+
vs raw web3.py / uniswap-python / eth_defi / Ape / aggregator SDKs:
|
|
44
|
+
**[docs/comparison.md](docs/comparison.md)**.
|
|
45
|
+
|
|
46
|
+
## What you get
|
|
47
|
+
|
|
48
|
+
* **One object, every function.** `SallyClient` exposes the whole protocol through
|
|
49
|
+
feature namespaces (`prices`, `swap`, `wallet`, `liquidity`, `fees`, `treasury`,
|
|
50
|
+
`admin`). Reads need only an RPC; writes need a signer.
|
|
51
|
+
* **Safe by default.** `swap.execute` enumerates routes, **integrity-checks** each so
|
|
52
|
+
funds can never enter a wrong pool, **simulates** every candidate on-chain, picks
|
|
53
|
+
the best realized output, runs a **honeypot/tax probe** on the output token, sets
|
|
54
|
+
`minOut` from the simulation, then **asserts the received balance actually grew**.
|
|
55
|
+
* **Single source of truth.** All addresses + ABIs come from the bundled
|
|
56
|
+
`deployment.json` (addresses + ABIs only). Nothing hard-coded; drop in a new
|
|
57
|
+
deployment and the SDK follows.
|
|
58
|
+
* **Proxy-fallback aware.** v2.0.2 serves Lens views through the swap proxy and
|
|
59
|
+
Sidecar views through the liq proxy — the SDK wires this for you.
|
|
60
|
+
* **Typed returns.** Every struct is a dataclass (`SwapPath`, `V3Position`, `Lock`,
|
|
61
|
+
`SwapPlan`, …) — `pos.liquidity`, not `pos[7]`.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv add sally-defi-py-sdk # or: pip install sally-defi-py-sdk
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> Install as **`sally-defi-py-sdk`**, import as **`sally_defi`**
|
|
70
|
+
> (like `pip install scikit-learn` → `import sklearn`).
|
|
71
|
+
|
|
72
|
+
Requires Python ≥ 3.12 and `web3 >= 7.6`.
|
|
73
|
+
|
|
74
|
+
## Quickstart (read-only)
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from sally_defi import SallyClient
|
|
78
|
+
from sally_defi.constants import Base
|
|
79
|
+
|
|
80
|
+
sally = SallyClient("base", "https://mainnet.base.org")
|
|
81
|
+
|
|
82
|
+
sally.prices.usd(Base.USDC).as_float # 1.0 (display / spot-mid)
|
|
83
|
+
sally.prices.usd_impact(Base.WETH).as_float # 1703.19 (execution-aware)
|
|
84
|
+
|
|
85
|
+
quote = sally.swap.quote(Base.WETH, Base.USDC, 10**18)
|
|
86
|
+
quote.estimated_amount_out / 1e6 # 1703.45 USDC
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Swapping — safe by default
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
sally = SallyClient("base", RPC, private_key="0x…") # or SALLY_PRIVATE_KEY env
|
|
93
|
+
|
|
94
|
+
# Inspect first: compares every route, simulates each, screens the output token.
|
|
95
|
+
plan = sally.swap.plan(Base.WETH, Base.USDC, 10**18)
|
|
96
|
+
plan.summary() # 'SwapPlan(… out~1702924727 sim=1702924727 min=1694410103 impact=13bps steps=2 SAFE)'
|
|
97
|
+
plan.is_safe # False if honeypot / excessive tax / >15% impact / bad route
|
|
98
|
+
plan.candidates # every route considered, with simulated output
|
|
99
|
+
|
|
100
|
+
# Execute: same vetting, then send + balance-delta assertion.
|
|
101
|
+
receipt = sally.swap.execute(Base.WETH, Base.USDC, 10**18, slippage_bps=50)
|
|
102
|
+
|
|
103
|
+
# Native ETH in — pass the NATIVE sentinel; value is attached for you.
|
|
104
|
+
from sally_defi.token import NATIVE
|
|
105
|
+
sally.swap.execute(NATIVE, Base.USDC, 10**17, slippage_bps=50)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
What `execute` does, in order: approve (exact amount) → enumerate routes →
|
|
109
|
+
integrity-check → simulate each on-chain → pick best realized output →
|
|
110
|
+
honeypot/tax preflight → `minOut` from the simulation → send →
|
|
111
|
+
**assert received ≥ minOut**. See [`docs/safety.md`](docs/safety.md).
|
|
112
|
+
|
|
113
|
+
Tune the guard rails:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from sally_defi import SafetyConfig
|
|
117
|
+
sally = SallyClient("base", RPC, private_key="0x…",
|
|
118
|
+
safety=SafetyConfig(slippage_bps=30, tax_block_bps=3000,
|
|
119
|
+
price_impact_block_bps=1000))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Liquidity
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
sally.liquidity.add_v2(dex_id=4, token_a=Base.WETH, token_b=Base.USDC,
|
|
126
|
+
amount_a=10**15, amount_b=2_000_000, lock_days=0)
|
|
127
|
+
|
|
128
|
+
sally.liquidity.positions_v3(wallet, 0, 10) # list[V3Position]
|
|
129
|
+
sally.liquidity.locks(wallet) # list[Lock]
|
|
130
|
+
sally.liquidity.claimable_fees(npm, token_id) # ClaimableFees
|
|
131
|
+
# add_v3 / add_v4 / add_slipstream, increase/decrease, remove_v2,
|
|
132
|
+
# lock/unlock, claim_fees_v3/v4(+_locked), mass_claim_fees, harvest_op
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Referral system 💸
|
|
136
|
+
|
|
137
|
+
Every swap and liquidity action takes a `referral` address. Pass yours and the
|
|
138
|
+
protocol accrues referral fees to it; read and claim them anytime:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
sally.swap.execute(Base.WETH, Base.USDC, 10**18, referral="0xYourRef")
|
|
142
|
+
|
|
143
|
+
sally.fees.total_referral_owed("0xYourRef") # total owed across tokens
|
|
144
|
+
sally.fees.referral_tokens("0xYourRef") # per-token breakdown (paged)
|
|
145
|
+
sally.fees.claim_referral([Base.USDC, Base.WETH]) # claim selected tokens
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
See [`docs/referrals.md`](docs/referrals.md).
|
|
149
|
+
|
|
150
|
+
## Async · permit · private mempool
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# Async (concurrent quoting + simulation)
|
|
154
|
+
from sally_defi.aio import AsyncSallyClient
|
|
155
|
+
sally = AsyncSallyClient("base", RPC, private_key="0x…")
|
|
156
|
+
await sally.swap.execute(Base.WETH, Base.USDC, 10**18, slippage_bps=50)
|
|
157
|
+
|
|
158
|
+
# EIP-2612 permit instead of approve
|
|
159
|
+
sally.swap.execute(Base.USDC, Base.WETH, 10**8, approval="permit")
|
|
160
|
+
|
|
161
|
+
# Private mempool (MEV protection)
|
|
162
|
+
from sally_defi.constants import PrivateRelays
|
|
163
|
+
SallyClient("base", RPC, private_key="0x…", private_rpc_url=PrivateRelays.FLASHBOTS_FAST)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Full guide: [`docs/advanced.md`](docs/advanced.md).
|
|
167
|
+
|
|
168
|
+
## Namespaces
|
|
169
|
+
|
|
170
|
+
| Namespace | What it covers |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `client.prices` | `usd` (display), `usd_impact` (exec), `spot`, `weth`, `usd_liquidity`, `token_info` |
|
|
173
|
+
| `client.swap` | `quote(_v2/v3/v4/deep)`, `candidates`, `plan`, `simulate_route`, `execute`, `token_info` |
|
|
174
|
+
| `client.wallet` | `balances`, `balances_raw`, `total_usd` |
|
|
175
|
+
| `client.liquidity` | add / increase / decrease / remove / lock / claim / harvest, positions, pool state, dex registry |
|
|
176
|
+
| `client.fees` | referral + batch fee reads & claims, `swap_fee` |
|
|
177
|
+
| `client.treasury` | owners, withdraw, `execute`, `swap_auto` |
|
|
178
|
+
| `client.admin` | owner-only setters, DEX registry, pause, rescue, UUPS upgrade |
|
|
179
|
+
|
|
180
|
+
Raw contracts are always reachable: `client.swap_contract`, `…liquidity_contract`,
|
|
181
|
+
`…lens_contract`, `…sidecar_contract`, `…treasury_contract`.
|
|
182
|
+
|
|
183
|
+
## AI agents
|
|
184
|
+
|
|
185
|
+
Every call returns a JSON-able dataclass with a clear docstring — wrapping Sally as
|
|
186
|
+
LLM tools is trivial. See [`docs/ai-agents.md`](docs/ai-agents.md) and
|
|
187
|
+
[`examples/10_ai_agent_tools.py`](examples/10_ai_agent_tools.py).
|
|
188
|
+
|
|
189
|
+
## Docs
|
|
190
|
+
|
|
191
|
+
| | |
|
|
192
|
+
|---|---|
|
|
193
|
+
| [Why sally-defi-py-sdk](docs/comparison.md) | comparison vs other DeFi SDKs, advantages & trade-offs |
|
|
194
|
+
| [Getting started](docs/getting-started.md) | install, connect, first read & swap |
|
|
195
|
+
| [Swap & safety](docs/safety.md) | route comparison, simulation, all guard rails |
|
|
196
|
+
| [Liquidity](docs/liquidity.md) | add/remove/lock/harvest across V2·V3·V4 |
|
|
197
|
+
| [Pricing](docs/pricing.md) | display vs execution price, spot, impact |
|
|
198
|
+
| [Referrals](docs/referrals.md) | earn & claim referral fees |
|
|
199
|
+
| [Advanced](docs/advanced.md) | async client, EIP-2612 permit, Permit2, private mempool, BSC |
|
|
200
|
+
| [AI agents](docs/ai-agents.md) | typed returns as tool functions |
|
|
201
|
+
| [API reference](docs/api-reference.md) | every namespace + method |
|
|
202
|
+
|
|
203
|
+
## Live deployment (v2.0.2, Base = BSC)
|
|
204
|
+
|
|
205
|
+
| Contract | Address |
|
|
206
|
+
|---|---|
|
|
207
|
+
| SwapController (proxy) | `0x7777D0e2e2d772b5D750540B3932261574e87777` |
|
|
208
|
+
| LiquidityController (proxy) | `0x7777d7fFF155B043CE0A0785dd8c8c42bEbe7777` |
|
|
209
|
+
| Treasury | `0x7777D7dC7ea65F33EC126165e03C6B6030887777` |
|
|
210
|
+
|
|
211
|
+
## Development & tests
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
uv sync
|
|
215
|
+
uv run pytest -m "not fork" # offline unit tests
|
|
216
|
+
anvil --fork-url $BASE_RPC --port 8547 --chain-id 8453 &
|
|
217
|
+
SALLY_RPC_URL=http://127.0.0.1:8547 uv run pytest # + fork smoke tests
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Support
|
|
221
|
+
|
|
222
|
+
Questions, integrations, partnerships → [`support@sally.tools`](mailto:support@sally.tools)
|
|
223
|
+
· [sally.tools](https://sally.tools)
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sally-defi-py-sdk"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Modern Python SDK for the Sally DeFi CrossHybrid protocol (Base + BSC) — swaps, pricing, liquidity, treasury."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = ["defi", "ethereum", "base", "bsc", "web3", "uniswap", "swap", "liquidity", "sally"]
|
|
10
|
+
authors = [{ name = "Sally.tools", email = "support@sally.tools" }]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
21
|
+
"Topic :: Office/Business :: Financial",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"web3>=7.6,<8",
|
|
26
|
+
"eth-account>=0.13,<0.14",
|
|
27
|
+
"eth-abi>=5,<6",
|
|
28
|
+
"eth-utils>=5,<6",
|
|
29
|
+
"pydantic>=2.9,<3",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://sally.tools"
|
|
34
|
+
Repository = "https://github.com/SallyTools/sally-defi-py-sdk"
|
|
35
|
+
Documentation = "https://github.com/SallyTools/sally-defi-py-sdk/tree/main/docs"
|
|
36
|
+
Issues = "https://github.com/SallyTools/sally-defi-py-sdk/issues"
|
|
37
|
+
Changelog = "https://github.com/SallyTools/sally-defi-py-sdk/blob/main/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["uv_build>=0.11.8,<0.12.0"]
|
|
41
|
+
build-backend = "uv_build"
|
|
42
|
+
|
|
43
|
+
[tool.uv.build-backend]
|
|
44
|
+
# Distribution is "sally-defi-py-sdk"; the import module stays "sally_defi".
|
|
45
|
+
module-name = "sally_defi"
|
|
46
|
+
# Ship the deployment JSON (single source of truth) inside the wheel.
|
|
47
|
+
source-include = ["src/sally_defi/data/*.json"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
markers = [
|
|
51
|
+
"fork: integration tests that need an anvil mainnet fork (set SALLY_RPC_URL)",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.mypy]
|
|
55
|
+
python_version = "3.12"
|
|
56
|
+
files = ["src/sally_defi"]
|
|
57
|
+
ignore_missing_imports = true
|
|
58
|
+
warn_unused_ignores = true
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = "py312"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
66
|
+
ignore = ["E501"]
|
|
67
|
+
|
|
68
|
+
[dependency-groups]
|
|
69
|
+
dev = [
|
|
70
|
+
"mypy>=1.11",
|
|
71
|
+
"pytest>=8",
|
|
72
|
+
"ruff>=0.6",
|
|
73
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Sally DeFi SDK — one client for CrossHybrid swaps & liquidity on Base + BSC.
|
|
2
|
+
|
|
3
|
+
Quickstart
|
|
4
|
+
----------
|
|
5
|
+
>>> from sally_defi import SallyClient
|
|
6
|
+
>>> from sally_defi.constants import Base
|
|
7
|
+
>>> sally = SallyClient("base", "https://mainnet.base.org")
|
|
8
|
+
>>> sally.prices.usd(Base.USDC).as_float # display price
|
|
9
|
+
>>> quote = sally.swap.quote(Base.WETH, Base.USDC, 10**18)
|
|
10
|
+
>>> quote.estimated_amount_out
|
|
11
|
+
|
|
12
|
+
To send transactions, give the client a signer:
|
|
13
|
+
>>> sally = SallyClient("base", RPC, private_key="0x…")
|
|
14
|
+
>>> sally.swap.execute(Base.WETH, Base.USDC, 10**17, slippage_bps=50)
|
|
15
|
+
|
|
16
|
+
Everything reads from the bundled ``deployment.json`` (addresses + ABIs only,
|
|
17
|
+
derived from the contracts' deployment artifact). Addresses/ABIs are never
|
|
18
|
+
hard-coded.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from . import constants, deployment
|
|
24
|
+
from .client import SallyClient
|
|
25
|
+
from .errors import (
|
|
26
|
+
SallyConfigError,
|
|
27
|
+
SallyError,
|
|
28
|
+
SallyRevert,
|
|
29
|
+
SallyRouteError,
|
|
30
|
+
SallySafetyError,
|
|
31
|
+
)
|
|
32
|
+
from .permit2 import PERMIT2_ADDRESS, Permit2
|
|
33
|
+
from .safety import RouteCandidate, SafetyConfig, SwapPlan
|
|
34
|
+
from .token import NATIVE, ZERO, Token, TokenAmount
|
|
35
|
+
from .types import (
|
|
36
|
+
ClaimableFees,
|
|
37
|
+
Lock,
|
|
38
|
+
LockKind,
|
|
39
|
+
PoolVersion,
|
|
40
|
+
PriceResult,
|
|
41
|
+
ReferralFeeTokens,
|
|
42
|
+
SwapPath,
|
|
43
|
+
SwapStep,
|
|
44
|
+
TokenInfo,
|
|
45
|
+
V2PoolState,
|
|
46
|
+
V2Position,
|
|
47
|
+
V3PoolState,
|
|
48
|
+
V3Position,
|
|
49
|
+
V4PoolState,
|
|
50
|
+
V4Position,
|
|
51
|
+
WalletBalance,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
56
|
+
|
|
57
|
+
__version__ = version("sally-defi-py-sdk")
|
|
58
|
+
except PackageNotFoundError: # not installed (e.g. running from a source checkout)
|
|
59
|
+
__version__ = "0.0.0+local"
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"SallyClient",
|
|
63
|
+
"SallyError",
|
|
64
|
+
"SallyConfigError",
|
|
65
|
+
"SallyRevert",
|
|
66
|
+
"SallyRouteError",
|
|
67
|
+
"SallySafetyError",
|
|
68
|
+
"SafetyConfig",
|
|
69
|
+
"SwapPlan",
|
|
70
|
+
"RouteCandidate",
|
|
71
|
+
"Permit2",
|
|
72
|
+
"PERMIT2_ADDRESS",
|
|
73
|
+
"Token",
|
|
74
|
+
"TokenAmount",
|
|
75
|
+
"NATIVE",
|
|
76
|
+
"ZERO",
|
|
77
|
+
"constants",
|
|
78
|
+
"deployment",
|
|
79
|
+
"PoolVersion",
|
|
80
|
+
"LockKind",
|
|
81
|
+
"SwapStep",
|
|
82
|
+
"SwapPath",
|
|
83
|
+
"TokenInfo",
|
|
84
|
+
"PriceResult",
|
|
85
|
+
"WalletBalance",
|
|
86
|
+
"V2Position",
|
|
87
|
+
"V3Position",
|
|
88
|
+
"V4Position",
|
|
89
|
+
"V2PoolState",
|
|
90
|
+
"V3PoolState",
|
|
91
|
+
"V4PoolState",
|
|
92
|
+
"Lock",
|
|
93
|
+
"ClaimableFees",
|
|
94
|
+
"ReferralFeeTokens",
|
|
95
|
+
]
|