@steerprotocol/sdk 4.0.0-next.6 → 4.0.0-next.8

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 CHANGED
@@ -90,7 +90,8 @@ interface VaultMetadata {
90
90
  readonly beaconName: string;
91
91
  readonly createdAt: string;
92
92
  readonly state: string | null;
93
- readonly pool: Address | null;
93
+ /** Contract address or 32-byte V4 pool ID. */
94
+ readonly pool: Hex | null;
94
95
  readonly poolKey?: string | null;
95
96
  readonly token0: Address | null;
96
97
  readonly token1: Address | null;
@@ -101,6 +102,8 @@ interface VaultMetadata {
101
102
 
102
103
  This replaces address-only pages. Use `item.id` when passing a listed result to a vault getter.
103
104
  `decimals` and `createdAt` retain their indexed string representation. Null fields remain null;
105
+ Unavailable indexed pool/token address fields represented by empty strings become `null`; the native zero address remains unchanged.
106
+ `pool` preserves either a contract address or a 32-byte V4 pool ID.
104
107
  `poolKey` is omitted when the subgraph schema does not expose it. Only an explicit rejection of
105
108
  that optional field permits querying without it; other GraphQL and transport errors propagate.
106
109
 
@@ -206,6 +209,26 @@ snapshots[0]?.total.token0.formatted;
206
209
  snapshots[0]?.fees.net.token1.raw;
207
210
  ```
208
211
 
212
+ Opt in to independent vault failures with `allowFailure: true`:
213
+
214
+ ```ts
215
+ const results = await steer.getVaultSnapshots(ChainId.Base, vaultAddresses, {
216
+ blockNumber,
217
+ allowFailure: true,
218
+ });
219
+ for (const [index, result] of results.entries()) {
220
+ if (result.status === "success") consume(vaultAddresses[index], result.snapshot);
221
+ else report(vaultAddresses[index], result.error);
222
+ }
223
+ ```
224
+
225
+ This mode preserves an outcome for every input, including duplicates. Preflight and snapshot
226
+ reverts, decoding errors and vault validation failures stay with that vault. Registered readers
227
+ and overrides still run their original identity checks. Shared RPC/HTTP failures, malformed batch
228
+ responses and invalid shared contexts still reject the call. Provider feature rejection alone
229
+ selects the existing compatibility path. Ordinary bounded override pages still use two RPCs;
230
+ there is no batch splitting to locate a failed vault. Calls without this option remain atomic.
231
+
209
232
  The deployment-level `steer.getDeployment(chainId).getVaultSnapshots(addresses, options)` is
210
233
  identical. Results use the same `LiquidityVaultSnapshot` model as `vault.getSnapshot()`, in input
211
234
  order, including duplicates. This method resolves identity and reads accounting; `getVaults()`
@@ -1346,9 +1369,11 @@ for (const key of page.items) {
1346
1369
 
1347
1370
  A pool key contains `currency0`, `currency1`, `fee`, `tickSpacing`, and `hooks`. Currencies must be
1348
1371
  sorted numerically. Key validation includes V4 fee flags and hook permission relationships.
1349
- Pool IDs are `keccak256(abi.encode(key))`; the hash cannot be reversed. ID-only reads require the
1350
- configured indexer to recover metadata and verify the complete key against its ID. Missing keys
1351
- fail clearly; supplying a complete key avoids that dependency. Per-request HTTP overrides are
1372
+ Pool IDs are `keccak256(abi.encode(key))`; the hash cannot be reversed. ID-only reads use the
1373
+ configured data adapter to recover metadata and verify the complete key against its ID. Indexed
1374
+ adapters query their configured endpoint; RPC adapters use the onchain key index or initialization
1375
+ event recovery with an archival provider. Missing evidence fails clearly; a complete key avoids
1376
+ that dependency. Per-request HTTP overrides are
1352
1377
  accepted by `getPoolKey`, ID-based `getPool`, `quote`, and `listPools`, including
1353
1378
  ID-based family discovery through `steer.getPool`.
1354
1379
 
@@ -1727,9 +1752,24 @@ This initial family implementation uses original contract reads and the existing
1727
1752
  reader with its deployless/override/Multicall fallback. A pinned resolved snapshot takes
1728
1753
  16 RPCs normally or 19 through compatibility; latest adds one. Bulk reads use bounded
1729
1754
  compatibility orchestration, not the optimized two-RPC V3/Algebra vault readers. BNB AMM
1730
- listing/ID lookup is explicitly unavailable without a qualified indexer; full-key reads work.
1731
- Steer indexed V4 beacon candidates are available, but token metadata is incomplete and
1732
- V4 token search is not qualified. Direct address-based reads are independently verified.
1755
+ pool browsing and token filtering use the configured Uniswap V4 subgraph with the client's
1756
+ Graph API key or proxy. ID-only reads retain the onchain key resolver independently of indexer
1757
+ availability; missing historical key-index entries need archival RPC. Indexed keys whose mutable
1758
+ fees no longer match their IDs require RPC initialization logs to recover the original key.
1759
+ Steer indexed V4 token metadata is populated, and ERC-20 address, symbol and name searches are
1760
+ live-verified for the registered Reserve vaults. Direct address-based reads remain independently verified.
1761
+
1762
+ ```ts
1763
+ const amm = steer.getAmm({
1764
+ chainId: ChainId.BSC,
1765
+ protocol: AmmProtocol.Uniswap,
1766
+ version: UniswapVersion.V4,
1767
+ });
1768
+ const pools = await amm.listPools({ token: { symbol: "USDC" }, limit: 25 });
1769
+ const nextPools = pools.nextCursor
1770
+ ? await amm.listPools({ cursor: pools.nextCursor, limit: 25 })
1771
+ : undefined;
1772
+ ```
1733
1773
  See [deployment qualification and budgets](src/vault/adapters/uniswap-v4/qualification.md).
1734
1774
 
1735
1775
  ### Aerodrome Slipstream on Base
@@ -1851,3 +1891,165 @@ standalone claim, signing or broadcasting occurs. Empty vaults require explicit
1851
1891
  Aerodrome's qualified helper accepts one raw initial share. One-sided vaults quote only the held asset.
1852
1892
 
1853
1893
  See [vault qualification, accounting and budgets](src/vault/adapters/aerodrome/qualification.md).
1894
+
1895
+
1896
+ ### Robinhood standalone Uniswap V4 snapshots
1897
+
1898
+ `MultiPositionUniswapV4NoOracle` uses the same standalone V4 snapshot reader as the qualified
1899
+ BNB Reserve deployment. The oracle difference belongs to tend validation, not snapshot accounting.
1900
+ Use `steer.getVaultSnapshots(ChainId.Robinhood, addresses, { blockNumber })` or a resolved vault's
1901
+ `getSnapshot()`. Exact proxy/beacon/implementation/helper identities, stored registry, pool routing
1902
+ and fees are checked at the selected block. Full pool keys are read from vault contracts; indexed
1903
+ pool listing remains unavailable; ID-only lookup uses the shared onchain key resolver. See the [deployment qualification](src/vault/adapters/uniswap-v4/qualification.md#robinhood-shared-snapshot-deployment).
1904
+
1905
+ V4 wallet discovery uses `listUserVaults()` with indexed candidates and onchain share balances,
1906
+ including shares acquired by transfer. Transfer-aware subgraph holdings are not required.
1907
+ Live tests cover BSC Reserve and Robinhood NoOracle holders for both ERC20 and native pairs,
1908
+ including pagination and compatibility reads. Follow every `nextCursor`, even when `items`
1909
+ is empty. Incomplete indexed token metadata still limits token-filtered discovery.
1910
+
1911
+
1912
+ ### V4 pool-ID lookup without a pool indexer
1913
+
1914
+ `amm.getPool({ poolId })`, `amm.getPoolKey(poolId)` and client/bulk ID getters share the
1915
+ family data adapter's key resolution. The RPC adapter is reusable on any configured canonical
1916
+ Uniswap V4 deployment. It verifies the configured PositionManager and the full key hash.
1917
+ Pools absent from that contract's key index are recovered from their original initialization
1918
+ event using historical RPC state. Recovery searches recent history first; older pools require
1919
+ an archival provider. Missing history
1920
+ is an error, not a fabricated key. Pool browsing remains separate and requires an indexed adapter.
1921
+ Base retains its existing indexed adapter by default. BNB combines indexed browsing with RPC key
1922
+ resolution; Robinhood uses RPC key resolution without indexed pool browsing.
1923
+
1924
+
1925
+ ### BSC PancakeSwap V3 vault snapshots
1926
+
1927
+ `MultiPositionPancake` vaults on BSC use the shared `UniswapV3Vault` implementation and
1928
+ existing fixed-fee reader. Resolve by address with `getLiquidityVault(ChainId.BSC, address)`
1929
+ or use `getVaultSnapshots`. Pool factory, registry, beacon, implementation and proxy runtime
1930
+ are checked at the operation block. Only the exact qualified deployment is supported.
1931
+
1932
+ This implementation reserves accrued Steer and strategist fees separately and takes a fixed
1933
+ 30% cut of earned fees. Its fee cut is rounded once over all ranges, matching original
1934
+ `poke()` followed by `getTotalAmounts()` in a single read-only simulation. Other V3 deployments
1935
+ keep their existing rounding policy. BSC bulk vault reads retain bounded compatibility
1936
+ orchestration; this addition does not introduce a two-RPC bulk optimization.
1937
+
1938
+ See [Pancake vault qualification](src/vault/adapters/uniswap-v3/pancake-qualification.md) for
1939
+ identities, funded fixtures, retained-response regressions and live test coverage. Separate funded user-flow qualification now covers deposit quotes/preparation, partial/full
1940
+ withdrawals, user positions and indexed wallet balance scanning.
1941
+
1942
+
1943
+ ### Camelot legacy vault snapshots on Arbitrum
1944
+
1945
+ `MultiPositionCamelot` vaults on Arbitrum use the shared `AlgebraLegacyVault` implementation.
1946
+ Resolve with `getLiquidityVault(ChainId.Arbitrum, address)` or call `getVaultSnapshots`.
1947
+ The exact beacon/implementation, proxy code, stored registry, pool factory/deployer and
1948
+ currencies are checked at the snapshot block. This covers the qualified legacy directional
1949
+ Algebra deployment; Camelot Integral and other beacon types are separate registrations.
1950
+
1951
+ A directional override reader preserves both pool swap fees and community shares while
1952
+ reusing existing position/fee accounting. Vault fees are fixed at 15% with aggregate rounding.
1953
+ Normal resolved snapshots take two RPC calls. Unsupported overrides use original-contract
1954
+ compatibility reads and the AMM's existing pool-reader fallbacks. Live checks cover funded
1955
+ three-range and five-range vaults. Bulk snapshots use bounded existing orchestration.
1956
+ See [Camelot qualification](src/vault/adapters/algebra-legacy/camelot-qualification.md).
1957
+ Funded simulations now also verify deposit quotes/preparation, partial/full withdrawals, user
1958
+ positions and wallet discovery. Arbitrum deposit accounting uses the verified Multicall3 deployment.
1959
+
1960
+
1961
+ ### Blackhole CL on Avalanche
1962
+
1963
+ Select pools with `{ protocol: AmmProtocol.Blackhole, version: BlackholeVersion.CL }`.
1964
+ Blackhole uses Integral-compatible pool reads with custom-deployer identity preserved.
1965
+ Pool and vault searches support token addresses, symbols, names and pagination.
1966
+
1967
+ The exact qualified `MultiPositionBlackHole` deployment resolves to `BlackholeVault` through
1968
+ `getLiquidityVault(ChainId.Avalanche, address)`. Snapshots include separate immutable `rewardAssets`
1969
+ and position-level `nftTokensOwed` diagnostics excluded from LP entitlement. Wallet discovery uses
1970
+ indexed vault candidates plus on-chain share balances. User positions and prepared ERC20 deposit
1971
+ and withdrawal flows have passed live simulations, including partial/full withdrawal rewards.
1972
+ AVAX inputs use an explicit WAVAX wrap before deposit. Native exits use a separate unwrap after
1973
+ withdrawal confirmation; standalone reward claims are not exposed. Vault reads use bounded
1974
+ compatibility calls, without a two-RPC guarantee.
1975
+
1976
+ See [qualification](src/vault/adapters/blackhole/qualification.md) for exact identities and evidence.
1977
+ Run `npm run test:integration:blackhole` with `AVALANCHE_RPC_URL` for the paced historical live suite.
1978
+
1979
+
1980
+ #### Blackhole AVAX deposit and exit
1981
+
1982
+ Use the zero address as the native AVAX input when the vault holds WAVAX:
1983
+
1984
+ ```ts
1985
+ const plan = await vault.prepareDeposit({
1986
+ account,
1987
+ receiver,
1988
+ assets: [
1989
+ { asset: zeroAddress, amount: avaxMaximum },
1990
+ { asset: otherToken, amount: otherMaximum },
1991
+ ],
1992
+ slippageBps: 50,
1993
+ });
1994
+ ```
1995
+
1996
+ The ordered unsigned transactions wrap AVAX, approve tokens if necessary, then deposit.
1997
+ Submit them in order from `account`. Only the wrap transaction carries AVAX. Unused input
1998
+ remains WAVAX, as shown in the preview's underlying-token `unusedAmounts`. Other assets are
1999
+ not swapped. Avoid providing both AVAX and WAVAX in the same request.
2000
+
2001
+ Withdraw normally with `prepareWithdraw`. After confirmation, the WAVAX recipient can call
2002
+ `prepareUnwrapNative({ account: recipient, amount: actualWavaxReceived })` on `BlackholeVault`.
2003
+ This prepares a separate WAVAX withdrawal transaction that pays AVAX to `recipient` after
2004
+ checking its balance. Use actual received amounts rather than the earlier withdrawal preview.
2005
+ Both stages are separate wallet operations; preparation sends nothing.
2006
+
2007
+
2008
+ ### Pancake V3 and Camelot user-flow verification
2009
+
2010
+ Run `npm run test:integration:vault-flows` with BSC and Arbitrum archival RPC URLs. This suite
2011
+ repeats actual-contract deposits/withdrawals and balance-based discovery at retained pinned blocks.
2012
+ It uses pinned public indexer responses and paces live RPC calls by 1.5 seconds. No Graph API
2013
+ credentials or Base RPC are needed for this isolated runner. Full historical state and temporary
2014
+ EOA code overrides must be supported. Native BNB/ETH wrapping helpers remain separate work.
2015
+ ### Partial vault loading and pool reads
2016
+
2017
+ Strict calls still reject the whole batch. Opt in to ordered, immutable per-input outcomes:
2018
+
2019
+ ```ts
2020
+ const vaults = await steer.getVaults(chainId, addresses, {
2021
+ blockNumber,
2022
+ metadata, // optional complete indexed VaultMetadata records
2023
+ allowFailure: true,
2024
+ });
2025
+ for (const result of vaults) {
2026
+ if (result.status === "success") console.log(result.vault.address);
2027
+ else console.log(result.error);
2028
+ }
2029
+
2030
+ const pools = await amm.getPools(references, { blockNumber, allowFailure: true });
2031
+ for (const result of pools) {
2032
+ if (result.status === "success") console.log(result.pool);
2033
+ else console.log(result.error);
2034
+ }
2035
+ ```
2036
+
2037
+ These methods preserve input order and duplicates. Unsupported implementations, absent V4 keys,
2038
+ and contract member failures do not discard successful members or trigger recursive splitting.
2039
+ Native readers retain their page sizes and RPC counts. Explicitly unsupported provider execution
2040
+ uses the existing bounded compatibility path. Shared RPC failures, invalid request context,
2041
+ malformed shared RPC responses, and shared vault metadata request failures still reject the call.
2042
+ `SteerClient.getPools(request)` discovery remains strict; partial pool reads use the selected
2043
+ AMM's `getPools(references, options)`.
2044
+
2045
+
2046
+ V4 partial ID reads isolate `V4PoolKeyNotFoundError` for a verified absent indexed key or
2047
+ uninitialized onchain pool. Shared indexer, JSON/schema, RPC, and unexpected key-adapter errors
2048
+ reject the request, including through registered adapter overrides. Custom V4 data adapters
2049
+ can throw `V4PoolKeyNotFoundError` to report an absent key explicitly.
2050
+
2051
+ Blackhole reward assets expose `unattributedReservedFees`: stored fee reserves not attributed
2052
+ to the current fee identifiers after a fee-policy replacement. These remain part of
2053
+ `reservedFees`, fully excluded from LP holdings. Current-recipient unpaid fees plus
2054
+ `unattributedReservedFees` reconcile to the stored reserve; current unclaimed fee cuts still
2055
+ reconcile exactly to the current policy.