@steerprotocol/sdk 4.0.0-next.7 → 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;
@@ -102,6 +103,7 @@ interface VaultMetadata {
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;
104
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.
105
107
  `poolKey` is omitted when the subgraph schema does not expose it. Only an explicit rejection of
106
108
  that optional field permits querying without it; other GraphQL and transport errors propagate.
107
109
 
@@ -1750,10 +1752,24 @@ This initial family implementation uses original contract reads and the existing
1750
1752
  reader with its deployless/override/Multicall fallback. A pinned resolved snapshot takes
1751
1753
  16 RPCs normally or 19 through compatibility; latest adds one. Bulk reads use bounded
1752
1754
  compatibility orchestration, not the optimized two-RPC V3/Algebra vault readers. BNB AMM
1753
- listing is unavailable without a qualified indexer. ID-only and full-key reads work through
1754
- the configured onchain data adapter; missing historical key-index entries need archival RPC.
1755
- Steer indexed V4 beacon candidates are available, but token metadata is incomplete and
1756
- 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
+ ```
1757
1773
  See [deployment qualification and budgets](src/vault/adapters/uniswap-v4/qualification.md).
1758
1774
 
1759
1775
  ### Aerodrome Slipstream on Base
@@ -1902,7 +1918,8 @@ Pools absent from that contract's key index are recovered from their original in
1902
1918
  event using historical RPC state. Recovery searches recent history first; older pools require
1903
1919
  an archival provider. Missing history
1904
1920
  is an error, not a fabricated key. Pool browsing remains separate and requires an indexed adapter.
1905
- Base retains its existing indexed adapter by default. BNB and Robinhood use RPC key resolution.
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.
1906
1923
 
1907
1924
 
1908
1925
  ### BSC PancakeSwap V3 vault snapshots
package/dist/index.cjs CHANGED
@@ -140,12 +140,20 @@ var Operation = class {
140
140
  };
141
141
  //#endregion
142
142
  //#region src/rpc/read-errors.ts
143
+ /** @internal Shared context or helper execution failed before a member outcome is available. */
144
+ var SharedRpcReadError = class extends Error {
145
+ constructor(message, options) {
146
+ super(message, options);
147
+ this.name = "SharedRpcReadError";
148
+ }
149
+ };
143
150
  /** Transport failures are shared by the request, never successful per-item outcomes. */
144
151
  function sharedReadError(error) {
145
152
  const seen = /* @__PURE__ */ new Set();
146
153
  let shared = false;
147
154
  for (let cause = error; cause && typeof cause === "object" && !seen.has(cause);) {
148
155
  seen.add(cause);
156
+ if (cause instanceof SharedRpcReadError) return true;
149
157
  if (cause instanceof viem.ContractFunctionRevertedError) return false;
150
158
  const value = cause;
151
159
  if (value.name === "ContractFunctionRevertedError") return false;
@@ -176,15 +184,15 @@ function sameAddress(left, right) {
176
184
  return left.toLowerCase() === right.toLowerCase();
177
185
  }
178
186
  function quantity(value) {
179
- if (typeof value !== "string" || !/^0x(?:0|[1-9a-f][\da-f]*)$/i.test(value)) throw new Error("Invalid RPC quantity");
187
+ if (typeof value !== "string" || !/^0x(?:0|[1-9a-f][\da-f]*)$/i.test(value)) throw new SharedRpcReadError("Invalid RPC quantity");
180
188
  return BigInt(value);
181
189
  }
182
190
  async function readContext(context, options) {
183
191
  const transport = context.transport;
184
- if (!transport) throw new Error(`No RPC transport configured for chain ${context.chainId}`);
185
- if (quantity(await transport.request("eth_chainId", [])) !== BigInt(context.chainId)) throw new Error("RPC chain does not match requested chain");
192
+ if (!transport) throw new SharedRpcReadError(`No RPC transport configured for chain ${context.chainId}`);
193
+ if (quantity(await transport.request("eth_chainId", [])) !== BigInt(context.chainId)) throw new SharedRpcReadError("RPC chain does not match requested chain");
186
194
  const blockNumber = options?.blockNumber ?? quantity(await transport.request("eth_blockNumber", []));
187
- if (blockNumber < 0n) throw new Error("Block number must be nonnegative");
195
+ if (blockNumber < 0n) throw new SharedRpcReadError("Block number must be nonnegative");
188
196
  return {
189
197
  ...context,
190
198
  transport,
@@ -778,9 +786,8 @@ function readerUnsupported(error, mode) {
778
786
  for (let cause = error; cause && typeof cause === "object" && !seen.has(cause);) {
779
787
  seen.add(cause);
780
788
  const value = cause;
781
- if (cause instanceof viem.ContractFunctionRevertedError || hasRevertData(value.data)) return false;
789
+ if (hasReaderRevert(cause)) return false;
782
790
  const message = typeof value.message === "string" ? value.message : "";
783
- if (/execution reverted|out of gas|invalid opcode/i.test(message)) return false;
784
791
  if (value.code === -32602 || value.code === -32004 || value.code === -32e3) {
785
792
  const feature = mode === "deployless" ? /contract creation|deployless|creation calls?/i : /state.?overrides?/i;
786
793
  const rejection = /not supported|unsupported|disabled/i;
@@ -797,11 +804,27 @@ function hasRevertData(data) {
797
804
  const wrapped = data;
798
805
  return [wrapped.data, wrapped.originalError?.data].some((value) => typeof value === "string" && /^0x[\da-f]+$/i.test(value));
799
806
  }
807
+ /** A failed member contract must not become a shared malformed-response error. */
808
+ function readerReverted(error) {
809
+ const seen = /* @__PURE__ */ new Set();
810
+ for (let cause = error; cause && typeof cause === "object" && !seen.has(cause);) {
811
+ seen.add(cause);
812
+ if (hasReaderRevert(cause)) return true;
813
+ cause = cause.cause;
814
+ }
815
+ return false;
816
+ }
817
+ function hasReaderRevert(error) {
818
+ const value = error;
819
+ if (value.name?.startsWith("AbiDecoding")) return false;
820
+ return error instanceof viem.ContractFunctionRevertedError || hasRevertData(value.data) || typeof value.message === "string" && /execution reverted|out of gas|invalid opcode/i.test(value.message);
821
+ }
800
822
  //#endregion
801
823
  //#region src/rpc/reader.ts
802
824
  /** Ephemeral code-only execution. Vault callers verify original proxy code before overriding it. */
803
- function executeReader(context, code, mode, read) {
804
- return read((0, viem.createClient)({ transport: (0, viem.custom)({ request: ({ method, params }) => context.transport.request(method, params ?? []) }, { retryCount: 0 }) }), mode === "deployless" ? {
825
+ async function executeReader(context, code, mode, read) {
826
+ const client = (0, viem.createClient)({ transport: (0, viem.custom)({ request: ({ method, params }) => context.transport.request(method, params ?? []) }, { retryCount: 0 }) });
827
+ const execution = mode === "deployless" ? {
805
828
  blockNumber: context.blockNumber,
806
829
  code: code.bytecode
807
830
  } : {
@@ -811,7 +834,13 @@ function executeReader(context, code, mode, read) {
811
834
  address: code.address,
812
835
  code: code.runtime
813
836
  }]
814
- });
837
+ };
838
+ try {
839
+ return await read(client, execution);
840
+ } catch (error) {
841
+ if (readerUnsupported(error, mode) || readerReverted(error) || sharedReadError(error)) throw error;
842
+ throw new SharedRpcReadError("Shared reader execution failed", { cause: error });
843
+ }
815
844
  }
816
845
  /** Compatibility is eligible only after both execution modes explicitly reject support. */
817
846
  async function readWithFallback(read) {
@@ -12239,6 +12268,11 @@ function nullableString(value) {
12239
12268
  if (value === null || typeof value === "string") return value;
12240
12269
  throw new Error("Malformed vault metadata string");
12241
12270
  }
12271
+ function poolIdentifier(value) {
12272
+ if (value === null) return null;
12273
+ if (typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value)) return value.toLowerCase();
12274
+ return indexedAddress(value, true);
12275
+ }
12242
12276
  /** Validate and detach indexed data, preserving nulls and optional pool keys. */
12243
12277
  function vaultMetadata(value) {
12244
12278
  const row = record(value);
@@ -12255,7 +12289,7 @@ function vaultMetadata(value) {
12255
12289
  beaconName: row.beaconName,
12256
12290
  createdAt: row.createdAt,
12257
12291
  state: nullableString(row.state),
12258
- pool: row.pool === null ? null : indexedAddress(row.pool, true),
12292
+ pool: poolIdentifier(row.pool),
12259
12293
  ...row.poolKey === void 0 ? {} : { poolKey: nullableString(row.poolKey) },
12260
12294
  token0: row.token0 === null ? null : indexedAddress(row.token0, true),
12261
12295
  token1: row.token1 === null ? null : indexedAddress(row.token1, true),
@@ -12287,6 +12321,15 @@ function captureVaultMetadata(addresses, values) {
12287
12321
  for (const address of requested) if (!records.has(address)) throw new Error(`Supplied vault metadata is missing address: ${address}`);
12288
12322
  return records;
12289
12323
  }
12324
+ /** @internal A valid indexed response can omit individual vaults. */
12325
+ var MissingVaultMetadataError = class extends Error {
12326
+ records;
12327
+ constructor(records) {
12328
+ super("Vault metadata is not indexed");
12329
+ this.name = "MissingVaultMetadataError";
12330
+ this.records = Object.freeze([...records]);
12331
+ }
12332
+ };
12290
12333
  //#endregion
12291
12334
  //#region src/vault/resolver-types.ts
12292
12335
  let ProtocolContractRole = /* @__PURE__ */ function(ProtocolContractRole) {
@@ -12819,9 +12862,10 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
12819
12862
  if (!unique.includes(metadata.id) || records.has(metadata.id)) throw new Error("Unexpected or duplicate vault metadata");
12820
12863
  records.set(metadata.id, metadata);
12821
12864
  }
12865
+ if (unique.some((id) => !records.has(id))) throw new MissingVaultMetadataError([...records.values()]);
12822
12866
  return Object.freeze(ids.map((id) => {
12823
12867
  const metadata = records.get(id);
12824
- if (!metadata) throw new Error("Vault metadata is not indexed");
12868
+ if (!metadata) throw new Error("Missing validated vault metadata");
12825
12869
  return metadata;
12826
12870
  }));
12827
12871
  }
@@ -23045,17 +23089,20 @@ async function readV4PoolKey(context, contracts, id, options) {
23045
23089
  }
23046
23090
  //#endregion
23047
23091
  //#region src/amm/adapters/uniswap-v4/rpc-data-adapter.ts
23048
- /** Pool-key resolution through onchain metadata, independent of indexed pool listing. */
23092
+ /** Onchain pool-key resolution with optional independent indexed listing. */
23049
23093
  var UniswapV4RpcDataAdapter = class extends V4PoolDataAdapter {
23050
23094
  context;
23051
23095
  contracts;
23052
- constructor(context, contracts) {
23053
- super({ kind: "rpc" });
23096
+ #listing;
23097
+ constructor(context, contracts, listing) {
23098
+ super(listing?.definition ?? { kind: "rpc" });
23054
23099
  this.context = context;
23055
23100
  this.contracts = contracts;
23101
+ this.#listing = listing;
23056
23102
  }
23057
- async listPools() {
23058
- throw new Error("Uniswap V4 pool listing is not configured on this network");
23103
+ async listPools(request, options) {
23104
+ if (!this.#listing) throw new Error("Uniswap V4 pool listing is not configured on this network");
23105
+ return this.#listing.listPools(request, options);
23059
23106
  }
23060
23107
  getPoolKey(id, options) {
23061
23108
  return readV4PoolKey(this.context, this.contracts, id, options);
@@ -23073,8 +23120,11 @@ const uniswapV4Bsc = /* @__PURE__ */ new Deployment({
23073
23120
  stateView: { address: "0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4" },
23074
23121
  quoter: { address: "0x9f75dd27d6664c475b90e105573e550ff69437b0" }
23075
23122
  },
23076
- dataAdapter: { kind: "rpc" }
23077
- }, (context, definition) => new UniswapV4(context, definition, new UniswapV4RpcDataAdapter(context, definition.contracts)));
23123
+ dataAdapter: {
23124
+ kind: "subgraph",
23125
+ url: "https://gateway.thegraph.com/api/subgraphs/id/EAq1nJKgjnuKH6Gj4RFjCW7LcL7E2uipbncdwV7TTWkX"
23126
+ }
23127
+ }, (context, definition) => new UniswapV4(context, definition, new UniswapV4RpcDataAdapter(context, definition.contracts, new UniswapV4DataAdapter(definition.dataAdapter, definition.contracts.poolManager.address, context))));
23078
23128
  //#endregion
23079
23129
  //#region src/amm/deployments/uniswap-v4-robinhood.ts
23080
23130
  /** Qualified Robinhood V4 deployment; full pool keys are available from vault contracts. */
@@ -24331,7 +24381,8 @@ var VaultResolver = class {
24331
24381
  }) } : {}
24332
24382
  });
24333
24383
  }
24334
- #metadata(steer, addresses, supplied, onRequestError) {
24384
+ #metadata(steer, addresses, options = {}) {
24385
+ const { supplied, allowMissing, onRequestError } = options;
24335
24386
  let pending;
24336
24387
  return async (address) => {
24337
24388
  if (supplied) {
@@ -24340,6 +24391,7 @@ var VaultResolver = class {
24340
24391
  return metadata;
24341
24392
  }
24342
24393
  pending ??= steer.dataAdapter.getVaultMetadata(addresses).catch((error) => {
24394
+ if (allowMissing && error instanceof MissingVaultMetadataError) return error.records;
24343
24395
  if (onRequestError) return onRequestError(error);
24344
24396
  throw error;
24345
24397
  });
@@ -24374,10 +24426,14 @@ var VaultResolver = class {
24374
24426
  let bulkSupported = !!this.#reader.readMany;
24375
24427
  for (let start = 0; start < inputs.length; start += 64) {
24376
24428
  const batch = inputs.slice(start, start + 64);
24377
- const loadMetadata = this.#metadata(steer, batch, metadata, (error) => {
24378
- metadataFailed = true;
24379
- metadataError = error;
24380
- throw error;
24429
+ const loadMetadata = this.#metadata(steer, batch, {
24430
+ ...metadata ? { supplied: metadata } : {},
24431
+ allowMissing: partial,
24432
+ onRequestError: (error) => {
24433
+ metadataFailed = true;
24434
+ metadataError = error;
24435
+ throw error;
24436
+ }
24381
24437
  });
24382
24438
  const contexts = batch.map((address) => Object.freeze({
24383
24439
  steer,
@@ -24433,10 +24489,13 @@ var VaultResolver = class {
24433
24489
  let bulkSupported = !!this.#reader.readSnapshots;
24434
24490
  for (let start = 0; start < inputs.length; start += 16) {
24435
24491
  const batch = inputs.slice(start, start + 16);
24436
- const loadMetadata = this.#metadata(steer, batch, void 0, (error) => {
24437
- metadataFailed = true;
24438
- metadataError = error;
24439
- throw error;
24492
+ const loadMetadata = this.#metadata(steer, batch, {
24493
+ allowMissing: partial,
24494
+ onRequestError: (error) => {
24495
+ metadataFailed = true;
24496
+ metadataError = error;
24497
+ throw error;
24498
+ }
24440
24499
  });
24441
24500
  const contexts = batch.map((address) => Object.freeze({
24442
24501
  steer,