@steerprotocol/sdk 4.0.0-next.11 → 4.0.0-next.13
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/README.md +445 -164
- package/dist/index.cjs +4487 -2999
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2854 -2724
- package/dist/index.d.mts +2854 -2724
- package/dist/index.mjs +4466 -3002
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -12
package/README.md
CHANGED
|
@@ -37,7 +37,11 @@ Consumers use these accessors (lending still needs an integration):
|
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
39
|
const vault = await steer.getVault(ChainId.Base, vaultAddress);
|
|
40
|
-
const amm = steer.getAmm({
|
|
40
|
+
const amm = steer.getAmm({
|
|
41
|
+
chainId: ChainId.Base,
|
|
42
|
+
protocol: AmmProtocol.Uniswap,
|
|
43
|
+
version: UniswapVersion.V3,
|
|
44
|
+
});
|
|
41
45
|
const lending = steer.getLending(aaveV3Base);
|
|
42
46
|
|
|
43
47
|
steer.getSubgraphUrl(ChainId.Base);
|
|
@@ -50,18 +54,156 @@ Base V3/Integral and Polygon V3/Legacy/Integral vault reads are implemented belo
|
|
|
50
54
|
implementations. The Steer catalog supplies network metadata and indexed vault discovery. Networks without resolver bindings report `NotConfigured` when loading a vault. Consumers don't assemble a chain or pass a `steer: steerBase` setting.
|
|
51
55
|
SDK integration code owns Steer's per-network contracts and data adapters.
|
|
52
56
|
|
|
57
|
+
## Development tooling
|
|
58
|
+
|
|
59
|
+
Run these commands from `next/`:
|
|
60
|
+
|
|
61
|
+
- `npm run fix`: Biome formatting, import organization, and safe lint fixes.
|
|
62
|
+
- `npm run check:code`: non-mutating Biome CI checks.
|
|
63
|
+
- `npm run check`: code checks, generated ABI/reader verification, types, tests, build and package guards.
|
|
64
|
+
- `npm run check:consumer`: packed ESM, CJS, TypeScript and Next.js consumer verification after the build.
|
|
65
|
+
|
|
66
|
+
Biome is pinned to `2.6.6`, with recommended lint rules, standard formatting defaults and
|
|
67
|
+
two-space indentation. Open `next/` as the VS Code folder to use its formatter, save actions,
|
|
68
|
+
and Biome extension recommendation. The repository's legacy package has separate tooling.
|
|
69
|
+
|
|
70
|
+
Dependencies, build output, generated reader/ABI artifacts, retained benchmark JSON and
|
|
71
|
+
lockfiles are excluded from routine code formatting. The existing generators format their own
|
|
72
|
+
output, and reproducibility checks verify it. No unsafe fixes are included in `npm run fix`.
|
|
73
|
+
|
|
74
|
+
Tests allow non-null assertions for fixed fixtures and decoded tuples; making those accesses
|
|
75
|
+
optional can weaken test assertions. Production non-null exceptions are limited to accesses
|
|
76
|
+
whose bounds were already checked or whose source guarantees a nonempty list. Local bigint
|
|
77
|
+
min/max exceptions preserve arithmetic that cannot use JavaScript's number-only Math functions.
|
|
78
|
+
Each production exception includes its invariant beside the expression.
|
|
79
|
+
|
|
80
|
+
## Steer ABI sources
|
|
81
|
+
|
|
82
|
+
Complete Steer ABIs are generated using the development-only, exact dependency
|
|
83
|
+
`@steerprotocol/contracts@3.1.7` into `src/generated/vault/steer-abis.ts`. The generator selects explicit
|
|
84
|
+
chain/contract/function entries, preserves input/output types and names, and removes only Solidity
|
|
85
|
+
`internalType` metadata. Runtime consumers do not load the contracts package or its deployment JSON.
|
|
86
|
+
|
|
87
|
+
Run `npm run build:abis` to regenerate and `npm run check:abis` to verify. The normal
|
|
88
|
+
`npm run check` includes this verification. Updating the package version requires reviewing the
|
|
89
|
+
generator's source selections and all qualified ABI compatibility exceptions.
|
|
90
|
+
|
|
91
|
+
The npm package does not include the qualified standalone V4, Aerodrome staking, or Blackhole
|
|
92
|
+
farming implementations. Their missing methods are explicit compatibility entries in the ABI
|
|
93
|
+
generator, with references to each family's qualification evidence. Callers import complete ABIs.
|
|
94
|
+
Shared deposit encoding retains an explicit payable adjustment for qualified native V4 deposits; the package's older deposit ABI
|
|
95
|
+
is nonpayable. External AMM, gauge, NFT and wrapped-token interfaces remain independently maintained.
|
|
96
|
+
SDK reader ABIs continue to be generated from their Solidity reader sources.
|
|
97
|
+
|
|
98
|
+
ABI generation does not generate addresses, change chain support, or update deployment registrations.
|
|
99
|
+
The SDK catalog, contract maps, implementation identities, and manual deployment additions are
|
|
100
|
+
preserved independently of the package's deployment inventory.
|
|
101
|
+
|
|
53
102
|
## App and data-connector loader dependencies
|
|
54
103
|
|
|
55
104
|
The SDK installs exact runtime dependencies `@steerprotocol/app-loader@3.2.6` and
|
|
56
|
-
`@steerprotocol/data-connector-loader@
|
|
105
|
+
`@steerprotocol/data-connector-loader@5.0.0-next.7`. Their APIs remain separate from `SteerClient`;
|
|
57
106
|
they are not re-exported from the SDK root.
|
|
58
107
|
|
|
59
|
-
Both loader packages support ESM
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
108
|
+
Both loader packages support ESM and CommonJS. The connector loader owns the execution-context
|
|
109
|
+
types and no longer depends on the stable SDK. The SDK's portable root does not import either
|
|
110
|
+
loader at runtime.
|
|
111
|
+
|
|
112
|
+
## IPFS content and vault configuration
|
|
113
|
+
|
|
114
|
+
`IpfsClient` reads JSON and bytes through `https://ipfs.io/ipfs/` by default. Bare CIDs,
|
|
115
|
+
`ipfs://` references and gateway URLs with an `/ipfs/` path are resolved through the configured
|
|
116
|
+
gateways. Construction performs no requests. JSON is `unknown` until validated by the caller.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { IpfsClient, SteerClient } from "@steerprotocol/sdk";
|
|
120
|
+
|
|
121
|
+
const ipfs = new IpfsClient();
|
|
122
|
+
const steer = new SteerClient({ ipfs });
|
|
123
|
+
const vault = await steer.getLiquidityVault(chainId, vaultAddress);
|
|
124
|
+
const config = await vault.getConfig();
|
|
125
|
+
const json = await ipfs.getJson(cid);
|
|
126
|
+
const bytes = await ipfs.getBytes(wasmCid); // Uint8Array with an ArrayBuffer backing store.
|
|
127
|
+
|
|
128
|
+
if (config.strategy) {
|
|
129
|
+
const wasm = await ipfs.getBytes(config.strategy.execBundle);
|
|
130
|
+
const { chainId, tokenId } = config.strategy; // Strategy registry identity.
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`vault.getConfig()` fetches `vault.metadata.payloadIpfs` and validates the stored manifest.
|
|
135
|
+
`VaultConfig` preserves strategy-specific `strategyConfigData` and `vaultPayload` objects,
|
|
136
|
+
including empty objects and nested configuration. `dataConnectorsData` can be empty; each entry
|
|
137
|
+
has a `bundleHash` and its own `configData` object. The optional `strategy` object contains
|
|
138
|
+
`chainId`, `execBundle` (the strategy WASM IPFS reference) and decimal-string `tokenId` (the
|
|
139
|
+
strategy registry token ID). Older manifests without `strategy` remain readable. Validation
|
|
140
|
+
checks this envelope, leaving individual configuration schemas to their strategy or connector.
|
|
141
|
+
The method does not add the loader's runtime `vaultAddress` or `epochTimestamp`.
|
|
142
|
+
Missing references and invalid manifests throw. Indexed
|
|
143
|
+
metadata selects the reference; this read does not revalidate that reference onchain.
|
|
144
|
+
|
|
145
|
+
Configure fallback gateways in order with `new IpfsClient({ gateways: [...] })`. Each attempt
|
|
146
|
+
uses the existing fetch transport with a 15-second default timeout, optional headers and fetch
|
|
147
|
+
override, and caller cancellation. A caller abort stops fallback. `timeoutMs: 0` disables the
|
|
148
|
+
timeout. `maxResponseBytes` defaults to 16 MiB and is enforced while streaming the decoded body.
|
|
149
|
+
These settings can also be passed per call, including to `vault.getConfig()`. IPFS settings are
|
|
150
|
+
separate from the client's indexer `http` settings. Gateway responses are not cryptographically
|
|
151
|
+
verified against the CID. Uploading, pinning and executing downloaded bundles are separate tasks.
|
|
152
|
+
|
|
153
|
+
## Running vault strategies and data connectors
|
|
154
|
+
|
|
155
|
+
Liquidity vaults own their manifests. Callers pass one loader-native context through both
|
|
156
|
+
execution stages:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const config = await vault.getConfig(); // Optional inspection; warms the instance cache.
|
|
160
|
+
const context = await vault.getExecutionContext({
|
|
161
|
+
blockNumber,
|
|
162
|
+
epochTimestamp,
|
|
163
|
+
epochLength,
|
|
164
|
+
});
|
|
165
|
+
const connectorData = await vault.executeDataConnectors(context);
|
|
166
|
+
const output = await vault.executeStrategy(context, connectorData);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`getConfig()` caches the in-flight promise and resolved manifest per vault instance and payload
|
|
170
|
+
CID. Failed loads are removed so a later call can retry. Cached manifests are deeply frozen.
|
|
171
|
+
The first request's transport settings govern a shared download. Each execution stage verifies
|
|
172
|
+
the RPC chain and reads VaultRegistry and StrategyRegistry at `context.blockNumber`, selecting
|
|
173
|
+
the registered payload CID and execution bundle independently of indexed metadata. A new CID
|
|
174
|
+
gets a separate cache entry, including for historical reads. An embedded manifest `strategy`
|
|
175
|
+
object must agree with the registration.
|
|
176
|
+
|
|
177
|
+
The SDK validates and captures the supplied context and connector results before asynchronous
|
|
178
|
+
reads. It does not recompute caller-supplied financial fields; use `getExecutionContext()` for
|
|
179
|
+
verified same-block vault accounting. A stored `epochLength`, when present, must agree with the
|
|
180
|
+
context. Empty strategy settings use the caller's context epoch length.
|
|
181
|
+
|
|
182
|
+
`executeDataConnectors()` delegates v1 request loops and v2 execute/transform dispatch to the
|
|
183
|
+
data connector loader in manifest order, returning an
|
|
184
|
+
empty array when no connectors are configured. Each connector initializes with its own
|
|
185
|
+
`configData` and nested `executionContext`. Versionless connectors follow the loader's v1 path,
|
|
186
|
+
and chain reads use the SDK RPC client at the supplied block. `executeStrategy()` uses the
|
|
187
|
+
loader's `ExecutionApp.executeWithContext()` with the manifest, flattened
|
|
188
|
+
strategy settings and context fields taking precedence, then supplies one serialized argument
|
|
189
|
+
per connector. The result count must match the manifest. It returns decoded JSON as `unknown`
|
|
190
|
+
or the exact `"continue"` sentinel, without preparing or submitting a transaction. The loader owns
|
|
191
|
+
result decoding; other non-JSON output remains an error.
|
|
192
|
+
|
|
193
|
+
Supplied-context execution passes only the vault and chain identity plus optional host credentials.
|
|
194
|
+
It does not require pool-type placeholders or a discovery subgraph endpoint.
|
|
195
|
+
|
|
196
|
+
WASM loading is lazy and each call gets fresh module instances. Initialization, malformed JSON,
|
|
197
|
+
transport and execution failures reject the call. Manifests are never mutated to construct runtime
|
|
198
|
+
inputs. Request options apply to IPFS downloads; connector HTTP uses the loader's host runtime
|
|
199
|
+
and is not guaranteed to reflect the selected historical block. No hard CPU deadline or process
|
|
200
|
+
isolation is provided by these portable methods.
|
|
201
|
+
|
|
202
|
+
Offline regression tests exercise retained public Static Stable, Liquid Arc and Robinhood
|
|
203
|
+
connector binaries, including a complete connector-to-strategy run with a synthetic HTTP quote.
|
|
204
|
+
The sampled No-op registration's empty manifest is readable, but its binary rejects those empty
|
|
205
|
+
settings; execution reports that initialization failure rather than inventing defaults. This is
|
|
206
|
+
execution-path verification, not qualification of real-time data feeds or transaction outcomes.
|
|
65
207
|
|
|
66
208
|
## Vault execution context
|
|
67
209
|
|
|
@@ -69,18 +211,36 @@ Liquidity vaults expose strategy inputs through the existing vault object:
|
|
|
69
211
|
|
|
70
212
|
```ts
|
|
71
213
|
const blockNumber = 12345678n; // Caller-selected operation block.
|
|
72
|
-
const vault = await steer.getLiquidityVault(chainId, vaultAddress, {
|
|
214
|
+
const vault = await steer.getLiquidityVault(chainId, vaultAddress, {
|
|
215
|
+
blockNumber,
|
|
216
|
+
});
|
|
73
217
|
const context = await vault.getExecutionContext({
|
|
74
218
|
blockNumber,
|
|
75
219
|
epochTimestamp: 1793577600n, // Explicit Unix seconds, independent of the wall clock.
|
|
220
|
+
epochLength: 60, // Strategy epoch duration in seconds.
|
|
76
221
|
});
|
|
77
222
|
```
|
|
78
223
|
|
|
79
|
-
The
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
224
|
+
The return type is `SmartPoolExecutionContext`, imported directly from the installed npm
|
|
225
|
+
package. The SDK does not define or re-export its own execution-context output type:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import type {
|
|
229
|
+
ExecutionContext,
|
|
230
|
+
SmartPoolExecutionContext,
|
|
231
|
+
} from "@steerprotocol/data-connector-loader";
|
|
232
|
+
|
|
233
|
+
const smartPool: SmartPoolExecutionContext = context;
|
|
234
|
+
const execution: ExecutionContext = smartPool;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The result uses the loader's native field names: `chainId`, numeric block/epoch values,
|
|
238
|
+
`executionTimestamp` in milliseconds, `epochLength` in seconds, `blockTime: 0` for the
|
|
239
|
+
explicitly pinned path, positions as `{ startTick, endTick, weight }`, decimal-string token
|
|
240
|
+
totals, and numeric `lastExecutionTime` in seconds. It contains no `provenance` or extra
|
|
241
|
+
vault/pool identity fields beyond `chainId` and `vaultAddress`. Those identities are verified
|
|
242
|
+
internally before returning data.
|
|
243
|
+
Configured ranges are independent of active NFT inventory.
|
|
84
244
|
|
|
85
245
|
Each call revalidates the native vault binding and dispatches registered vault/AMM overrides.
|
|
86
246
|
All onchain reads use the explicit block. `totalAmount0`/`totalAmount1` come from the original
|
|
@@ -89,9 +249,9 @@ they are not aliases for the fee-adjusted `getSnapshot().total` accounting field
|
|
|
89
249
|
|
|
90
250
|
The Steer data adapter queries the latest indexed `vaultSnapshot` at that block, bounded by
|
|
91
251
|
the earlier of the block timestamp and execution epoch. Its timestamp is the history source,
|
|
92
|
-
not proof that the indexer has captured every execution. An empty history
|
|
93
|
-
|
|
94
|
-
malformed responses and RPC/HTTP failures throw
|
|
252
|
+
not proof that the indexer has captured every execution. An empty history maps to
|
|
253
|
+
`lastExecutionTime: 0`, matching the loader contract. Unsupported history adapters,
|
|
254
|
+
unavailable blocks, indexing errors, malformed responses and RPC/HTTP failures throw.
|
|
95
255
|
Block numbers must fit the indexer's nonnegative GraphQL Int range. The optional second
|
|
96
256
|
argument accepts the existing per-request HTTP overrides for the history query.
|
|
97
257
|
|
|
@@ -99,20 +259,21 @@ Custom Steer data adapters can implement `getLastVaultExecution(request, options
|
|
|
99
259
|
history without a subgraph. The request contains `vaultAddress`, `blockNumber` and the bounded
|
|
100
260
|
`timestamp`; return a nonnegative bigint no later than that timestamp, or `null`.
|
|
101
261
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
262
|
+
Pass this result to the loader's `initializeStrategyWithContext` or
|
|
263
|
+
`initializeDataConnectorWithContext` methods. `ExecutionApp.executeWithContext` also accepts
|
|
264
|
+
`SmartPoolExecutionContext` directly; `DataConnectorExecution.executeWithContext` accepts its
|
|
265
|
+
base `ExecutionContext`. No SDK-specific wrapper or mapping is required. Full WASM strategy
|
|
266
|
+
execution requires separate qualification.
|
|
107
267
|
|
|
108
|
-
|
|
268
|
+
The underlying read path was verified live on November 2, 2026 using 13 fixtures across six chains:
|
|
109
269
|
Base Uniswap V3, Base QuickSwap Integral, all three Base Aerodrome generations, Polygon
|
|
110
270
|
QuickSwap Legacy, Arbitrum Camelot, Avalanche Blackhole, BSC Pancake V3, and ERC20/native
|
|
111
|
-
standalone V4 vaults on both BSC and Robinhood.
|
|
271
|
+
standalone V4 vaults on both BSC and Robinhood. The prior wrapper was compared with independent
|
|
112
272
|
same-block original contract totals, configured ranges, registry/beacon identity, pool
|
|
113
|
-
tick/spacing and identity, and a separate indexed history query.
|
|
114
|
-
|
|
115
|
-
|
|
273
|
+
tick/spacing and identity, and a separate indexed history query. The updated native loader
|
|
274
|
+
shape also passed new live Base V3 and Robinhood native ETH checks, plus the offline suite.
|
|
275
|
+
These results qualify those fixtures and reads, not every deployment or full WASM strategy
|
|
276
|
+
execution. Blackhole execution pool reads retain the custom deployer from the verified vault layout.
|
|
116
277
|
|
|
117
278
|
Reproduce with the explicit RPC/indexer settings in `.env.integration`:
|
|
118
279
|
|
|
@@ -138,7 +299,10 @@ for (const metadata of page.items) {
|
|
|
138
299
|
}
|
|
139
300
|
|
|
140
301
|
// Load a page or any explicit address list through the same resolver.
|
|
141
|
-
const vaults = await steer.getVaults(
|
|
302
|
+
const vaults = await steer.getVaults(
|
|
303
|
+
ChainId.Base,
|
|
304
|
+
page.items.map((metadata) => metadata.id),
|
|
305
|
+
);
|
|
142
306
|
|
|
143
307
|
if (page.nextCursor) {
|
|
144
308
|
const next = await steer.listVaults({
|
|
@@ -215,10 +379,14 @@ its onchain identity checks remain the loading path.
|
|
|
215
379
|
When catalog records are already available, supply them through the optional `VaultLoadOptions.metadata` field:
|
|
216
380
|
|
|
217
381
|
```ts
|
|
218
|
-
const vaults = await steer.getVaults(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
382
|
+
const vaults = await steer.getVaults(
|
|
383
|
+
ChainId.Base,
|
|
384
|
+
records.map((record) => record.id),
|
|
385
|
+
{
|
|
386
|
+
blockNumber: 100000n,
|
|
387
|
+
metadata: records,
|
|
388
|
+
},
|
|
389
|
+
);
|
|
222
390
|
```
|
|
223
391
|
|
|
224
392
|
This skips metadata subgraph requests while retaining all onchain registration, implementation,
|
|
@@ -260,9 +428,10 @@ already checked by that reader and adds no RPC calls. Custom bindings without a
|
|
|
260
428
|
`implementationCodeHash` omit the metadata. `beaconName` reuses the indexed descriptive name from
|
|
261
429
|
the vault metadata, not onchain identity evidence. It does not assert fee-event completeness.
|
|
262
430
|
|
|
263
|
-
|
|
264
431
|
```ts
|
|
265
|
-
const supported: boolean = steer
|
|
432
|
+
const supported: boolean = steer
|
|
433
|
+
.getDeployment(chainId)
|
|
434
|
+
.supportsVaultSnapshots(beaconName);
|
|
266
435
|
```
|
|
267
436
|
|
|
268
437
|
This synchronous check performs no RPC or HTTP requests. It matches an exact indexed beacon name
|
|
@@ -292,7 +461,8 @@ const results = await steer.getVaultSnapshots(ChainId.Base, vaultAddresses, {
|
|
|
292
461
|
allowFailure: true,
|
|
293
462
|
});
|
|
294
463
|
for (const [index, result] of results.entries()) {
|
|
295
|
-
if (result.status === "success")
|
|
464
|
+
if (result.status === "success")
|
|
465
|
+
consume(vaultAddresses[index], result.snapshot);
|
|
296
466
|
else report(vaultAddresses[index], result.error);
|
|
297
467
|
}
|
|
298
468
|
```
|
|
@@ -348,7 +518,9 @@ const amm = steer.getAmm({
|
|
|
348
518
|
protocol: AmmProtocol.Uniswap,
|
|
349
519
|
version: UniswapVersion.V3,
|
|
350
520
|
});
|
|
351
|
-
const pools = await amm.getPools([poolAddress, anotherPoolAddress], {
|
|
521
|
+
const pools = await amm.getPools([poolAddress, anotherPoolAddress], {
|
|
522
|
+
blockNumber,
|
|
523
|
+
});
|
|
352
524
|
|
|
353
525
|
// Let the family discover each pool's registered AMM.
|
|
354
526
|
const discovered = await steer.getPools({
|
|
@@ -398,13 +570,22 @@ without a wrapper; install it in the application and use its numeric debug level
|
|
|
398
570
|
|
|
399
571
|
```ts
|
|
400
572
|
import { createConsola } from "consola";
|
|
401
|
-
import {
|
|
573
|
+
import {
|
|
574
|
+
AmmProtocol,
|
|
575
|
+
ChainId,
|
|
576
|
+
SteerClient,
|
|
577
|
+
UniswapVersion,
|
|
578
|
+
} from "@steerprotocol/sdk";
|
|
402
579
|
|
|
403
580
|
const steer = new SteerClient({
|
|
404
581
|
logger: createConsola({ level: 4 }),
|
|
405
582
|
indexers: { theGraph: { proxyUrl: "/api/the-graph" } },
|
|
406
583
|
});
|
|
407
|
-
const amm = steer.getAmm({
|
|
584
|
+
const amm = steer.getAmm({
|
|
585
|
+
chainId: ChainId.Base,
|
|
586
|
+
protocol: AmmProtocol.Uniswap,
|
|
587
|
+
version: UniswapVersion.V3,
|
|
588
|
+
});
|
|
408
589
|
|
|
409
590
|
await amm.listPools({ token: { symbol: "USDC" } });
|
|
410
591
|
```
|
|
@@ -477,7 +658,13 @@ new catalog as additional chains.
|
|
|
477
658
|
## Uniswap V3 on Base
|
|
478
659
|
|
|
479
660
|
```ts
|
|
480
|
-
import {
|
|
661
|
+
import {
|
|
662
|
+
AmmFamily,
|
|
663
|
+
ChainId,
|
|
664
|
+
SteerClient,
|
|
665
|
+
AmmProtocol,
|
|
666
|
+
UniswapVersion,
|
|
667
|
+
} from "@steerprotocol/sdk";
|
|
481
668
|
|
|
482
669
|
const steer = new SteerClient({ transports: { [ChainId.Base]: rpcTransport } });
|
|
483
670
|
const pool = await steer.getPool({
|
|
@@ -496,23 +683,32 @@ pool.token0Decimals;
|
|
|
496
683
|
pool.token1Decimals;
|
|
497
684
|
pool.price?.currency0InCurrency1; // Whole token1 per whole token0, as a decimal string.
|
|
498
685
|
pool.price?.currency1InCurrency0; // Whole token0 per whole token1.
|
|
499
|
-
pool.fees.swapFeePercent;
|
|
500
|
-
pool.balances.token0.raw;
|
|
686
|
+
pool.fees.swapFeePercent; // "0.05" for raw fee 500.
|
|
687
|
+
pool.balances.token0.raw; // bigint from token0.balanceOf(pool.address).
|
|
501
688
|
pool.balances.token0.formatted; // Exact decimal string using token0Decimals.
|
|
502
689
|
pool.balances.token1.raw;
|
|
503
690
|
pool.balances.token1.formatted;
|
|
504
691
|
|
|
505
|
-
const amm = steer.getAmm({
|
|
692
|
+
const amm = steer.getAmm({
|
|
693
|
+
chainId: ChainId.Base,
|
|
694
|
+
protocol: AmmProtocol.Uniswap,
|
|
695
|
+
version: UniswapVersion.V3,
|
|
696
|
+
});
|
|
506
697
|
amm.contracts.factory.address;
|
|
507
698
|
amm.subgraphUrl;
|
|
508
|
-
const state = await amm.getPool(pool.address, {
|
|
699
|
+
const state = await amm.getPool(pool.address, {
|
|
700
|
+
blockNumber: pool.blockNumber,
|
|
701
|
+
});
|
|
509
702
|
// Also exposes token0, token1, fees.swapFeePips, tickSpacing, factory, and sqrtPriceX96.
|
|
510
|
-
const quote = await amm.quote(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
703
|
+
const quote = await amm.quote(
|
|
704
|
+
{
|
|
705
|
+
pool: pool.address,
|
|
706
|
+
inputCurrency: state.token0,
|
|
707
|
+
outputCurrency: state.token1,
|
|
708
|
+
amountIn: 1_000_000_000_000_000n,
|
|
709
|
+
},
|
|
710
|
+
{ blockNumber: state.blockNumber },
|
|
711
|
+
);
|
|
516
712
|
```
|
|
517
713
|
|
|
518
714
|
`rpcTransport` implements `request(method, params): Promise<unknown>` and returns the JSON-RPC
|
|
@@ -560,13 +756,20 @@ indexed listing.
|
|
|
560
756
|
|
|
561
757
|
### List indexed pools
|
|
562
758
|
|
|
563
|
-
Configure
|
|
759
|
+
Configure Graph and Sentio keys once on the client in server code. They are reused by SDK indexer requests and vault data connectors. Connector execution keeps both keys in host-side loader settings, outside WASM configuration. The loader attaches Graph authorization only to recognized HTTPS gateway requests after resolving their actual destination, and scopes Sentio authentication to its HTTPS endpoints. It does not change the stored IPFS manifest. Graph proxy configuration continues to apply to SDK indexer requests, not WASM connector requests.
|
|
564
760
|
|
|
565
761
|
```ts
|
|
566
762
|
const steer = new SteerClient({
|
|
567
|
-
indexers: {
|
|
763
|
+
indexers: {
|
|
764
|
+
theGraph: { apiKey: theGraphApiKey },
|
|
765
|
+
sentio: { apiKey: sentioApiKey },
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
const amm = steer.getAmm({
|
|
769
|
+
chainId: ChainId.Base,
|
|
770
|
+
protocol: AmmProtocol.Uniswap,
|
|
771
|
+
version: UniswapVersion.V3,
|
|
568
772
|
});
|
|
569
|
-
const amm = steer.getAmm({ chainId: ChainId.Base, protocol: AmmProtocol.Uniswap, version: UniswapVersion.V3 });
|
|
570
773
|
const first = await amm.listPools({ limit: 100, token: { symbol: "USDC" } });
|
|
571
774
|
|
|
572
775
|
first.items; // Pool addresses; load one with amm.getPool(address) for verified RPC state.
|
|
@@ -588,7 +791,11 @@ For browser applications, configure a server proxy instead of putting a private
|
|
|
588
791
|
const steer = new SteerClient({
|
|
589
792
|
indexers: { theGraph: { proxyUrl: "/api/the-graph" } },
|
|
590
793
|
});
|
|
591
|
-
const amm = steer.getAmm({
|
|
794
|
+
const amm = steer.getAmm({
|
|
795
|
+
chainId: ChainId.Base,
|
|
796
|
+
protocol: AmmProtocol.Uniswap,
|
|
797
|
+
version: UniswapVersion.V3,
|
|
798
|
+
});
|
|
592
799
|
const pools = await amm.listPools({ token: { symbol: "USDC" } });
|
|
593
800
|
```
|
|
594
801
|
|
|
@@ -605,10 +812,13 @@ the canonical indexer URL; API keys and proxy settings never become public deplo
|
|
|
605
812
|
Per-request overrides remain available:
|
|
606
813
|
|
|
607
814
|
```ts
|
|
608
|
-
await amm.listPools(
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
815
|
+
await amm.listPools(
|
|
816
|
+
{ token: { symbol: "USDC" } },
|
|
817
|
+
{
|
|
818
|
+
headers: { Authorization: `Bearer ${otherApiKey}` },
|
|
819
|
+
timeoutMs: 5_000,
|
|
820
|
+
},
|
|
821
|
+
);
|
|
612
822
|
```
|
|
613
823
|
|
|
614
824
|
An explicit authorization header overrides the client key, using case-insensitive header matching.
|
|
@@ -657,11 +867,11 @@ Bearer authentication follows [The Graph API-key documentation](https://thegraph
|
|
|
657
867
|
|
|
658
868
|
Successful Uniswap V3 operations currently make these calls through the configured `RpcTransport`:
|
|
659
869
|
|
|
660
|
-
| Operation
|
|
661
|
-
|
|
|
662
|
-
| `amm.getPool()`
|
|
663
|
-
| `steer.getPool()` with discovery |
|
|
664
|
-
| `amm.quote()`
|
|
870
|
+
| Operation | Select latest block | Explicit block |
|
|
871
|
+
| -------------------------------- | ------------------: | -------------: |
|
|
872
|
+
| `amm.getPool()` | 3 | 2 |
|
|
873
|
+
| `steer.getPool()` with discovery | 4 | 3 |
|
|
874
|
+
| `amm.quote()` | 6 | 5 |
|
|
665
875
|
|
|
666
876
|
Discovery reads the factory, selects an exact registered adapter, and dispatches to that adapter's
|
|
667
877
|
`getPool()` method before any snapshot reads. Custom adapters can handle nonstandard metadata
|
|
@@ -710,9 +920,13 @@ batching remains a separate transport concern.
|
|
|
710
920
|
|
|
711
921
|
### Solidity reader development
|
|
712
922
|
|
|
923
|
+
Generated TypeScript lives under `src/generated/`, mirroring the AMM, vault and RPC paths.
|
|
924
|
+
This includes reader ABIs/bytecode, batch/tick limits and the package-sourced Steer ABIs.
|
|
925
|
+
The generators create their output directories; routine formatting excludes these files.
|
|
926
|
+
|
|
713
927
|
The sources are `src/amm/adapters/uniswap-v3/reader/V3PoolReader.sol` and `src/vault/adapters/uniswap-v3/reader/V3VaultReader.sol`; their internal ABIs and
|
|
714
|
-
creation/runtime bytecode are checked in under `src/amm/adapters/uniswap-v3/reader/artifact.ts` and
|
|
715
|
-
`src/vault/adapters/uniswap-v3/reader/artifact.ts`. `npm run build:reader` regenerates both artifacts with
|
|
928
|
+
creation/runtime bytecode are checked in under `src/generated/amm/adapters/uniswap-v3/reader/artifact.ts` and
|
|
929
|
+
`src/generated/vault/adapters/uniswap-v3/reader/artifact.ts`. `npm run build:reader` regenerates both artifacts with
|
|
716
930
|
the pinned development-only Solidity compiler, optimizer settings, and Paris EVM target.
|
|
717
931
|
`npm run check:reader`, included in `npm run check`, detects stale artifacts. Consumers receive
|
|
718
932
|
compiled JavaScript and bytecode and do not need Solidity tools.
|
|
@@ -786,6 +1000,12 @@ code identities; `rpc.ts` and `reader/` hold internal reads. Shared models and e
|
|
|
786
1000
|
`vault/types.ts` and `vault/enums.ts`. The Steer data-adapter scaffold lives in
|
|
787
1001
|
`vault/data-adapter.ts`; built-in indexed listing supports optional AMM/version and token filters.
|
|
788
1002
|
|
|
1003
|
+
Shared vault operations are grouped by responsibility: `vault/accounting/` contains snapshot
|
|
1004
|
+
validation, fee/reward accounting and user positions; `vault/transactions/` contains deposit
|
|
1005
|
+
quotes, deposit math, RPC reads and withdrawal preparation; `vault/resolution/` contains the
|
|
1006
|
+
resolver and its internal models; `vault/execution/` contains execution context and WASM orchestration.
|
|
1007
|
+
Family-specific operations stay with their adapters, and shared Solidity readers stay in `vault/reader/`.
|
|
1008
|
+
|
|
789
1009
|
## Automatic vault resolution
|
|
790
1010
|
|
|
791
1011
|
`getVault(chainId, address, { blockNumber })` uses the network's SDK-owned `vaultResolver` configuration. Consumers
|
|
@@ -825,7 +1045,10 @@ Use the same automatic resolver and snapshot API. Consumers do not select a vaul
|
|
|
825
1045
|
```ts
|
|
826
1046
|
const vault = await steer.getLiquidityVault(ChainId.Polygon, vaultAddress);
|
|
827
1047
|
const snapshot = await vault.getSnapshot();
|
|
828
|
-
const snapshots = await steer.getVaultSnapshots(
|
|
1048
|
+
const snapshots = await steer.getVaultSnapshots(
|
|
1049
|
+
ChainId.Polygon,
|
|
1050
|
+
vaultAddresses,
|
|
1051
|
+
);
|
|
829
1052
|
|
|
830
1053
|
const page = await steer.listVaults({
|
|
831
1054
|
chainId: ChainId.Polygon,
|
|
@@ -836,13 +1059,13 @@ const page = await steer.listVaults({
|
|
|
836
1059
|
|
|
837
1060
|
Polygon mappings cover these exact beacon deployments:
|
|
838
1061
|
|
|
839
|
-
| Indexed beacon
|
|
840
|
-
|
|
|
841
|
-
| `MultiPositionUniswapV3`
|
|
842
|
-
| `MultiPositionSushi`
|
|
843
|
-
| `MultiPositionRetro`
|
|
844
|
-
| `MultiPositionQuickSwap`
|
|
845
|
-
| `MultiPositionQuickSwapIntegral` | QuickSwap V4 / Algebra Integral | Existing Integral fee-manager reader
|
|
1062
|
+
| Indexed beacon | Underlying AMM | Accounting policy |
|
|
1063
|
+
| -------------------------------- | ------------------------------- | --------------------------------------------------------------- |
|
|
1064
|
+
| `MultiPositionUniswapV3` | Uniswap V3 | Vault `TOTAL_FEE` and accrued Steer/strategist reserves |
|
|
1065
|
+
| `MultiPositionSushi` | SushiSwap V3 | Same fixed-fee V3 layout |
|
|
1066
|
+
| `MultiPositionRetro` | Retro V3 | Same fixed-fee V3 layout |
|
|
1067
|
+
| `MultiPositionQuickSwap` | QuickSwap V3 / Legacy Algebra | Fixed fees; packed position key and six-field pool position ABI |
|
|
1068
|
+
| `MultiPositionQuickSwapIntegral` | QuickSwap V4 / Algebra Integral | Existing Integral fee-manager reader |
|
|
846
1069
|
|
|
847
1070
|
The SDK verifies actual registry membership, stored beacon/registry, exact implementation and original
|
|
848
1071
|
proxy/implementation runtime hashes, factory registration and token ordering. Indexed names only filter
|
|
@@ -892,32 +1115,36 @@ vault.amm.subgraphUrl;
|
|
|
892
1115
|
vault.steer.subgraphUrl;
|
|
893
1116
|
snapshot.blockNumber;
|
|
894
1117
|
snapshot.pool;
|
|
895
|
-
snapshot.idle.token0.formatted;
|
|
896
|
-
snapshot.balances.token0.raw;
|
|
897
|
-
snapshot.reservedFees.token0.raw;
|
|
898
|
-
snapshot.principal.token0.formatted;
|
|
899
|
-
snapshot.fees.gross.token0.formatted;
|
|
900
|
-
snapshot.fees.net.token0.formatted;
|
|
901
|
-
snapshot.fees.recorded.token0.raw;
|
|
1118
|
+
snapshot.idle.token0.formatted; // Available idle tokens after fee reserves.
|
|
1119
|
+
snapshot.balances.token0.raw; // Full vault ERC-20 holdings.
|
|
1120
|
+
snapshot.reservedFees.token0.raw; // Already collected protocol/strategist fees.
|
|
1121
|
+
snapshot.principal.token0.formatted; // Sum of deployed position principal.
|
|
1122
|
+
snapshot.fees.gross.token0.formatted; // All uncollected position fees.
|
|
1123
|
+
snapshot.fees.net.token0.formatted; // LP portion after the vault fee cut.
|
|
1124
|
+
snapshot.fees.recorded.token0.raw; // Sum of pool-checkpointed fees across positions.
|
|
902
1125
|
snapshot.feeRateBps;
|
|
903
|
-
snapshot.total.token0.formatted;
|
|
1126
|
+
snapshot.total.token0.formatted; // idle + principal + net fees.
|
|
904
1127
|
// Every amount pair also includes token1. Raw amounts are bigint; formatted amounts are strings.
|
|
905
1128
|
|
|
906
1129
|
for (const position of snapshot.positions) {
|
|
907
|
-
position.positionId;
|
|
1130
|
+
position.positionId; // keccak256(vault address, lowerTick, upperTick).
|
|
908
1131
|
position.lowerTick;
|
|
909
1132
|
position.upperTick;
|
|
910
|
-
position.relativeWeight;
|
|
911
|
-
position.weightPercent;
|
|
1133
|
+
position.relativeWeight; // Contract-configured units, not basis points.
|
|
1134
|
+
position.weightPercent; // Normalized percentage string, not a value allocation.
|
|
912
1135
|
position.liquidity;
|
|
913
1136
|
position.principal.token0.formatted;
|
|
914
|
-
position.fees.gross.token0.formatted;
|
|
1137
|
+
position.fees.gross.token0.formatted; // Gross collectible fees, including accrued growth.
|
|
915
1138
|
position.fees.net.token0.formatted;
|
|
916
|
-
position.fees.recorded.token0.raw;
|
|
1139
|
+
position.fees.recorded.token0.raw; // Pool checkpoint tokensOwed, before new fee growth.
|
|
917
1140
|
}
|
|
918
1141
|
|
|
919
|
-
const positions = await vault.getPositions({
|
|
920
|
-
|
|
1142
|
+
const positions = await vault.getPositions({
|
|
1143
|
+
blockNumber: snapshot.blockNumber,
|
|
1144
|
+
});
|
|
1145
|
+
const shares = await vault.getShareBalance(accountAddress, {
|
|
1146
|
+
blockNumber: snapshot.blockNumber,
|
|
1147
|
+
});
|
|
921
1148
|
```
|
|
922
1149
|
|
|
923
1150
|
Omit `blockNumber` to choose the current RPC block, or pin it on resolution and each read. Each
|
|
@@ -986,10 +1213,10 @@ own `getTotalAmounts()` after a sequential `poke()` simulation in `eth_call`. No
|
|
|
986
1213
|
const vault = await steer.getLiquidityVault(chainId, vaultAddress);
|
|
987
1214
|
const position = await vault.getUserPosition(accountAddress);
|
|
988
1215
|
|
|
989
|
-
position.shares;
|
|
990
|
-
position.totalSupply;
|
|
1216
|
+
position.shares; // Raw share balance (bigint)
|
|
1217
|
+
position.totalSupply; // Raw share supply (bigint)
|
|
991
1218
|
position.ownershipPercent; // Decimal string, e.g. "12.5"
|
|
992
|
-
position.amounts.token0;
|
|
1219
|
+
position.amounts.token0; // { raw: bigint, formatted: string }
|
|
993
1220
|
position.amounts.token1;
|
|
994
1221
|
position.blockNumber;
|
|
995
1222
|
```
|
|
@@ -1025,7 +1252,12 @@ const positions = await steer.getVaultUserPositions(
|
|
|
1025
1252
|
);
|
|
1026
1253
|
|
|
1027
1254
|
for (const { vault, position } of positions) {
|
|
1028
|
-
console.log(
|
|
1255
|
+
console.log(
|
|
1256
|
+
vault,
|
|
1257
|
+
position.shares,
|
|
1258
|
+
position.ownershipPercent,
|
|
1259
|
+
position.amounts,
|
|
1260
|
+
);
|
|
1029
1261
|
}
|
|
1030
1262
|
```
|
|
1031
1263
|
|
|
@@ -1097,19 +1329,19 @@ const request = {
|
|
|
1097
1329
|
};
|
|
1098
1330
|
const preview = await vault.previewDeposit(request, { blockNumber });
|
|
1099
1331
|
preview.blockNumber;
|
|
1100
|
-
preview.shares;
|
|
1332
|
+
preview.shares; // Expected raw share units
|
|
1101
1333
|
preview.token0;
|
|
1102
1334
|
preview.token1;
|
|
1103
|
-
preview.amounts.token0;
|
|
1335
|
+
preview.amounts.token0; // Accepted input: { raw: bigint, formatted: string }
|
|
1104
1336
|
preview.amounts.token1;
|
|
1105
|
-
preview.unusedAmounts;
|
|
1337
|
+
preview.unusedAmounts; // Requested inputs which remain in the wallet
|
|
1106
1338
|
|
|
1107
1339
|
const plan = await vault.prepareDeposit({ ...request, slippageBps: 50 });
|
|
1108
1340
|
plan.account;
|
|
1109
1341
|
plan.chainId;
|
|
1110
1342
|
plan.preview;
|
|
1111
1343
|
plan.minimumAmounts; // Minimum accepted amounts in token0/token1 order
|
|
1112
|
-
plan.transactions;
|
|
1344
|
+
plan.transactions; // Required capped approvals, then the unsigned deposit
|
|
1113
1345
|
```
|
|
1114
1346
|
|
|
1115
1347
|
Both methods accept `{ blockNumber }` as the second argument. Omitting it pins one latest block.
|
|
@@ -1126,9 +1358,9 @@ const quote = await vault.quoteDeposit({
|
|
|
1126
1358
|
amount: 100_000000n,
|
|
1127
1359
|
});
|
|
1128
1360
|
|
|
1129
|
-
quote.assets;
|
|
1130
|
-
quote.shares;
|
|
1131
|
-
quote.amounts;
|
|
1361
|
+
quote.assets; // Maxima for both tokens, already in token0/token1 order
|
|
1362
|
+
quote.shares; // Expected raw shares
|
|
1363
|
+
quote.amounts; // Expected accepted amounts, raw and formatted
|
|
1132
1364
|
quote.unusedAmounts; // Any unused input caused by contract rounding
|
|
1133
1365
|
quote.blockNumber;
|
|
1134
1366
|
|
|
@@ -1209,11 +1441,11 @@ preview.amounts.token0; // { raw: bigint, formatted: string }
|
|
|
1209
1441
|
preview.amounts.token1;
|
|
1210
1442
|
|
|
1211
1443
|
const plan = await vault.prepareWithdraw({ ...request, slippageBps: 50 });
|
|
1212
|
-
plan.account;
|
|
1444
|
+
plan.account; // Must submit the transaction: this account owns the shares
|
|
1213
1445
|
plan.chainId;
|
|
1214
1446
|
plan.preview;
|
|
1215
1447
|
plan.minimumAmounts; // Raw and formatted minimum outputs for both tokens
|
|
1216
|
-
plan.transactions;
|
|
1448
|
+
plan.transactions; // Unsigned { to, data, value } requests
|
|
1217
1449
|
```
|
|
1218
1450
|
|
|
1219
1451
|
Both methods accept `{ blockNumber }` as their optional second argument; omitting it pins one
|
|
@@ -1250,12 +1482,12 @@ const vault = await steer.getLiquidityVault(ChainId.Base, integralVaultAddress);
|
|
|
1250
1482
|
const snapshot = await vault.getSnapshot();
|
|
1251
1483
|
|
|
1252
1484
|
vault.amm.protocol; // AmmProtocol.QuickSwap
|
|
1253
|
-
vault.amm.version;
|
|
1485
|
+
vault.amm.version; // QuickSwapVersion.V4
|
|
1254
1486
|
vault.amm.subgraphUrl;
|
|
1255
1487
|
snapshot.positions;
|
|
1256
1488
|
snapshot.idle;
|
|
1257
1489
|
snapshot.principal;
|
|
1258
|
-
snapshot.fees;
|
|
1490
|
+
snapshot.fees; // gross, net, recorded, each with token0/token1 raw and formatted amounts.
|
|
1259
1491
|
snapshot.feeRateBps;
|
|
1260
1492
|
snapshot.total;
|
|
1261
1493
|
```
|
|
@@ -1296,16 +1528,16 @@ No Graph credentials are required for vault reads.
|
|
|
1296
1528
|
|
|
1297
1529
|
## Extension points
|
|
1298
1530
|
|
|
1299
|
-
| Base
|
|
1300
|
-
|
|
|
1301
|
-
| `UniswapV3Adapter`
|
|
1302
|
-
| `UniswapV4Adapter`
|
|
1303
|
-
| `AaveV3Adapter`
|
|
1304
|
-
| `PoolDataAdapter`, `LendingDataAdapter` | Indexed protocol discovery
|
|
1305
|
-
| `SteerClient`
|
|
1306
|
-
| `SteerNetwork`, `SteerDataAdapter`
|
|
1307
|
-
| `VaultResolver`, `VaultIdentityReader`
|
|
1308
|
-
| `SteerVault`
|
|
1531
|
+
| Base | Responsibility |
|
|
1532
|
+
| --------------------------------------- | -------------------------------------------------------------- |
|
|
1533
|
+
| `UniswapV3Adapter` | Address-based pool reads and V3 quote interface |
|
|
1534
|
+
| `UniswapV4Adapter` | Reusable V4 pool-key/ID reads and quote implementation |
|
|
1535
|
+
| `AaveV3Adapter` | Reserve reads and supply transaction preparation interface |
|
|
1536
|
+
| `PoolDataAdapter`, `LendingDataAdapter` | Indexed protocol discovery |
|
|
1537
|
+
| `SteerClient` | SDK entry point, network routing, and typed integration lookup |
|
|
1538
|
+
| `SteerNetwork`, `SteerDataAdapter` | Verified vault loading and indexed vault discovery |
|
|
1539
|
+
| `VaultResolver`, `VaultIdentityReader` | Exact implementation resolution and pinned identity reads |
|
|
1540
|
+
| `SteerVault` | Identity, shares, and strategy positions |
|
|
1309
1541
|
|
|
1310
1542
|
Vault categories describe strategy exposure, not an ERC-4626 or multi-asset ABI. Every vault has
|
|
1311
1543
|
consistent direct methods: `previewDeposit`, `prepareDeposit`, and `prepareWithdraw`. Deposits use
|
|
@@ -1418,7 +1650,8 @@ as V3. Standalone V4 vault operations are qualified separately on BNB Chain belo
|
|
|
1418
1650
|
```ts
|
|
1419
1651
|
const amm = steer.getAmm({
|
|
1420
1652
|
chainId: ChainId.Base,
|
|
1421
|
-
protocol: AmmProtocol.Uniswap,
|
|
1653
|
+
protocol: AmmProtocol.Uniswap,
|
|
1654
|
+
version: UniswapVersion.V4,
|
|
1422
1655
|
});
|
|
1423
1656
|
|
|
1424
1657
|
const page = await amm.listPools({ token: { symbol: "USDC" }, limit: 20 });
|
|
@@ -1427,10 +1660,10 @@ await amm.listPools({ token: { address: usdcAddress } });
|
|
|
1427
1660
|
await amm.listPools({ token: { name: "USD Coin" } });
|
|
1428
1661
|
|
|
1429
1662
|
for (const key of page.items) {
|
|
1430
|
-
const id = amm.getPoolId(key);
|
|
1663
|
+
const id = amm.getPoolId(key); // Pure ABI encoding and hashing; no network request.
|
|
1431
1664
|
const recovered = await amm.getPoolKey(id); // Verified indexed metadata lookup.
|
|
1432
1665
|
const pool = await amm.getPool(key); // Complete keys require RPC only.
|
|
1433
|
-
await amm.getPool({ poolId: id });
|
|
1666
|
+
await amm.getPool({ poolId: id }); // Resolves metadata, then verifies onchain state.
|
|
1434
1667
|
|
|
1435
1668
|
const quote = await amm.quote({
|
|
1436
1669
|
pool: recovered,
|
|
@@ -1533,7 +1766,11 @@ const options = { blockNumber }; // Omit to pin the latest block once for the op
|
|
|
1533
1766
|
const specific = await amm.getTicks(reference, [-120, 0, 120], options);
|
|
1534
1767
|
const range = { lowerTick: lowerVisibleTick, upperTick: upperVisibleTick };
|
|
1535
1768
|
const window = await amm.getSurroundingTicks(reference, range, options);
|
|
1536
|
-
const distribution = await amm.getLiquidityDistribution(
|
|
1769
|
+
const distribution = await amm.getLiquidityDistribution(
|
|
1770
|
+
reference,
|
|
1771
|
+
range,
|
|
1772
|
+
options,
|
|
1773
|
+
);
|
|
1537
1774
|
|
|
1538
1775
|
// Reuse an already fetched window without another RPC call.
|
|
1539
1776
|
const sameDistribution = buildLiquidityDistribution(window);
|
|
@@ -1583,7 +1820,8 @@ This is fee-free geometric liquidity depth, not an executable swap quote or hook
|
|
|
1583
1820
|
```ts
|
|
1584
1821
|
const amm = steer.getAmm({
|
|
1585
1822
|
chainId: ChainId.Base,
|
|
1586
|
-
protocol: AmmProtocol.QuickSwap,
|
|
1823
|
+
protocol: AmmProtocol.QuickSwap,
|
|
1824
|
+
version: QuickSwapVersion.V4,
|
|
1587
1825
|
});
|
|
1588
1826
|
|
|
1589
1827
|
const page = await amm.listPools({ token: { symbol: "USDC" } });
|
|
@@ -1597,10 +1835,14 @@ const quote = await amm.quote({
|
|
|
1597
1835
|
outputCurrency: pool.token1,
|
|
1598
1836
|
amountIn: 10n ** BigInt(pool.token0Decimals), // One whole token0.
|
|
1599
1837
|
});
|
|
1600
|
-
const ticks = await amm.getSurroundingTicks(
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1838
|
+
const ticks = await amm.getSurroundingTicks(
|
|
1839
|
+
reference,
|
|
1840
|
+
{
|
|
1841
|
+
lowerTick: pool.tick - 100,
|
|
1842
|
+
upperTick: pool.tick + 100,
|
|
1843
|
+
},
|
|
1844
|
+
{ blockNumber: pool.blockNumber },
|
|
1845
|
+
);
|
|
1604
1846
|
```
|
|
1605
1847
|
|
|
1606
1848
|
Ordinary pools accept an address. Custom pools use `{ address, deployer }`; listings return the
|
|
@@ -1635,11 +1877,13 @@ suite. This qualifies the deployed Integral interface, without inferring a versi
|
|
|
1635
1877
|
```ts
|
|
1636
1878
|
const quickswap = steer.getAmm({
|
|
1637
1879
|
chainId: ChainId.Polygon,
|
|
1638
|
-
protocol: AmmProtocol.QuickSwap,
|
|
1880
|
+
protocol: AmmProtocol.QuickSwap,
|
|
1881
|
+
version: QuickSwapVersion.V3,
|
|
1639
1882
|
});
|
|
1640
1883
|
const camelot = steer.getAmm({
|
|
1641
1884
|
chainId: ChainId.Arbitrum,
|
|
1642
|
-
protocol: AmmProtocol.Camelot,
|
|
1885
|
+
protocol: AmmProtocol.Camelot,
|
|
1886
|
+
version: CamelotVersion.V3,
|
|
1643
1887
|
});
|
|
1644
1888
|
const page = await camelot.listPools({ token: { symbol: "USDC" } });
|
|
1645
1889
|
const address = page.items[0];
|
|
@@ -1704,12 +1948,12 @@ Pool fee data lives under `pool.fees`. Directional fields consistently use
|
|
|
1704
1948
|
`currency0ToCurrency1` and `currency1ToCurrency0`, including pools with native currencies.
|
|
1705
1949
|
Rates retain explicit units and meanings:
|
|
1706
1950
|
|
|
1707
|
-
| Family
|
|
1708
|
-
|
|
|
1709
|
-
| Uniswap V3
|
|
1710
|
-
| Uniswap V4
|
|
1711
|
-
| Algebra Legacy
|
|
1712
|
-
| Algebra Integral | `fees.lastFeePips`, `fees.lastFeePercent`
|
|
1951
|
+
| Family | Swap/LP fee fields | Additional fields |
|
|
1952
|
+
| ---------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
1953
|
+
| Uniswap V3 | `fees.swapFeePips`, `fees.swapFeePercent` | Fixed swap fee, including any protocol share deducted from it |
|
|
1954
|
+
| Uniswap V4 | `fees.lpFeePips`, `fees.lpFeePercent` | Configured key fee, packed protocol fee and per-direction protocol fee rates |
|
|
1955
|
+
| Algebra Legacy | `fees.currency0ToCurrency1.lastFeePips/lastFeePercent` and the reverse direction | Token-specific community shares in permille and percentage |
|
|
1956
|
+
| Algebra Integral | `fees.lastFeePips`, `fees.lastFeePercent` | Community share in permille and percentage |
|
|
1713
1957
|
|
|
1714
1958
|
Stored dynamic fees are not promises of the next swap's execution fee. V4's LP fee and protocol
|
|
1715
1959
|
fees remain separate; Algebra community shares are cuts of collected swap fees. The new SDK uses
|
|
@@ -1731,22 +1975,30 @@ const steer = new SteerClient({
|
|
|
1731
1975
|
});
|
|
1732
1976
|
const pancake = steer.getAmm({
|
|
1733
1977
|
chainId: ChainId.BSC,
|
|
1734
|
-
protocol: AmmProtocol.PancakeSwap,
|
|
1978
|
+
protocol: AmmProtocol.PancakeSwap,
|
|
1979
|
+
version: PancakeSwapVersion.V3,
|
|
1735
1980
|
});
|
|
1736
1981
|
const infinity = steer.getAmm({
|
|
1737
1982
|
chainId: ChainId.BSC,
|
|
1738
|
-
protocol: AmmProtocol.PancakeSwap,
|
|
1983
|
+
protocol: AmmProtocol.PancakeSwap,
|
|
1984
|
+
version: PancakeSwapVersion.InfinityCL,
|
|
1739
1985
|
});
|
|
1740
1986
|
|
|
1741
1987
|
const pool = await pancake.getPool(poolAddress);
|
|
1742
|
-
const indexed = await infinity.listPools({
|
|
1988
|
+
const indexed = await infinity.listPools({
|
|
1989
|
+
token: { symbol: "USDT" },
|
|
1990
|
+
limit: 10,
|
|
1991
|
+
});
|
|
1743
1992
|
// Infinity listings return { poolId }; canonical keys resolve directly from the manager.
|
|
1744
1993
|
const reference = indexed.items[0];
|
|
1745
1994
|
if (!reference) throw new Error("No matching Infinity CL pools");
|
|
1746
1995
|
const infinityPool = await infinity.getPool(reference);
|
|
1747
1996
|
const key = await infinity.getPoolKey(infinityPool.poolId);
|
|
1748
1997
|
const id = infinity.getPoolId(key);
|
|
1749
|
-
const ticks = await infinity.getSurroundingTicks(key, {
|
|
1998
|
+
const ticks = await infinity.getSurroundingTicks(key, {
|
|
1999
|
+
lowerTick: -100,
|
|
2000
|
+
upperTick: 100,
|
|
2001
|
+
});
|
|
1750
2002
|
```
|
|
1751
2003
|
|
|
1752
2004
|
`getPoolKey` needs RPC, while
|
|
@@ -1762,12 +2014,12 @@ quotes accept optional `hookData` and reject input amounts outside positive `int
|
|
|
1762
2014
|
Snapshots expose decoded prices, raw fees and percentages, and the shared `dynamicFee` flag.
|
|
1763
2015
|
Infinity's Vault holds shared reserves; its snapshot does not expose those as pool balances.
|
|
1764
2016
|
|
|
1765
|
-
| Pinned read
|
|
1766
|
-
|
|
|
1767
|
-
| Snapshot
|
|
1768
|
-
| Specific ticks, one page
|
|
1769
|
-
| Nonempty initialized range, one page | 2 / 3 / 9
|
|
1770
|
-
| Exact-input quote
|
|
2017
|
+
| Pinned read | Pancake V3 | Infinity CL |
|
|
2018
|
+
| ------------------------------------ | ---------- | ----------- |
|
|
2019
|
+
| Snapshot | 2 / 3 / 7 | 2 / 3 / 5 |
|
|
2020
|
+
| Specific ticks, one page | 2 / 3 / 8 | 2 / 3 / 6 |
|
|
2021
|
+
| Nonempty initialized range, one page | 2 / 3 / 9 | 2 / 3 / 7 |
|
|
2022
|
+
| Exact-input quote | 5 | 4 |
|
|
1771
2023
|
|
|
1772
2024
|
Counts show deployless / override / Multicall compatibility, including rejected feature probes.
|
|
1773
2025
|
Latest-block reads add one RPC. Infinity ID and full-key references use the same budgets;
|
|
@@ -1813,7 +2065,10 @@ if (vault instanceof UniswapV4Vault) {
|
|
|
1813
2065
|
const holdings = await vault.getUserPosition(account);
|
|
1814
2066
|
const quote = await vault.quoteDeposit({ asset: snapshot.token0, amount });
|
|
1815
2067
|
const plan = await vault.prepareDeposit({
|
|
1816
|
-
account,
|
|
2068
|
+
account,
|
|
2069
|
+
receiver: account,
|
|
2070
|
+
assets: quote.assets,
|
|
2071
|
+
slippageBps: 50,
|
|
1817
2072
|
});
|
|
1818
2073
|
```
|
|
1819
2074
|
|
|
@@ -1856,10 +2111,19 @@ const amm = steer.getAmm({
|
|
|
1856
2111
|
version: AerodromeVersion.SlipstreamV3,
|
|
1857
2112
|
});
|
|
1858
2113
|
const pool = await amm.getPool(poolAddress);
|
|
1859
|
-
const ticks = await amm.getSurroundingTicks(poolAddress, {
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
2114
|
+
const ticks = await amm.getSurroundingTicks(poolAddress, {
|
|
2115
|
+
lowerTick,
|
|
2116
|
+
upperTick,
|
|
2117
|
+
});
|
|
2118
|
+
const quote = await amm.quote(
|
|
2119
|
+
{
|
|
2120
|
+
pool: poolAddress,
|
|
2121
|
+
inputCurrency: pool.token0,
|
|
2122
|
+
outputCurrency: pool.token1,
|
|
2123
|
+
amountIn,
|
|
2124
|
+
},
|
|
2125
|
+
{ account },
|
|
2126
|
+
); // Optional caller for fee-module discounts.
|
|
1863
2127
|
```
|
|
1864
2128
|
|
|
1865
2129
|
Use Slipstream, SlipstreamV2 or SlipstreamV3 explicitly; omitting version is ambiguous. All three
|
|
@@ -1913,7 +2177,10 @@ snapshot.rewards; // Separate reward asset, unclaimed rewards and unpaid Steer/s
|
|
|
1913
2177
|
|
|
1914
2178
|
const page = await steer.listVaults({
|
|
1915
2179
|
chainId: ChainId.Base,
|
|
1916
|
-
amm: {
|
|
2180
|
+
amm: {
|
|
2181
|
+
protocol: AmmProtocol.Aerodrome,
|
|
2182
|
+
version: AerodromeVersion.SlipstreamV2,
|
|
2183
|
+
},
|
|
1917
2184
|
token: { symbol: "USDC" },
|
|
1918
2185
|
});
|
|
1919
2186
|
```
|
|
@@ -1935,7 +2202,12 @@ const withdrawal = await vault.previewWithdraw({ account, shares, receiver });
|
|
|
1935
2202
|
withdrawal.amounts; // Raw/formatted token0 and token1 proceeds paid to receiver.
|
|
1936
2203
|
withdrawal.rewards; // Aerodrome: { token, recipient: account, amount: { raw, formatted } }.
|
|
1937
2204
|
|
|
1938
|
-
const plan = await vault.prepareWithdraw({
|
|
2205
|
+
const plan = await vault.prepareWithdraw({
|
|
2206
|
+
account,
|
|
2207
|
+
shares,
|
|
2208
|
+
receiver,
|
|
2209
|
+
slippageBps: 50,
|
|
2210
|
+
});
|
|
1939
2211
|
// Submit plan.transactions from plan.account on plan.chainId using your wallet integration.
|
|
1940
2212
|
```
|
|
1941
2213
|
|
|
@@ -1949,10 +2221,17 @@ quote, preview and unsigned preparation API is supported:
|
|
|
1949
2221
|
|
|
1950
2222
|
```ts
|
|
1951
2223
|
const quote = await vault.quoteDeposit({ asset: token0, amount });
|
|
1952
|
-
const deposit = await vault.previewDeposit({
|
|
2224
|
+
const deposit = await vault.previewDeposit({
|
|
2225
|
+
account,
|
|
2226
|
+
receiver,
|
|
2227
|
+
assets: quote.assets,
|
|
2228
|
+
});
|
|
1953
2229
|
deposit.rewards; // Aerodrome: existing receiver rewards, settled before new shares are minted.
|
|
1954
2230
|
const depositPlan = await vault.prepareDeposit({
|
|
1955
|
-
account,
|
|
2231
|
+
account,
|
|
2232
|
+
receiver,
|
|
2233
|
+
assets: quote.assets,
|
|
2234
|
+
slippageBps: 50,
|
|
1956
2235
|
});
|
|
1957
2236
|
```
|
|
1958
2237
|
|
|
@@ -1967,7 +2246,6 @@ Aerodrome's qualified helper accepts one raw initial share. One-sided vaults quo
|
|
|
1967
2246
|
|
|
1968
2247
|
See [vault qualification, accounting and budgets](src/vault/adapters/aerodrome/qualification.md).
|
|
1969
2248
|
|
|
1970
|
-
|
|
1971
2249
|
### Robinhood standalone Uniswap V4 snapshots
|
|
1972
2250
|
|
|
1973
2251
|
`MultiPositionUniswapV4NoOracle` uses the same standalone V4 snapshot reader as the qualified
|
|
@@ -1983,7 +2261,6 @@ Live tests cover BSC Reserve and Robinhood NoOracle holders for both ERC20 and n
|
|
|
1983
2261
|
including pagination and compatibility reads. Follow every `nextCursor`, even when `items`
|
|
1984
2262
|
is empty. Incomplete indexed token metadata still limits token-filtered discovery.
|
|
1985
2263
|
|
|
1986
|
-
|
|
1987
2264
|
### V4 pool-ID lookup without a pool indexer
|
|
1988
2265
|
|
|
1989
2266
|
`amm.getPool({ poolId })`, `amm.getPoolKey(poolId)` and client/bulk ID getters share the
|
|
@@ -1996,6 +2273,11 @@ is an error, not a fabricated key. Pool browsing remains separate and requires a
|
|
|
1996
2273
|
Base retains its existing indexed adapter by default. BNB combines indexed browsing with RPC key
|
|
1997
2274
|
resolution; Robinhood uses RPC key resolution without indexed pool browsing.
|
|
1998
2275
|
|
|
2276
|
+
### BSC SushiSwap and Uniswap V3 snapshots
|
|
2277
|
+
|
|
2278
|
+
The exact qualified `MultiPositionSushi` and `MultiPositionUniswapV3` implementations now support vault resolution, single/bulk snapshots and direct pool reads. They reuse the fixed-fee V3 reader with 15% aggregate fee rounding. Normal bulk snapshot pages use two pinned RPC requests and preserve per-member failures with `allowFailure: true`.
|
|
2279
|
+
|
|
2280
|
+
AMM-filtered Steer vault listing works. Underlying AMM pool browsing remains unqualified: both inherited subgraphs returned pool rows without the expected factory identity and the SDK rejects those responses. Deposit/withdrawal flows were not newly qualified by this snapshot work. Base Sushi is separately qualified after the 30-vault migration, using its exact upgraded FeeManager/helper implementation; see [Base Sushi qualification](src/vault/adapters/uniswap-v3/base-sushi-qualification.md). See [source and live qualification](src/vault/adapters/uniswap-v3/bsc-qualification.md).
|
|
1999
2281
|
|
|
2000
2282
|
### BSC PancakeSwap V3 vault snapshots
|
|
2001
2283
|
|
|
@@ -2015,7 +2297,6 @@ See [Pancake vault qualification](src/vault/adapters/uniswap-v3/pancake-qualific
|
|
|
2015
2297
|
identities, funded fixtures, retained-response regressions and live test coverage. Separate funded user-flow qualification now covers deposit quotes/preparation, partial/full
|
|
2016
2298
|
withdrawals, user positions and indexed wallet balance scanning.
|
|
2017
2299
|
|
|
2018
|
-
|
|
2019
2300
|
### Camelot legacy vault snapshots on Arbitrum
|
|
2020
2301
|
|
|
2021
2302
|
`MultiPositionCamelot` vaults on Arbitrum use the shared `AlgebraLegacyVault` implementation.
|
|
@@ -2034,7 +2315,6 @@ See [Camelot qualification](src/vault/adapters/algebra-legacy/camelot-qualificat
|
|
|
2034
2315
|
Funded simulations now also verify deposit quotes/preparation, partial/full withdrawals, user
|
|
2035
2316
|
positions and wallet discovery. Arbitrum deposit accounting uses the verified Multicall3 deployment.
|
|
2036
2317
|
|
|
2037
|
-
|
|
2038
2318
|
### Blackhole CL on Avalanche
|
|
2039
2319
|
|
|
2040
2320
|
Select pools with `{ protocol: AmmProtocol.Blackhole, version: BlackholeVersion.CL }`.
|
|
@@ -2053,7 +2333,6 @@ compatibility calls, without a two-RPC guarantee.
|
|
|
2053
2333
|
See [qualification](src/vault/adapters/blackhole/qualification.md) for exact identities and evidence.
|
|
2054
2334
|
Run `npm run test:integration:blackhole` with `AVALANCHE_RPC_URL` for the paced historical live suite.
|
|
2055
2335
|
|
|
2056
|
-
|
|
2057
2336
|
#### Blackhole AVAX deposit and exit
|
|
2058
2337
|
|
|
2059
2338
|
Use the zero address as the native AVAX input when the vault holds WAVAX:
|
|
@@ -2081,7 +2360,6 @@ This prepares a separate WAVAX withdrawal transaction that pays AVAX to `recipie
|
|
|
2081
2360
|
checking its balance. Use actual received amounts rather than the earlier withdrawal preview.
|
|
2082
2361
|
Both stages are separate wallet operations; preparation sends nothing.
|
|
2083
2362
|
|
|
2084
|
-
|
|
2085
2363
|
### Pancake V3 and Camelot user-flow verification
|
|
2086
2364
|
|
|
2087
2365
|
Run `npm run test:integration:vault-flows` with BSC and Arbitrum archival RPC URLs. This suite
|
|
@@ -2089,20 +2367,21 @@ repeats actual-contract deposits/withdrawals and balance-based discovery at reta
|
|
|
2089
2367
|
It uses pinned public indexer responses and paces live RPC calls by 1.5 seconds. No Graph API
|
|
2090
2368
|
credentials or Base RPC are needed for this isolated runner. Full historical state and temporary
|
|
2091
2369
|
EOA code overrides must be supported. Native BNB/ETH wrapping helpers remain separate work.
|
|
2370
|
+
|
|
2092
2371
|
### Bulk vault execution coverage
|
|
2093
2372
|
|
|
2094
2373
|
Accepting an array does not imply optimized execution. In `4.0.0-next.8`, BSC and Arbitrum
|
|
2095
2374
|
accepted both bulk methods but used individual-vault reads with concurrency four. This change
|
|
2096
2375
|
adds native bulk readers for their exact qualified deployments.
|
|
2097
2376
|
|
|
2098
|
-
| Chain / qualified vaults
|
|
2099
|
-
|
|
|
2100
|
-
| Base: V3, Integral, Aerodrome
|
|
2101
|
-
| Polygon: registered V3, Legacy, Integral | Yes
|
|
2102
|
-
| BSC: Pancake V3 and Reserve V4
|
|
2103
|
-
| Arbitrum: Camelot legacy
|
|
2104
|
-
| Robinhood: NoOracle V4
|
|
2105
|
-
| Avalanche: Blackhole
|
|
2377
|
+
| Chain / qualified vaults | Bulk API | Efficient execution in `4.0.0-next.8` | Efficient execution after this change |
|
|
2378
|
+
| ---------------------------------------- | -------- | ------------------------------------- | ------------------------------------- |
|
|
2379
|
+
| Base: V3, Integral, Aerodrome | Yes | Yes | Yes |
|
|
2380
|
+
| Polygon: registered V3, Legacy, Integral | Yes | Yes | Yes |
|
|
2381
|
+
| BSC: Pancake V3 and Reserve V4 | Yes | No | Yes |
|
|
2382
|
+
| Arbitrum: Camelot legacy | Yes | No | Yes |
|
|
2383
|
+
| Robinhood: NoOracle V4 | Yes | No | Bounded individual reads |
|
|
2384
|
+
| Avalanche: Blackhole | Yes | No | Bounded individual reads |
|
|
2106
2385
|
|
|
2107
2386
|
On providers supporting code-only overrides, optimized resolution uses two pinned RPC calls per
|
|
2108
2387
|
64-vault page. Snapshots use one original-code/position-count preflight per 16-vault page and one
|
|
@@ -2130,7 +2409,10 @@ for (const result of vaults) {
|
|
|
2130
2409
|
else console.log(result.error);
|
|
2131
2410
|
}
|
|
2132
2411
|
|
|
2133
|
-
const pools = await amm.getPools(references, {
|
|
2412
|
+
const pools = await amm.getPools(references, {
|
|
2413
|
+
blockNumber,
|
|
2414
|
+
allowFailure: true,
|
|
2415
|
+
});
|
|
2134
2416
|
for (const result of pools) {
|
|
2135
2417
|
if (result.status === "success") console.log(result.pool);
|
|
2136
2418
|
else console.log(result.error);
|
|
@@ -2145,7 +2427,6 @@ malformed shared RPC responses, and shared vault metadata request failures still
|
|
|
2145
2427
|
`SteerClient.getPools(request)` discovery remains strict; partial pool reads use the selected
|
|
2146
2428
|
AMM's `getPools(references, options)`.
|
|
2147
2429
|
|
|
2148
|
-
|
|
2149
2430
|
V4 partial ID reads isolate `V4PoolKeyNotFoundError` for a verified absent indexed key or
|
|
2150
2431
|
uninitialized onchain pool. Shared indexer, JSON/schema, RPC, and unexpected key-adapter errors
|
|
2151
2432
|
reject the request, including through registered adapter overrides. Custom V4 data adapters
|