@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 +23 -6
- package/dist/index.cjs +86 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +86 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
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
|
|
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
|
|
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
|
|
185
|
-
if (quantity(await transport.request("eth_chainId", [])) !== BigInt(context.chainId)) throw new
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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("
|
|
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
|
-
/**
|
|
23092
|
+
/** Onchain pool-key resolution with optional independent indexed listing. */
|
|
23049
23093
|
var UniswapV4RpcDataAdapter = class extends V4PoolDataAdapter {
|
|
23050
23094
|
context;
|
|
23051
23095
|
contracts;
|
|
23052
|
-
|
|
23053
|
-
|
|
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: {
|
|
23077
|
-
|
|
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,
|
|
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,
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
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,
|
|
24437
|
-
|
|
24438
|
-
|
|
24439
|
-
|
|
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,
|