sally-defi-ts-sdk 0.3.2
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.
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/dist/aio/client.d.ts +93 -0
- package/dist/aio/client.d.ts.map +1 -0
- package/dist/aio/client.js +283 -0
- package/dist/aio/client.js.map +1 -0
- package/dist/aio/index.d.ts +20 -0
- package/dist/aio/index.d.ts.map +1 -0
- package/dist/aio/index.js +19 -0
- package/dist/aio/index.js.map +1 -0
- package/dist/aio/modules/fees.d.ts +19 -0
- package/dist/aio/modules/fees.d.ts.map +1 -0
- package/dist/aio/modules/fees.js +47 -0
- package/dist/aio/modules/fees.js.map +1 -0
- package/dist/aio/modules/liquidity.d.ts +47 -0
- package/dist/aio/modules/liquidity.d.ts.map +1 -0
- package/dist/aio/modules/liquidity.js +115 -0
- package/dist/aio/modules/liquidity.js.map +1 -0
- package/dist/aio/modules/prices.d.ts +18 -0
- package/dist/aio/modules/prices.d.ts.map +1 -0
- package/dist/aio/modules/prices.js +48 -0
- package/dist/aio/modules/prices.js.map +1 -0
- package/dist/aio/modules/swap.d.ts +50 -0
- package/dist/aio/modules/swap.d.ts.map +1 -0
- package/dist/aio/modules/swap.js +267 -0
- package/dist/aio/modules/swap.js.map +1 -0
- package/dist/aio/modules/wallet.d.ts +13 -0
- package/dist/aio/modules/wallet.d.ts.map +1 -0
- package/dist/aio/modules/wallet.js +27 -0
- package/dist/aio/modules/wallet.js.map +1 -0
- package/dist/aio/token.d.ts +19 -0
- package/dist/aio/token.d.ts.map +1 -0
- package/dist/aio/token.js +50 -0
- package/dist/aio/token.js.map +1 -0
- package/dist/client.d.ts +142 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +452 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +36 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +39 -0
- package/dist/constants.js.map +1 -0
- package/dist/data/deployment.json +1 -0
- package/dist/deployment.d.ts +44 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +118 -0
- package/dist/deployment.js.map +1 -0
- package/dist/errors.d.ts +57 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +197 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/fees.d.ts +32 -0
- package/dist/modules/fees.d.ts.map +1 -0
- package/dist/modules/fees.js +64 -0
- package/dist/modules/fees.js.map +1 -0
- package/dist/modules/liquidity.d.ts +134 -0
- package/dist/modules/liquidity.d.ts.map +1 -0
- package/dist/modules/liquidity.js +277 -0
- package/dist/modules/liquidity.js.map +1 -0
- package/dist/modules/prices.d.ts +47 -0
- package/dist/modules/prices.d.ts.map +1 -0
- package/dist/modules/prices.js +85 -0
- package/dist/modules/prices.js.map +1 -0
- package/dist/modules/swap.d.ts +102 -0
- package/dist/modules/swap.d.ts.map +1 -0
- package/dist/modules/swap.js +400 -0
- package/dist/modules/swap.js.map +1 -0
- package/dist/modules/wallet.d.ts +16 -0
- package/dist/modules/wallet.d.ts.map +1 -0
- package/dist/modules/wallet.js +30 -0
- package/dist/modules/wallet.js.map +1 -0
- package/dist/permit2.d.ts +97 -0
- package/dist/permit2.d.ts.map +1 -0
- package/dist/permit2.js +130 -0
- package/dist/permit2.js.map +1 -0
- package/dist/previews.d.ts +57 -0
- package/dist/previews.d.ts.map +1 -0
- package/dist/previews.js +69 -0
- package/dist/previews.js.map +1 -0
- package/dist/safety.d.ts +80 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +133 -0
- package/dist/safety.js.map +1 -0
- package/dist/token.d.ts +215 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +239 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +462 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +13 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +22 -0
- package/dist/util.js.map +1 -0
- package/package.json +48 -0
- package/src/aio/client.ts +329 -0
- package/src/aio/index.ts +20 -0
- package/src/aio/modules/fees.ts +60 -0
- package/src/aio/modules/liquidity.ts +181 -0
- package/src/aio/modules/prices.ts +57 -0
- package/src/aio/modules/swap.ts +347 -0
- package/src/aio/modules/wallet.ts +34 -0
- package/src/aio/token.ts +59 -0
- package/src/client.ts +526 -0
- package/src/constants.ts +43 -0
- package/src/data/deployment.json +1 -0
- package/src/deployment.ts +132 -0
- package/src/errors.ts +215 -0
- package/src/index.ts +90 -0
- package/src/modules/fees.ts +78 -0
- package/src/modules/liquidity.ts +446 -0
- package/src/modules/prices.ts +97 -0
- package/src/modules/swap.ts +502 -0
- package/src/modules/wallet.ts +37 -0
- package/src/permit2.ts +169 -0
- package/src/previews.ts +95 -0
- package/src/safety.ts +152 -0
- package/src/token.ts +254 -0
- package/src/types.ts +438 -0
- package/src/util.ts +20 -0
package/LICENSE
ADDED
|
@@ -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.
|
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/header.svg" alt="sally-defi-ts-sdk · TypeScript SDK" width="100%">
|
|
4
|
+
|
|
5
|
+
# sally-defi-ts-sdk · TypeScript 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
|
+
and referral fees — behind one typed, beginner-friendly and AI-agent-ready API.
|
|
12
|
+
|
|
13
|
+
**100% on-chain — no API, no subgraph, no backend.**
|
|
14
|
+
|
|
15
|
+
[Website](https://sally.tools) · [Docs](docs/) · [Examples](examples/) · `support@sally.tools`
|
|
16
|
+
|
|
17
|
+
> A faithful 1:1 TypeScript port of the [sally-defi-py-sdk](https://github.com/SallyTools/sally-defi-py-sdk) (`v0.3.2`, deployment `v2.0.2`), built on [ethers v6](https://docs.ethers.org/v6/).
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why sally-defi-ts-sdk
|
|
24
|
+
|
|
25
|
+
Most TypeScript DeFi code is raw `ethers`/`viem`: untyped tuples, hand-rolled
|
|
26
|
+
approvals, a quote you *hope* matches execution, and zero protection against
|
|
27
|
+
honeypots or sandwiches. sally-defi-ts-sdk collapses all of that into **one safe
|
|
28
|
+
call**.
|
|
29
|
+
|
|
30
|
+
`swap.execute` does the work a careful integrator would do by hand — approve →
|
|
31
|
+
enumerate routes → **simulate every route on-chain** → screen the output token for
|
|
32
|
+
honeypot/tax → set `minOut` from the *simulated* output → send → **assert you
|
|
33
|
+
actually received it**. That whole pipeline is the default, not an opt-in.
|
|
34
|
+
|
|
35
|
+
* 🌐 **Fully on-chain & decentralized.** Every read — prices, routes, positions,
|
|
36
|
+
balances — is a direct `eth_call` to the contracts. **No proprietary API, no
|
|
37
|
+
subgraph / Graph Node, no backend.** Point it at any RPC (or your own node) and
|
|
38
|
+
the only thing you trust is the chain itself.
|
|
39
|
+
* 🎯 **Exact output, not an estimate.** Routes are ranked and `minOut` is set from
|
|
40
|
+
the realized `amountOut` of the *real* `executeHybridSwap` (`eth_call`'d against
|
|
41
|
+
live state), not a reserve-formula guess — `plan.simulatedOut` is what you'd
|
|
42
|
+
actually receive. See [docs/safety.md](docs/safety.md).
|
|
43
|
+
* 🛡️ **Safety in the hot path.** Simulate-before-execute + post-trade balance-delta
|
|
44
|
+
assertion — not just in your test suite.
|
|
45
|
+
* 🧱 **Typed end-to-end.** `pos.liquidity`, `plan.isSafe`, `quote.estimatedAmountOut`
|
|
46
|
+
— classes, autocomplete, ships `.d.ts`. No `tuple[7]`.
|
|
47
|
+
* 🤖 **AI-agent-ready by construction.** Every return serializes cleanly, so wiring
|
|
48
|
+
Sally into LLM tools is mechanical.
|
|
49
|
+
* ⚡ **Async + MEV-protection + permit** are constructor/option arguments, not projects.
|
|
50
|
+
* 🪶 **Light deps** (just `ethers`), Node ≥ 18, full TypeScript types.
|
|
51
|
+
|
|
52
|
+
Honest about scope: Sally routes within **its own protocol** on **Base + BSC** — it
|
|
53
|
+
is not a 100-source aggregator. Full, cited comparison (advantages *and* trade-offs)
|
|
54
|
+
vs raw ethers.js / viem / Uniswap SDK / aggregator SDKs:
|
|
55
|
+
**[docs/comparison.md](docs/comparison.md)**.
|
|
56
|
+
|
|
57
|
+
### 🌐 Fully on-chain, fully decentralized
|
|
58
|
+
|
|
59
|
+
The SDK talks to **the chain and nothing else**. Routing, pricing, positions,
|
|
60
|
+
honeypot screening and balances are all read with plain `eth_call`s against the
|
|
61
|
+
on-chain contracts — there is **no Sally API, no Graph Node / subgraph, no indexer
|
|
62
|
+
and no backend service** in the data path. The only runtime dependency is `ethers`.
|
|
63
|
+
Bring your own RPC or node and you trust nothing but the chain.
|
|
64
|
+
|
|
65
|
+
## What you get
|
|
66
|
+
|
|
67
|
+
* **One object, every function.** `SallyClient` exposes the whole protocol through
|
|
68
|
+
feature namespaces (`prices`, `swap`, `wallet`, `liquidity`, `fees`). Reads need
|
|
69
|
+
only an RPC; writes need a signer.
|
|
70
|
+
* **Safe by default.** `swap.execute` enumerates routes, **integrity-checks** each so
|
|
71
|
+
funds can never enter a wrong pool, **simulates** every candidate on-chain, picks
|
|
72
|
+
the best realized output, runs a **honeypot/tax probe** on the output token, sets
|
|
73
|
+
`minOut` from the simulation, then **asserts the received balance actually grew**.
|
|
74
|
+
* **Single source of truth.** All addresses + ABIs come from the bundled
|
|
75
|
+
`deployment.json` (addresses + ABIs only). Nothing hard-coded; drop in a new
|
|
76
|
+
deployment and the SDK follows.
|
|
77
|
+
* **Proxy-fallback aware.** v2.0.2 serves Lens views through the swap proxy and
|
|
78
|
+
Sidecar views through the liq proxy — the SDK wires this for you.
|
|
79
|
+
* **Typed returns.** Every struct is a class (`SwapPath`, `V3Position`, `Lock`,
|
|
80
|
+
`SwapPlan`, …) — `pos.liquidity`, not `pos[7]`.
|
|
81
|
+
|
|
82
|
+
## Install
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install sally-defi-ts-sdk ethers
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Requires Node ≥ 18 and `ethers >= 6`.
|
|
89
|
+
|
|
90
|
+
## Quickstart (read-only)
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { SallyClient, Base } from "sally-defi-ts-sdk";
|
|
94
|
+
|
|
95
|
+
const sally = new SallyClient("base", "https://mainnet.base.org");
|
|
96
|
+
|
|
97
|
+
(await sally.prices.usd(Base.USDC)).asFloat; // 1.0 (display / spot-mid)
|
|
98
|
+
(await sally.prices.usdImpact(Base.WETH)).asFloat; // 1703.19 (execution-aware)
|
|
99
|
+
|
|
100
|
+
const quote = await sally.swap.quote(Base.WETH, Base.USDC, 10n ** 18n);
|
|
101
|
+
Number(quote.estimatedAmountOut) / 1e6; // 1703.45 USDC
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Swapping — safe by default
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const sally = new SallyClient("base", RPC, { privateKey: "0x…" }); // or SALLY_PRIVATE_KEY env
|
|
108
|
+
|
|
109
|
+
// Inspect first: compares every route, simulates each, screens the output token.
|
|
110
|
+
const plan = await sally.swap.plan(Base.WETH, Base.USDC, 10n ** 18n);
|
|
111
|
+
plan.summary(); // 'SwapPlan(… out~1702924727 sim=1702924727 min=1694410103 impact=13bps steps=2 SAFE)'
|
|
112
|
+
plan.isSafe; // false if honeypot / excessive tax / >15% impact / bad route
|
|
113
|
+
plan.candidates; // every route considered, with simulated output
|
|
114
|
+
|
|
115
|
+
// Execute: same vetting, then send + balance-delta assertion.
|
|
116
|
+
const receipt = await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { slippageBps: 50 });
|
|
117
|
+
|
|
118
|
+
// Native ETH in — pass the NATIVE sentinel; value is attached for you.
|
|
119
|
+
import { NATIVE } from "sally-defi-ts-sdk";
|
|
120
|
+
await sally.swap.execute(NATIVE, Base.USDC, 10n ** 17n, { slippageBps: 50 });
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
What `execute` does, in order: approve (exact amount) → enumerate routes →
|
|
124
|
+
integrity-check → simulate each on-chain → pick best realized output →
|
|
125
|
+
honeypot/tax preflight → `minOut` from the simulation → send →
|
|
126
|
+
**assert received ≥ minOut**. See [`docs/safety.md`](docs/safety.md).
|
|
127
|
+
|
|
128
|
+
Tune the guard rails:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { SafetyConfig } from "sally-defi-ts-sdk";
|
|
132
|
+
const sally = new SallyClient("base", RPC, {
|
|
133
|
+
privateKey: "0x…",
|
|
134
|
+
safety: new SafetyConfig({ slippageBps: 30, taxBlockBps: 3000, priceImpactBlockBps: 1000 }),
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Liquidity
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
await sally.liquidity.addV2(4, Base.WETH, Base.USDC, 10n ** 15n, 2_000_000n, { lockDays: 0 });
|
|
142
|
+
|
|
143
|
+
await sally.liquidity.positionsV3(wallet, 0, 10); // V3Position[]
|
|
144
|
+
await sally.liquidity.locks(wallet); // Lock[]
|
|
145
|
+
await sally.liquidity.claimableFees(npm, tokenId); // ClaimableFees
|
|
146
|
+
// addV3 / addV4 / addSlipstream, increase/decrease, removeV2,
|
|
147
|
+
// lock/unlock, claimFeesV3/V4(+Locked), massClaimFees, harvestOp
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Referral system 💸
|
|
151
|
+
|
|
152
|
+
Every swap and liquidity action takes a `referral` address. Pass yours and the
|
|
153
|
+
protocol accrues referral fees to it; read and claim them anytime:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { referral: "0xYourRef" });
|
|
157
|
+
|
|
158
|
+
await sally.fees.totalReferralOwed("0xYourRef"); // total owed across tokens
|
|
159
|
+
await sally.fees.referralTokens("0xYourRef"); // per-token breakdown (paged)
|
|
160
|
+
await sally.fees.claimReferral([Base.USDC, Base.WETH]); // claim selected tokens
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See [`docs/referrals.md`](docs/referrals.md).
|
|
164
|
+
|
|
165
|
+
## Async · permit · private mempool
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// Async (concurrent quoting + simulation)
|
|
169
|
+
import { AsyncSallyClient } from "sally-defi-ts-sdk/aio";
|
|
170
|
+
import { Base } from "sally-defi-ts-sdk";
|
|
171
|
+
const sally = new AsyncSallyClient("base", RPC, { privateKey: "0x…" });
|
|
172
|
+
await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { slippageBps: 50 });
|
|
173
|
+
|
|
174
|
+
// EIP-2612 permit instead of approve
|
|
175
|
+
await sally.swap.execute(Base.USDC, Base.WETH, 10n ** 8n, { approval: "permit" });
|
|
176
|
+
|
|
177
|
+
// Private mempool (MEV protection)
|
|
178
|
+
import { PrivateRelays } from "sally-defi-ts-sdk";
|
|
179
|
+
new SallyClient("base", RPC, { privateKey: "0x…", privateRpcUrl: PrivateRelays.FLASHBOTS_FAST });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Full guide: [`docs/advanced.md`](docs/advanced.md).
|
|
183
|
+
|
|
184
|
+
## Sign with your own wallet 🔑
|
|
185
|
+
|
|
186
|
+
Give a `privateKey`, a `mnemonic`, or **no key at all** — build a vetted, simulated,
|
|
187
|
+
ABI-decoded *unsigned* transaction and sign it with your own ethers wallet:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
// mnemonic seed phrase
|
|
191
|
+
const sally = new SallyClient("base", RPC, { mnemonic: "word1 … word12", accountIndex: 0 });
|
|
192
|
+
|
|
193
|
+
// external signing — the SDK never holds your key
|
|
194
|
+
const ext = new SallyClient("base", RPC, { address: "0xYourWallet" }); // knows sender, no key
|
|
195
|
+
const build = await ext.swap.execute(Base.WETH, Base.USDC, 10n ** 18n, { buildOnly: true });
|
|
196
|
+
build.plan.summary(); // what happens to your wallet: out, min, impact, safe?
|
|
197
|
+
build.needsApproval; // sign build.approveTx first if true
|
|
198
|
+
build.swapTx; // unsigned tx → sign with your own account, then broadcast
|
|
199
|
+
|
|
200
|
+
// preview ANY call: decoded function + args + simulated result + revert reason
|
|
201
|
+
(await ext.preview(fn, args)).summary();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
See [`docs/signing.md`](docs/signing.md).
|
|
205
|
+
|
|
206
|
+
## Namespaces
|
|
207
|
+
|
|
208
|
+
| Namespace | What it covers |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `client.prices` | `usd` (display), `usdImpact` (exec), `spot`, `weth`, `usdLiquidity`, `tokenInfo` |
|
|
211
|
+
| `client.swap` | `quote(V2/V3/V4/Deep)`, `candidates`, `plan`, `simulateRoute`, `execute` (+ `buildOnly`), `tokenInfo` |
|
|
212
|
+
| `client.wallet` | `balances`, `balancesRaw`, `totalUsd` |
|
|
213
|
+
| `client.liquidity` | add / increase / decrease / remove / lock / claim / harvest, positions, pool state, dex registry |
|
|
214
|
+
| `client.fees` | referral + batch fee reads & claims, `swapFee` |
|
|
215
|
+
|
|
216
|
+
Raw contracts are always reachable: `client.swapContract`, `…liquidityContract`,
|
|
217
|
+
`…lensContract`, `…sidecarContract`.
|
|
218
|
+
|
|
219
|
+
## AI agents
|
|
220
|
+
|
|
221
|
+
Every call returns a JSON-able typed object with a clear doc comment — wrapping Sally
|
|
222
|
+
as LLM tools is trivial. See [`docs/ai-agents.md`](docs/ai-agents.md) and
|
|
223
|
+
[`examples/10_ai_agent_tools.ts`](examples/10_ai_agent_tools.ts).
|
|
224
|
+
|
|
225
|
+
## Docs
|
|
226
|
+
|
|
227
|
+
| | |
|
|
228
|
+
|---|---|
|
|
229
|
+
| [Why sally-defi-ts-sdk](docs/comparison.md) | comparison vs other DeFi SDKs, advantages & trade-offs |
|
|
230
|
+
| [Getting started](docs/getting-started.md) | install, connect, first read & swap |
|
|
231
|
+
| [Swap & safety](docs/safety.md) | route comparison, simulation, all guard rails |
|
|
232
|
+
| [Liquidity](docs/liquidity.md) | add/remove/lock/harvest across V2·V3·V4 |
|
|
233
|
+
| [Pricing](docs/pricing.md) | display vs execution price, spot, impact |
|
|
234
|
+
| [Referrals](docs/referrals.md) | earn & claim referral fees |
|
|
235
|
+
| [Advanced](docs/advanced.md) | async client, EIP-2612 permit, Permit2, private mempool, BSC |
|
|
236
|
+
| [AI agents](docs/ai-agents.md) | typed returns as tool functions |
|
|
237
|
+
| [API reference](docs/api-reference.md) | every namespace + method |
|
|
238
|
+
|
|
239
|
+
## Live deployment (v2.0.2, Base = BSC)
|
|
240
|
+
|
|
241
|
+
| Contract | Address |
|
|
242
|
+
|---|---|
|
|
243
|
+
| SwapController (proxy) | `0x7777D0e2e2d772b5D750540B3932261574e87777` |
|
|
244
|
+
| LiquidityController (proxy) | `0x7777d7fFF155B043CE0A0785dd8c8c42bEbe7777` |
|
|
245
|
+
| Treasury | `0x7777D7dC7ea65F33EC126165e03C6B6030887777` |
|
|
246
|
+
|
|
247
|
+
## Development & tests
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm install
|
|
251
|
+
npm run build # tsc -> dist/ (+ copies deployment.json)
|
|
252
|
+
npm test # offline vitest unit tests (no network)
|
|
253
|
+
npm run typecheck # tsc --noEmit
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Support
|
|
257
|
+
|
|
258
|
+
Questions, integrations, partnerships → [`support@sally.tools`](mailto:support@sally.tools)
|
|
259
|
+
· [sally.tools](https://sally.tools)
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async entry point: {@link AsyncSallyClient}.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors {@link SallyClient} on top of ethers' provider for non-blocking I/O.
|
|
5
|
+
* Shares all pure machinery (deployment, types, safety, errors, permit signing)
|
|
6
|
+
* with the sync client; only the network calls are awaited.
|
|
7
|
+
*
|
|
8
|
+
* In JS every network call is already async, so this client mainly differs from
|
|
9
|
+
* the main one in that its candidate quoting/simulation fans out concurrently
|
|
10
|
+
* (`Promise.all`), exactly like the Python `aio` client uses `asyncio.gather`.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { AsyncSallyClient } from "sally-defi-ts-sdk/aio";
|
|
14
|
+
* import { Base } from "sally-defi-ts-sdk";
|
|
15
|
+
*
|
|
16
|
+
* const sally = new AsyncSallyClient("base", RPC, { privateKey: "0x…" });
|
|
17
|
+
* console.log((await sally.prices.usd(Base.USDC)).asFloat);
|
|
18
|
+
* await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 17n, { slippageBps: 50 });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { Contract, JsonRpcProvider, type Signer } from "ethers";
|
|
22
|
+
import { SafetyConfig } from "../safety.js";
|
|
23
|
+
import { AsyncToken } from "./token.js";
|
|
24
|
+
import { AsyncPrices } from "./modules/prices.js";
|
|
25
|
+
import { AsyncSwap } from "./modules/swap.js";
|
|
26
|
+
import { AsyncWallet } from "./modules/wallet.js";
|
|
27
|
+
import { AsyncLiquidity } from "./modules/liquidity.js";
|
|
28
|
+
import { AsyncFees } from "./modules/fees.js";
|
|
29
|
+
export interface AsyncSallyClientOptions {
|
|
30
|
+
privateKey?: string | null;
|
|
31
|
+
mnemonic?: string | null;
|
|
32
|
+
accountIndex?: number;
|
|
33
|
+
account?: Signer | null;
|
|
34
|
+
address?: string | null;
|
|
35
|
+
poa?: boolean | null;
|
|
36
|
+
safety?: SafetyConfig | null;
|
|
37
|
+
privateRpcUrl?: string | null;
|
|
38
|
+
gasMultiplier?: number;
|
|
39
|
+
}
|
|
40
|
+
type Fn = any;
|
|
41
|
+
export declare class AsyncSallyClient {
|
|
42
|
+
readonly chainKey: string;
|
|
43
|
+
readonly chainId: number;
|
|
44
|
+
safety: SafetyConfig;
|
|
45
|
+
privateRpcUrl: string | null;
|
|
46
|
+
private _privateW3;
|
|
47
|
+
gasMultiplier: number;
|
|
48
|
+
w3: JsonRpcProvider;
|
|
49
|
+
account: Signer | null;
|
|
50
|
+
private _sender;
|
|
51
|
+
private _sendLock;
|
|
52
|
+
addresses: Record<string, string>;
|
|
53
|
+
private _cache;
|
|
54
|
+
constructor(chain?: string | number, rpcUrl?: string | null, opts?: AsyncSallyClientOptions);
|
|
55
|
+
isConnected(): Promise<boolean>;
|
|
56
|
+
get address(): string | null;
|
|
57
|
+
get canSign(): boolean;
|
|
58
|
+
requireAddress(): string;
|
|
59
|
+
requireSigner(): Signer;
|
|
60
|
+
private _contract;
|
|
61
|
+
private _cached;
|
|
62
|
+
get swapContract(): Contract;
|
|
63
|
+
get liquidityContract(): Contract;
|
|
64
|
+
get lensContract(): Contract;
|
|
65
|
+
get sidecarContract(): Contract;
|
|
66
|
+
token(address: string): AsyncToken;
|
|
67
|
+
call(fn: Fn, args?: any[], opts?: {
|
|
68
|
+
value?: bigint;
|
|
69
|
+
sender?: string | null;
|
|
70
|
+
}): Promise<any>;
|
|
71
|
+
buildTx(fn: Fn, args?: any[], opts?: {
|
|
72
|
+
value?: bigint;
|
|
73
|
+
gas?: bigint | null;
|
|
74
|
+
}): Promise<Record<string, any>>;
|
|
75
|
+
send(fn: Fn, args?: any[], opts?: {
|
|
76
|
+
value?: bigint;
|
|
77
|
+
gas?: bigint | null;
|
|
78
|
+
wait?: boolean;
|
|
79
|
+
simulate?: boolean;
|
|
80
|
+
private?: boolean | null;
|
|
81
|
+
timeout?: number;
|
|
82
|
+
buildOnly?: boolean;
|
|
83
|
+
}): Promise<any>;
|
|
84
|
+
private _broadcastPrivate;
|
|
85
|
+
get prices(): AsyncPrices;
|
|
86
|
+
get swap(): AsyncSwap;
|
|
87
|
+
get wallet(): AsyncWallet;
|
|
88
|
+
get liquidity(): AsyncLiquidity;
|
|
89
|
+
get fees(): AsyncFees;
|
|
90
|
+
toString(): string;
|
|
91
|
+
}
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/aio/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async entry point: {@link AsyncSallyClient}.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors {@link SallyClient} on top of ethers' provider for non-blocking I/O.
|
|
5
|
+
* Shares all pure machinery (deployment, types, safety, errors, permit signing)
|
|
6
|
+
* with the sync client; only the network calls are awaited.
|
|
7
|
+
*
|
|
8
|
+
* In JS every network call is already async, so this client mainly differs from
|
|
9
|
+
* the main one in that its candidate quoting/simulation fans out concurrently
|
|
10
|
+
* (`Promise.all`), exactly like the Python `aio` client uses `asyncio.gather`.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { AsyncSallyClient } from "sally-defi-ts-sdk/aio";
|
|
14
|
+
* import { Base } from "sally-defi-ts-sdk";
|
|
15
|
+
*
|
|
16
|
+
* const sally = new AsyncSallyClient("base", RPC, { privateKey: "0x…" });
|
|
17
|
+
* console.log((await sally.prices.usd(Base.USDC)).asFloat);
|
|
18
|
+
* await sally.swap.execute(Base.WETH, Base.USDC, 10n ** 17n, { slippageBps: 50 });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { Contract, HDNodeWallet, JsonRpcProvider, Network, Wallet, getAddress, } from "ethers";
|
|
22
|
+
import * as deployment from "../deployment.js";
|
|
23
|
+
import { SallyConfigError, SallyError, SallyRevert, wrapWeb3Error } from "../errors.js";
|
|
24
|
+
import { SafetyConfig } from "../safety.js";
|
|
25
|
+
import { AsyncToken } from "./token.js";
|
|
26
|
+
import { AsyncPrices } from "./modules/prices.js";
|
|
27
|
+
import { AsyncSwap } from "./modules/swap.js";
|
|
28
|
+
import { AsyncWallet } from "./modules/wallet.js";
|
|
29
|
+
import { AsyncLiquidity } from "./modules/liquidity.js";
|
|
30
|
+
import { AsyncFees } from "./modules/fees.js";
|
|
31
|
+
/** Minimal async mutex: serialize an async critical section. */
|
|
32
|
+
class AsyncMutex {
|
|
33
|
+
_chain = Promise.resolve();
|
|
34
|
+
async run(fn) {
|
|
35
|
+
const prev = this._chain;
|
|
36
|
+
let release;
|
|
37
|
+
this._chain = new Promise((res) => (release = res));
|
|
38
|
+
await prev;
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
release();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class AsyncSallyClient {
|
|
48
|
+
chainKey;
|
|
49
|
+
chainId;
|
|
50
|
+
safety;
|
|
51
|
+
privateRpcUrl;
|
|
52
|
+
_privateW3 = null;
|
|
53
|
+
gasMultiplier;
|
|
54
|
+
w3;
|
|
55
|
+
account = null;
|
|
56
|
+
_sender = null;
|
|
57
|
+
_sendLock = new AsyncMutex();
|
|
58
|
+
addresses;
|
|
59
|
+
_cache = {};
|
|
60
|
+
constructor(chain = "base", rpcUrl, opts = {}) {
|
|
61
|
+
this.chainKey = deployment.normalizeChain(chain);
|
|
62
|
+
this.chainId = deployment.chainId(this.chainKey);
|
|
63
|
+
this.safety = opts.safety ?? new SafetyConfig();
|
|
64
|
+
this.privateRpcUrl = opts.privateRpcUrl ?? process.env.SALLY_PRIVATE_RPC_URL ?? null;
|
|
65
|
+
this.gasMultiplier = opts.gasMultiplier ?? 1.25;
|
|
66
|
+
rpcUrl = rpcUrl ?? process.env.SALLY_RPC_URL ?? null;
|
|
67
|
+
if (!rpcUrl) {
|
|
68
|
+
throw new SallyConfigError("No rpcUrl given and SALLY_RPC_URL is unset.");
|
|
69
|
+
}
|
|
70
|
+
const network = Network.from(this.chainId);
|
|
71
|
+
this.w3 = new JsonRpcProvider(rpcUrl, network, { staticNetwork: network });
|
|
72
|
+
void (opts.poa ?? this.chainKey === "bsc");
|
|
73
|
+
const key = opts.privateKey ?? process.env.SALLY_PRIVATE_KEY ?? null;
|
|
74
|
+
const phrase = opts.mnemonic ?? process.env.SALLY_MNEMONIC ?? null;
|
|
75
|
+
if (opts.account != null) {
|
|
76
|
+
this.account = opts.account;
|
|
77
|
+
}
|
|
78
|
+
else if (key) {
|
|
79
|
+
this.account = new Wallet(key);
|
|
80
|
+
}
|
|
81
|
+
else if (phrase) {
|
|
82
|
+
const idx = opts.accountIndex ?? 0;
|
|
83
|
+
this.account = HDNodeWallet.fromPhrase(phrase, undefined, `m/44'/60'/0'/0/${idx}`);
|
|
84
|
+
}
|
|
85
|
+
this._sender = this.account
|
|
86
|
+
? getAddress(this.account.address)
|
|
87
|
+
: opts.address
|
|
88
|
+
? getAddress(opts.address)
|
|
89
|
+
: null;
|
|
90
|
+
this.addresses = Object.fromEntries(Object.entries(deployment.addresses(this.chainKey)).map(([k, v]) => [
|
|
91
|
+
k,
|
|
92
|
+
v.startsWith("0x") ? getAddress(v) : v,
|
|
93
|
+
]));
|
|
94
|
+
}
|
|
95
|
+
// -- identity ---------------------------------------------------------- //
|
|
96
|
+
async isConnected() {
|
|
97
|
+
try {
|
|
98
|
+
await this.w3.getBlockNumber();
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
get address() {
|
|
106
|
+
return this._sender;
|
|
107
|
+
}
|
|
108
|
+
get canSign() {
|
|
109
|
+
return this.account !== null;
|
|
110
|
+
}
|
|
111
|
+
requireAddress() {
|
|
112
|
+
if (!this._sender) {
|
|
113
|
+
throw new SallyConfigError("This action needs a sender (privateKey=…, mnemonic=…, or address='0x…').");
|
|
114
|
+
}
|
|
115
|
+
return this._sender;
|
|
116
|
+
}
|
|
117
|
+
requireSigner() {
|
|
118
|
+
if (this.account === null) {
|
|
119
|
+
throw new SallyConfigError("This action signs a tx and needs a key (privateKey=… or mnemonic=…).");
|
|
120
|
+
}
|
|
121
|
+
return this.account;
|
|
122
|
+
}
|
|
123
|
+
// -- contracts --------------------------------------------------------- //
|
|
124
|
+
_contract(address, component) {
|
|
125
|
+
return new Contract(getAddress(address), deployment.abi(this.chainKey, component), this.w3);
|
|
126
|
+
}
|
|
127
|
+
_cached(key, make) {
|
|
128
|
+
if (!(key in this._cache))
|
|
129
|
+
this._cache[key] = make();
|
|
130
|
+
return this._cache[key];
|
|
131
|
+
}
|
|
132
|
+
get swapContract() {
|
|
133
|
+
return this._cached("swapContract", () => this._contract(this.addresses["swap"], "swap"));
|
|
134
|
+
}
|
|
135
|
+
get liquidityContract() {
|
|
136
|
+
return this._cached("liquidityContract", () => this._contract(this.addresses["liquidity"], "liquidity"));
|
|
137
|
+
}
|
|
138
|
+
get lensContract() {
|
|
139
|
+
return this._cached("lensContract", () => this._contract(this.addresses["lens"], "lens"));
|
|
140
|
+
}
|
|
141
|
+
get sidecarContract() {
|
|
142
|
+
return this._cached("sidecarContract", () => this._contract(this.addresses["sidecar"], "sidecar"));
|
|
143
|
+
}
|
|
144
|
+
token(address) {
|
|
145
|
+
return new AsyncToken(this, address);
|
|
146
|
+
}
|
|
147
|
+
// -- call / send ------------------------------------------------------- //
|
|
148
|
+
async call(fn, args = [], opts = {}) {
|
|
149
|
+
const overrides = {};
|
|
150
|
+
if (opts.value)
|
|
151
|
+
overrides.value = opts.value;
|
|
152
|
+
const sender = opts.sender ?? this.address;
|
|
153
|
+
if (sender)
|
|
154
|
+
overrides.from = sender;
|
|
155
|
+
try {
|
|
156
|
+
return await fn.staticCall(...args, overrides);
|
|
157
|
+
}
|
|
158
|
+
catch (exc) {
|
|
159
|
+
throw wrapWeb3Error(this.chainKey, exc);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async buildTx(fn, args = [], opts = {}) {
|
|
163
|
+
const sender = this.requireAddress();
|
|
164
|
+
const overrides = { from: sender };
|
|
165
|
+
if (opts.value)
|
|
166
|
+
overrides.value = opts.value;
|
|
167
|
+
let tx;
|
|
168
|
+
try {
|
|
169
|
+
tx = { ...(await fn.populateTransaction(...args, overrides)) };
|
|
170
|
+
}
|
|
171
|
+
catch (exc) {
|
|
172
|
+
throw wrapWeb3Error(this.chainKey, exc);
|
|
173
|
+
}
|
|
174
|
+
tx.nonce = await this.w3.getTransactionCount(sender, "pending");
|
|
175
|
+
tx.chainId = this.chainId;
|
|
176
|
+
if (opts.value)
|
|
177
|
+
tx.value = opts.value;
|
|
178
|
+
const latest = await this.w3.getBlock("latest");
|
|
179
|
+
const feeData = await this.w3.getFeeData();
|
|
180
|
+
if (latest && latest.baseFeePerGas != null) {
|
|
181
|
+
let tip = feeData.maxPriorityFeePerGas;
|
|
182
|
+
if (tip == null)
|
|
183
|
+
tip = 1000000000n;
|
|
184
|
+
tx.maxPriorityFeePerGas = tip;
|
|
185
|
+
tx.maxFeePerGas = tip + 2n * latest.baseFeePerGas;
|
|
186
|
+
tx.type = 2;
|
|
187
|
+
delete tx.gasPrice;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
tx.gasPrice = feeData.gasPrice ?? 0n;
|
|
191
|
+
tx.type = 0;
|
|
192
|
+
}
|
|
193
|
+
let gas = opts.gas ?? null;
|
|
194
|
+
if (gas !== null) {
|
|
195
|
+
tx.gasLimit = gas;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const overrides2 = { from: sender };
|
|
199
|
+
if (opts.value)
|
|
200
|
+
overrides2.value = opts.value;
|
|
201
|
+
let est = BigInt(await fn.estimateGas(...args, overrides2));
|
|
202
|
+
if (this.gasMultiplier && this.gasMultiplier !== 1.0) {
|
|
203
|
+
est = (est * BigInt(Math.round(this.gasMultiplier * 1000))) / 1000n;
|
|
204
|
+
}
|
|
205
|
+
tx.gasLimit = est;
|
|
206
|
+
}
|
|
207
|
+
delete tx.gas;
|
|
208
|
+
return tx;
|
|
209
|
+
}
|
|
210
|
+
async send(fn, args = [], opts = {}) {
|
|
211
|
+
const value = opts.value ?? 0n;
|
|
212
|
+
const wait = opts.wait ?? true;
|
|
213
|
+
const simulate = opts.simulate ?? true;
|
|
214
|
+
const timeout = opts.timeout ?? 180;
|
|
215
|
+
if (simulate) {
|
|
216
|
+
await this.call(fn, args, { value, sender: this.address });
|
|
217
|
+
}
|
|
218
|
+
if (opts.buildOnly) {
|
|
219
|
+
return this.buildTx(fn, args, { value, gas: opts.gas ?? null });
|
|
220
|
+
}
|
|
221
|
+
const account = this.requireSigner();
|
|
222
|
+
const usePrivate = opts.private == null ? this.privateRpcUrl !== null : opts.private;
|
|
223
|
+
const txh = await this._sendLock.run(async () => {
|
|
224
|
+
const tx = await this.buildTx(fn, args, { value, gas: opts.gas ?? null });
|
|
225
|
+
const raw = await account.signTransaction(tx);
|
|
226
|
+
if (usePrivate)
|
|
227
|
+
return this._broadcastPrivate(raw);
|
|
228
|
+
const resp = await this.w3.broadcastTransaction(raw);
|
|
229
|
+
return resp.hash;
|
|
230
|
+
});
|
|
231
|
+
if (!wait)
|
|
232
|
+
return txh;
|
|
233
|
+
let receipt;
|
|
234
|
+
try {
|
|
235
|
+
receipt = await this.w3.waitForTransaction(txh, 1, timeout * 1000);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
receipt = null;
|
|
239
|
+
}
|
|
240
|
+
if (receipt === null) {
|
|
241
|
+
const where = usePrivate ? "private relay" : "public mempool";
|
|
242
|
+
throw new SallyError(`tx ${txh} not mined within ${timeout}s via ${where}. It may have been ` +
|
|
243
|
+
`dropped/not included — check the hash before resubmitting.`);
|
|
244
|
+
}
|
|
245
|
+
if (receipt.status !== 1) {
|
|
246
|
+
throw new SallyRevert("Error", [], null, `tx ${txh} reverted on-chain`);
|
|
247
|
+
}
|
|
248
|
+
return receipt;
|
|
249
|
+
}
|
|
250
|
+
async _broadcastPrivate(rawTx) {
|
|
251
|
+
if (!this.privateRpcUrl) {
|
|
252
|
+
throw new SallyConfigError("private=true but no privateRpcUrl configured.");
|
|
253
|
+
}
|
|
254
|
+
if (this._privateW3 === null) {
|
|
255
|
+
this._privateW3 = new JsonRpcProvider(this.privateRpcUrl, Network.from(this.chainId), {
|
|
256
|
+
staticNetwork: Network.from(this.chainId),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const resp = await this._privateW3.broadcastTransaction(rawTx);
|
|
260
|
+
return resp.hash;
|
|
261
|
+
}
|
|
262
|
+
// -- namespaces -------------------------------------------------------- //
|
|
263
|
+
get prices() {
|
|
264
|
+
return this._cached("prices", () => new AsyncPrices(this));
|
|
265
|
+
}
|
|
266
|
+
get swap() {
|
|
267
|
+
return this._cached("swap", () => new AsyncSwap(this));
|
|
268
|
+
}
|
|
269
|
+
get wallet() {
|
|
270
|
+
return this._cached("wallet", () => new AsyncWallet(this));
|
|
271
|
+
}
|
|
272
|
+
get liquidity() {
|
|
273
|
+
return this._cached("liquidity", () => new AsyncLiquidity(this));
|
|
274
|
+
}
|
|
275
|
+
get fees() {
|
|
276
|
+
return this._cached("fees", () => new AsyncFees(this));
|
|
277
|
+
}
|
|
278
|
+
toString() {
|
|
279
|
+
const who = this.address ?? "read-only";
|
|
280
|
+
return `<AsyncSallyClient chain=${this.chainKey} id=${this.chainId} signer=${who}>`;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=client.js.map
|