@steerprotocol/sdk 4.0.0-next.2 → 4.0.0-next.4
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 +64 -7
- package/dist/index.cjs +151 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -17
- package/dist/index.d.mts +43 -17
- package/dist/index.mjs +151 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ SDK integration code owns Steer's per-network contracts and data adapters.
|
|
|
52
52
|
|
|
53
53
|
## Vault discovery
|
|
54
54
|
|
|
55
|
-
List indexed vault
|
|
55
|
+
List indexed vault metadata, then load a result through the identity-verified resolver:
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
58
|
const page = await steer.listVaults({
|
|
@@ -62,12 +62,13 @@ const page = await steer.listVaults({
|
|
|
62
62
|
limit: 20,
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
for (const
|
|
66
|
-
const vault = await steer.getVault(ChainId.Base,
|
|
65
|
+
for (const metadata of page.items) {
|
|
66
|
+
const vault = await steer.getVault(ChainId.Base, metadata.id);
|
|
67
|
+
vault.metadata.name; // Complete indexed metadata, separate from verified accounting.
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// Load a page or any explicit address list through the same resolver.
|
|
70
|
-
const vaults = await steer.getVaults(ChainId.Base, page.items);
|
|
71
|
+
const vaults = await steer.getVaults(ChainId.Base, page.items.map((metadata) => metadata.id));
|
|
71
72
|
|
|
72
73
|
if (page.nextCursor) {
|
|
73
74
|
const next = await steer.listVaults({
|
|
@@ -78,6 +79,41 @@ if (page.nextCursor) {
|
|
|
78
79
|
}
|
|
79
80
|
```
|
|
80
81
|
|
|
82
|
+
`listVaults()` returns `Page<VaultMetadata>` with immutable indexed records:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
interface VaultMetadata {
|
|
86
|
+
readonly id: Address;
|
|
87
|
+
readonly name: string | null;
|
|
88
|
+
readonly symbol: string | null;
|
|
89
|
+
readonly decimals: string | null;
|
|
90
|
+
readonly beaconName: string;
|
|
91
|
+
readonly createdAt: string;
|
|
92
|
+
readonly state: string | null;
|
|
93
|
+
readonly pool: Address | null;
|
|
94
|
+
readonly poolKey?: string | null;
|
|
95
|
+
readonly token0: Address | null;
|
|
96
|
+
readonly token1: Address | null;
|
|
97
|
+
readonly payloadIpfs: string | null;
|
|
98
|
+
readonly strategyToken: { readonly id: string } | null;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This replaces address-only pages. Use `item.id` when passing a listed result to a vault getter.
|
|
103
|
+
`decimals` and `createdAt` retain their indexed string representation. Null fields remain null;
|
|
104
|
+
`poolKey` is omitted when the subgraph schema does not expose it. Only an explicit rejection of
|
|
105
|
+
that optional field permits querying without it; other GraphQL and transport errors propagate.
|
|
106
|
+
|
|
107
|
+
Every loaded `SteerVault` exposes the same model as `vault.metadata`. Vault construction loads
|
|
108
|
+
indexed metadata once per resolution/snapshot batch, preserving input order and duplicates.
|
|
109
|
+
Loading a vault, including internal construction during bulk snapshots, now requires indexed
|
|
110
|
+
metadata in addition to RPC identity verification. Missing records and indexer failures propagate.
|
|
111
|
+
Existing resolved vault operations retain that metadata snapshot. It reflects the indexer's head
|
|
112
|
+
at construction, independently of an explicitly selected RPC block; state, pool and token hints
|
|
113
|
+
never replace the verified underlying references or accounting used by vault methods.
|
|
114
|
+
Custom Steer data adapters implement `getVaultMetadata(addresses, options)` as well as listing;
|
|
115
|
+
custom vault constructors pass `context.metadata` to the base constructor.
|
|
116
|
+
|
|
81
117
|
`amm` and `token` are optional. Token filters accept `address`, `symbol`, and `name`;
|
|
82
118
|
symbol and token name use case-insensitive substring matches. Combined criteria must match
|
|
83
119
|
the same token on either side. `name` refers to the underlying token, not the vault's name.
|
|
@@ -86,9 +122,10 @@ AMM selection uses the same typed protocol/version/deployment selectors and ambi
|
|
|
86
122
|
on Base, plus Polygon Uniswap V3, SushiSwap V3, Retro V3, QuickSwap V3 and QuickSwap V4.
|
|
87
123
|
Other AMM filters fail explicitly until their exact indexed beacon mappings are qualified.
|
|
88
124
|
|
|
89
|
-
|
|
90
|
-
then one per continuation.
|
|
91
|
-
|
|
125
|
+
Unfiltered listing costs zero RPC requests: two HTTP requests for the first page (snapshot and vaults),
|
|
126
|
+
then one per continuation. An older schema without poolKey adds one HTTP request for field negotiation.
|
|
127
|
+
AMM filters with factory qualification also perform RPC checks at the indexed block. Cursors retain filters and pin every page to the indexed block and
|
|
128
|
+
subgraph deployment. If supplied again, filters must agree with the cursor. Results contain indexed metadata for
|
|
92
129
|
candidates, which may include implementations unsupported by `getVault()`; listing does not
|
|
93
130
|
establish current onchain identity or add resolver support. Indexing, HTTP, and GraphQL errors propagate.
|
|
94
131
|
|
|
@@ -97,6 +134,23 @@ entry points honor client `indexers`/`http` configuration and accept per-request
|
|
|
97
134
|
timeout, and cancellation overrides as the second argument. The existing `getVault()` API and
|
|
98
135
|
its onchain identity checks remain the loading path.
|
|
99
136
|
|
|
137
|
+
When catalog records are already available, supply them through the optional `VaultLoadOptions.metadata` field:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const vaults = await steer.getVaults(ChainId.Base, records.map((record) => record.id), {
|
|
141
|
+
blockNumber: 100000n,
|
|
142
|
+
metadata: records,
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This skips metadata subgraph requests while retaining all onchain registration, implementation,
|
|
147
|
+
protocol and pool checks. Records are matched by address case-insensitively and copied before IO.
|
|
148
|
+
Supply one complete record per unique requested address. Missing, duplicate, unrequested or malformed
|
|
149
|
+
records fail without a metadata fetch. Results preserve requested order and duplicate addresses.
|
|
150
|
+
Omit `metadata` to retain the existing subgraph lookup. The same options work on `getVault`, typed
|
|
151
|
+
vault getters and the connected-network loading methods. Metadata remains descriptive and is never
|
|
152
|
+
forwarded to viem/RPC or used to establish verified identity. Snapshot/accounting options are unchanged.
|
|
153
|
+
|
|
100
154
|
`getVaults(chainId, addresses, options)` returns the same resolved vault objects as `getVault()`
|
|
101
155
|
in input order, including duplicate inputs. Base V3/Integral resolution uses a bulk Solidity reader:
|
|
102
156
|
**two RPC requests per page of at most 64 vaults** at a pinned block. Latest reads add one shared
|
|
@@ -500,6 +554,9 @@ shared internally; no snapshot is preloaded or passed through discovery. Each op
|
|
|
500
554
|
the RPC chain, with no cross-operation identity cache or additional consumer options.
|
|
501
555
|
|
|
502
556
|
Quotes read pool identity, the token pair and fee, verify factory registration, and call QuoterV2.
|
|
557
|
+
V3 and Aerodrome Slipstream quotes reject results at either default price boundary because
|
|
558
|
+
QuoterV2 does not establish the consumed input there. Requests that cannot establish full input
|
|
559
|
+
consumption fail explicitly.
|
|
503
560
|
They don't load token decimals, balances, ticks, or liquidity. Discovering a pool and then quoting
|
|
504
561
|
at its block uses 9 RPC calls in total. Counts exclude retries and provider-specific transport
|
|
505
562
|
behavior.
|
package/dist/index.cjs
CHANGED
|
@@ -2063,6 +2063,7 @@ var AerodromeSlipstreamAdapter = class extends AmmAdapter {
|
|
|
2063
2063
|
blockNumber: context.blockNumber,
|
|
2064
2064
|
...captured.account ? { account: captured.account } : {}
|
|
2065
2065
|
});
|
|
2066
|
+
if (result[1] <= sqrtRatioAtTick(-887272) + 1n || result[1] >= sqrtRatioAtTick(887272) - 1n) throw new Error("Slipstream quote reached the price limit; full input consumption is not established");
|
|
2066
2067
|
return Object.freeze({
|
|
2067
2068
|
amountIn: input.amountIn,
|
|
2068
2069
|
amountOut: result[0]
|
|
@@ -2103,6 +2104,9 @@ var SteerDataAdapter = class extends DataAdapter {
|
|
|
2103
2104
|
async listUserVaults(_request, _options) {
|
|
2104
2105
|
throw new Error("User vault discovery is not configured for this data adapter");
|
|
2105
2106
|
}
|
|
2107
|
+
async getVaultMetadata(_addresses, _options) {
|
|
2108
|
+
throw new Error("Vault metadata is not configured for this data adapter");
|
|
2109
|
+
}
|
|
2106
2110
|
};
|
|
2107
2111
|
//#endregion
|
|
2108
2112
|
//#region src/data/subgraph.ts
|
|
@@ -7743,6 +7747,7 @@ var RpcUniswapV3Adapter = class RpcUniswapV3Adapter extends UniswapV3Adapter {
|
|
|
7743
7747
|
fee: pool.fee,
|
|
7744
7748
|
sqrtPriceLimitX96: 0n
|
|
7745
7749
|
}]);
|
|
7750
|
+
if (result[1] <= sqrtRatioAtTick(-887272) + 1n || result[1] >= sqrtRatioAtTick(887272) - 1n) throw new Error("V3 quote reached the price limit; full input consumption is not established");
|
|
7746
7751
|
return {
|
|
7747
7752
|
amountIn: request.amountIn,
|
|
7748
7753
|
amountOut: result[0]
|
|
@@ -11399,6 +11404,50 @@ var DeploymentRegistry = class {
|
|
|
11399
11404
|
}
|
|
11400
11405
|
};
|
|
11401
11406
|
//#endregion
|
|
11407
|
+
//#region src/vault/metadata.ts
|
|
11408
|
+
function nullableString(value) {
|
|
11409
|
+
if (value === null || typeof value === "string") return value;
|
|
11410
|
+
throw new Error("Malformed vault metadata string");
|
|
11411
|
+
}
|
|
11412
|
+
/** Validate and detach indexed data, preserving nulls and optional pool keys. */
|
|
11413
|
+
function vaultMetadata(value) {
|
|
11414
|
+
const row = record(value);
|
|
11415
|
+
if (typeof row.beaconName !== "string" || !row.beaconName || typeof row.createdAt !== "string" || !/^\d+$/.test(row.createdAt)) throw new Error("Malformed vault metadata identity");
|
|
11416
|
+
const decimals = nullableString(row.decimals);
|
|
11417
|
+
if (decimals !== null && !/^\d+$/.test(decimals)) throw new Error("Malformed vault metadata decimals");
|
|
11418
|
+
const strategy = row.strategyToken === null ? null : record(row.strategyToken);
|
|
11419
|
+
if (strategy !== null && (typeof strategy.id !== "string" || !strategy.id)) throw new Error("Malformed vault strategy token");
|
|
11420
|
+
return Object.freeze({
|
|
11421
|
+
id: indexedAddress(row.id),
|
|
11422
|
+
name: nullableString(row.name),
|
|
11423
|
+
symbol: nullableString(row.symbol),
|
|
11424
|
+
decimals,
|
|
11425
|
+
beaconName: row.beaconName,
|
|
11426
|
+
createdAt: row.createdAt,
|
|
11427
|
+
state: nullableString(row.state),
|
|
11428
|
+
pool: row.pool === null ? null : indexedAddress(row.pool, true),
|
|
11429
|
+
...row.poolKey === void 0 ? {} : { poolKey: nullableString(row.poolKey) },
|
|
11430
|
+
token0: row.token0 === null ? null : indexedAddress(row.token0, true),
|
|
11431
|
+
token1: row.token1 === null ? null : indexedAddress(row.token1, true),
|
|
11432
|
+
payloadIpfs: nullableString(row.payloadIpfs),
|
|
11433
|
+
strategyToken: strategy === null ? null : Object.freeze({ id: String(strategy.id) })
|
|
11434
|
+
});
|
|
11435
|
+
}
|
|
11436
|
+
/** Validate and detach caller-owned metadata before any asynchronous resolution. */
|
|
11437
|
+
function captureVaultMetadata(addresses, values) {
|
|
11438
|
+
if (!Array.isArray(values)) throw new Error("Supplied vault metadata must be an array");
|
|
11439
|
+
const requested = new Set(addresses.map((address) => address.toLowerCase()));
|
|
11440
|
+
const records = /* @__PURE__ */ new Map();
|
|
11441
|
+
for (const value of values) {
|
|
11442
|
+
const metadata = vaultMetadata(value);
|
|
11443
|
+
if (!requested.has(metadata.id)) throw new Error(`Supplied vault metadata contains an unrequested address: ${metadata.id}`);
|
|
11444
|
+
if (records.has(metadata.id)) throw new Error(`Supplied vault metadata contains a duplicate address: ${metadata.id}`);
|
|
11445
|
+
records.set(metadata.id, metadata);
|
|
11446
|
+
}
|
|
11447
|
+
for (const address of requested) if (!records.has(address)) throw new Error(`Supplied vault metadata is missing address: ${address}`);
|
|
11448
|
+
return records;
|
|
11449
|
+
}
|
|
11450
|
+
//#endregion
|
|
11402
11451
|
//#region src/vault/resolver-types.ts
|
|
11403
11452
|
let ProtocolContractRole = /* @__PURE__ */ function(ProtocolContractRole) {
|
|
11404
11453
|
ProtocolContractRole["AmmFactory"] = "amm-factory";
|
|
@@ -11786,18 +11835,25 @@ async function filterVaultsByFactory(context, vaults, factory, blockNumber) {
|
|
|
11786
11835
|
//#endregion
|
|
11787
11836
|
//#region src/vault/data-adapter.ts
|
|
11788
11837
|
const nonShareBeacons = ["ScheduledJobs", "DynamicJobs"];
|
|
11789
|
-
const
|
|
11838
|
+
const metadataFields = `id name symbol decimals beaconName createdAt state pool poolKey token0 token1 payloadIpfs strategyToken { id }`;
|
|
11839
|
+
const metadataQuery = `query VaultMetadata($ids: [ID!]!, $first: Int!) {
|
|
11840
|
+
vaults(first: $first, orderBy: id, orderDirection: asc, where: {id_in: $ids}) { ${metadataFields} }
|
|
11841
|
+
_meta { ${metaFields} }
|
|
11842
|
+
}`;
|
|
11843
|
+
const vaultsQuery = (balances) => `query Vaults($first: Int!, $where: Vault_filter!, $block: Int!) {
|
|
11790
11844
|
vaults(first: $first, orderBy: id, orderDirection: asc, where: $where, block: {number: $block}) {
|
|
11791
|
-
id beaconName token0 token1
|
|
11845
|
+
${balances ? "id beaconName token0 token1" : metadataFields}
|
|
11846
|
+
token0Symbol token1Symbol token0Name token1Name
|
|
11792
11847
|
}
|
|
11793
11848
|
_meta(block: {number: $block}) { ${metaFields} }
|
|
11794
11849
|
}`;
|
|
11795
11850
|
function matchesToken(row, token, side) {
|
|
11796
|
-
|
|
11851
|
+
if (row[`token${side}`] === null) return false;
|
|
11852
|
+
const address = indexedAddress(row[`token${side}`], true);
|
|
11797
11853
|
const symbol = row[`token${side}Symbol`];
|
|
11798
11854
|
const name = row[`token${side}Name`];
|
|
11799
|
-
if (typeof symbol !== "string" || typeof name !== "string") throw new Error("Malformed vault token metadata");
|
|
11800
|
-
return (!token.address || token.address === address) && (!token.symbol || symbol.toLowerCase().includes(token.symbol)) && (!token.name || name.toLowerCase().includes(token.name));
|
|
11855
|
+
if (symbol !== null && typeof symbol !== "string" || name !== null && typeof name !== "string") throw new Error("Malformed vault token metadata");
|
|
11856
|
+
return (!token.address || token.address === address) && (!token.symbol || typeof symbol === "string" && symbol.toLowerCase().includes(token.symbol)) && (!token.name || typeof name === "string" && name.toLowerCase().includes(token.name));
|
|
11801
11857
|
}
|
|
11802
11858
|
function vaultFilter(token, after, beacons, balances) {
|
|
11803
11859
|
const base = {
|
|
@@ -11886,7 +11942,7 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11886
11942
|
...cursor === void 0 ? {} : { cursor },
|
|
11887
11943
|
limit: input.limit ?? 1e3
|
|
11888
11944
|
}, options, true);
|
|
11889
|
-
const items = await scanVaultBalances(this.#context, page.items, input.account, page.indexedBlock);
|
|
11945
|
+
const items = await scanVaultBalances(this.#context, page.items.map((row) => indexedAddress(row.id)), input.account, page.indexedBlock);
|
|
11890
11946
|
return Object.freeze({
|
|
11891
11947
|
items,
|
|
11892
11948
|
indexedBlock: page.indexedBlock,
|
|
@@ -11897,9 +11953,44 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11897
11953
|
}) } : {}
|
|
11898
11954
|
});
|
|
11899
11955
|
}
|
|
11956
|
+
async #metadataQuery(query, variables, options) {
|
|
11957
|
+
try {
|
|
11958
|
+
return await this.#query(query, variables, options);
|
|
11959
|
+
} catch (error) {
|
|
11960
|
+
const errors = error instanceof Error ? error.cause : void 0;
|
|
11961
|
+
if (!query.includes("poolKey") || !Array.isArray(errors) || !errors.length || !errors.every((value) => typeof value?.message === "string" && /^Cannot query field "poolKey" on type "Vault"\.(?: Did you mean .*\?)?$/.test(value.message))) throw error;
|
|
11962
|
+
return this.#query(query.replace(/\bpoolKey\b/g, ""), variables, options);
|
|
11963
|
+
}
|
|
11964
|
+
}
|
|
11965
|
+
async getVaultMetadata(addresses, options = {}) {
|
|
11966
|
+
const ids = addresses.map((address) => indexedAddress(address));
|
|
11967
|
+
const unique = [...new Set(ids)];
|
|
11968
|
+
if (!unique.length) return Object.freeze([]);
|
|
11969
|
+
if (unique.length > 1e3) throw new Error("Vault metadata lookup supports at most 1000 addresses");
|
|
11970
|
+
const data = await this.#metadataQuery(metadataQuery, {
|
|
11971
|
+
ids: unique,
|
|
11972
|
+
first: unique.length
|
|
11973
|
+
}, options);
|
|
11974
|
+
snapshot(data);
|
|
11975
|
+
if (!Array.isArray(data.vaults) || data.vaults.length > unique.length) throw new Error("Malformed vault metadata response");
|
|
11976
|
+
const records = /* @__PURE__ */ new Map();
|
|
11977
|
+
for (const value of data.vaults) {
|
|
11978
|
+
const metadata = vaultMetadata(value);
|
|
11979
|
+
if (!unique.includes(metadata.id) || records.has(metadata.id)) throw new Error("Unexpected or duplicate vault metadata");
|
|
11980
|
+
records.set(metadata.id, metadata);
|
|
11981
|
+
}
|
|
11982
|
+
return Object.freeze(ids.map((id) => {
|
|
11983
|
+
const metadata = records.get(id);
|
|
11984
|
+
if (!metadata) throw new Error("Vault metadata is not indexed");
|
|
11985
|
+
return metadata;
|
|
11986
|
+
}));
|
|
11987
|
+
}
|
|
11900
11988
|
async listVaults(request = {}, options = {}) {
|
|
11901
|
-
const { indexedBlock: _, ...page } = await this.#listVaults(request, options, false);
|
|
11902
|
-
return Object.freeze(
|
|
11989
|
+
const { indexedBlock: _, items, ...page } = await this.#listVaults(request, options, false);
|
|
11990
|
+
return Object.freeze({
|
|
11991
|
+
...page,
|
|
11992
|
+
items: Object.freeze(items.map(vaultMetadata))
|
|
11993
|
+
});
|
|
11903
11994
|
}
|
|
11904
11995
|
async #listVaults(request = {}, options = {}, balances = false) {
|
|
11905
11996
|
const limit = request.limit ?? 100;
|
|
@@ -11932,7 +12023,7 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11932
12023
|
} });
|
|
11933
12024
|
} else expected = snapshot(await this.#query(snapshotQuery, {}, options));
|
|
11934
12025
|
const beacons = amm ? this.#beacons.get(amm) : void 0;
|
|
11935
|
-
const data = await this.#
|
|
12026
|
+
const data = await this.#metadataQuery(vaultsQuery(balances), {
|
|
11936
12027
|
first: limit,
|
|
11937
12028
|
where: vaultFilter(token, after, beacons, balances),
|
|
11938
12029
|
block: expected.block
|
|
@@ -11949,13 +12040,15 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11949
12040
|
if (balances && (typeof row.beaconName !== "string" || nonShareBeacons.includes(row.beaconName))) throw new Error("User vault listing returned a non-share deployment");
|
|
11950
12041
|
if (beacons && (typeof row.beaconName !== "string" || !beacons.includes(row.beaconName))) throw new Error("Vault listing does not match the AMM beacon mapping");
|
|
11951
12042
|
if (token && !matchesToken(row, token, 0) && !matchesToken(row, token, 1)) throw new Error("Vault listing does not match the token filter");
|
|
11952
|
-
|
|
12043
|
+
if (!balances) vaultMetadata(row);
|
|
12044
|
+
return row;
|
|
11953
12045
|
});
|
|
11954
|
-
const last = items.at(-1);
|
|
12046
|
+
const last = items.at(-1)?.id;
|
|
11955
12047
|
const factory = amm ? this.#factories.get(amm) : void 0;
|
|
11956
|
-
const filtered = factory ? await filterVaultsByFactory(this.#context, items, factory, BigInt(expected.block)) : items;
|
|
12048
|
+
const filtered = factory ? await filterVaultsByFactory(this.#context, items.map((row) => indexedAddress(row.id)), factory, BigInt(expected.block)) : items.map((row) => indexedAddress(row.id));
|
|
12049
|
+
const included = new Set(filtered);
|
|
11957
12050
|
return Object.freeze({
|
|
11958
|
-
items: Object.freeze(
|
|
12051
|
+
items: Object.freeze(items.filter((row) => included.has(indexedAddress(row.id)))),
|
|
11959
12052
|
indexedBlock: BigInt(expected.block),
|
|
11960
12053
|
...items.length === limit && last ? { nextCursor: JSON.stringify({
|
|
11961
12054
|
version: 1,
|
|
@@ -13759,10 +13852,13 @@ var SteerVault = class {
|
|
|
13759
13852
|
chain;
|
|
13760
13853
|
steer;
|
|
13761
13854
|
underlying;
|
|
13762
|
-
|
|
13855
|
+
metadata;
|
|
13856
|
+
constructor(address, chain, steer, underlying, metadata) {
|
|
13763
13857
|
this.address = address;
|
|
13764
13858
|
this.chain = chain;
|
|
13765
13859
|
this.steer = steer;
|
|
13860
|
+
this.metadata = vaultMetadata(metadata);
|
|
13861
|
+
if (this.metadata.id !== address.toLowerCase()) throw new Error("Vault metadata belongs to another vault");
|
|
13766
13862
|
this.underlying = Object.freeze([...underlying]);
|
|
13767
13863
|
}
|
|
13768
13864
|
};
|
|
@@ -14424,7 +14520,7 @@ var AerodromeVault = class extends LiquidityVault {
|
|
|
14424
14520
|
#positionManager;
|
|
14425
14521
|
/** @internal */
|
|
14426
14522
|
constructor(context, identity) {
|
|
14427
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
14523
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
14428
14524
|
const underlying = context.underlying[0];
|
|
14429
14525
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !(underlying.amm instanceof AerodromeSlipstreamAdapter) || typeof underlying.pool !== "string") identityMismatch("Expected one Aerodrome vault pool");
|
|
14430
14526
|
requireAddress(underlying.pool);
|
|
@@ -14528,6 +14624,7 @@ async function readAerodromeBulkSnapshot(vault, data, rpc, beacon) {
|
|
|
14528
14624
|
steer: vault.steer,
|
|
14529
14625
|
address: vault.address,
|
|
14530
14626
|
underlying: vault.underlying,
|
|
14627
|
+
metadata: vault.metadata,
|
|
14531
14628
|
blockNumber: rpc.blockNumber
|
|
14532
14629
|
}, identity, {
|
|
14533
14630
|
raw: decodeAerodromeSnapshot(data),
|
|
@@ -15567,6 +15664,7 @@ async function readIntegralBulkSnapshot(vault, data, rpc) {
|
|
|
15567
15664
|
steer: vault.steer,
|
|
15568
15665
|
address: vault.address,
|
|
15569
15666
|
underlying: vault.underlying,
|
|
15667
|
+
metadata: vault.metadata,
|
|
15570
15668
|
blockNumber: rpc.blockNumber
|
|
15571
15669
|
}, {
|
|
15572
15670
|
raw,
|
|
@@ -15580,7 +15678,7 @@ var AlgebraIntegralVault = class extends LiquidityVault {
|
|
|
15580
15678
|
amm;
|
|
15581
15679
|
pool;
|
|
15582
15680
|
constructor(context) {
|
|
15583
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
15681
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
15584
15682
|
const underlying = context.underlying[0];
|
|
15585
15683
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isAlgebraIntegralAdapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one Integral vault pool");
|
|
15586
15684
|
requireAddress(underlying.pool);
|
|
@@ -17179,6 +17277,7 @@ async function readV3BulkSnapshot(vault, data, rpc) {
|
|
|
17179
17277
|
steer: vault.steer,
|
|
17180
17278
|
address: vault.address,
|
|
17181
17279
|
underlying: vault.underlying,
|
|
17280
|
+
metadata: vault.metadata,
|
|
17182
17281
|
blockNumber: rpc.blockNumber
|
|
17183
17282
|
}, {
|
|
17184
17283
|
raw,
|
|
@@ -17192,7 +17291,7 @@ var UniswapV3Vault = class extends LiquidityVault {
|
|
|
17192
17291
|
amm;
|
|
17193
17292
|
pool;
|
|
17194
17293
|
constructor(context) {
|
|
17195
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
17294
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
17196
17295
|
const underlying = context.underlying[0];
|
|
17197
17296
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isUniswapV3Adapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one V3 vault pool");
|
|
17198
17297
|
requireAddress(underlying.pool);
|
|
@@ -18825,7 +18924,7 @@ var UniswapV4Vault = class extends LiquidityVault {
|
|
|
18825
18924
|
amm;
|
|
18826
18925
|
pool;
|
|
18827
18926
|
constructor(context) {
|
|
18828
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
18927
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
18829
18928
|
const underlying = context.underlying[0];
|
|
18830
18929
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !(underlying.amm instanceof UniswapV4Adapter) || !isLiquidityPool(underlying.pool) || typeof underlying.pool === "string") identityMismatch("Expected one standalone V4 vault pool");
|
|
18831
18930
|
this.amm = underlying.amm;
|
|
@@ -20205,6 +20304,7 @@ async function readLegacyBulkSnapshot(vault, data, rpc, beacon) {
|
|
|
20205
20304
|
steer: vault.steer,
|
|
20206
20305
|
address: vault.address,
|
|
20207
20306
|
underlying: vault.underlying,
|
|
20307
|
+
metadata: vault.metadata,
|
|
20208
20308
|
blockNumber: rpc.blockNumber
|
|
20209
20309
|
}, {
|
|
20210
20310
|
raw,
|
|
@@ -20218,7 +20318,7 @@ var AlgebraLegacyVault = class extends LiquidityVault {
|
|
|
20218
20318
|
amm;
|
|
20219
20319
|
pool;
|
|
20220
20320
|
constructor(context) {
|
|
20221
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
20321
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
20222
20322
|
const underlying = context.underlying[0];
|
|
20223
20323
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isAlgebraLegacyAdapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one Legacy vault pool");
|
|
20224
20324
|
requireAddress(underlying.pool);
|
|
@@ -20825,7 +20925,7 @@ var VaultResolver = class {
|
|
|
20825
20925
|
if (!vault) throw new Error("Missing resolved vault");
|
|
20826
20926
|
return vault;
|
|
20827
20927
|
}
|
|
20828
|
-
async #createVault(context, identity, verified) {
|
|
20928
|
+
async #createVault(context, identity, loadMetadata, verified) {
|
|
20829
20929
|
const { steer, address } = context;
|
|
20830
20930
|
if (!identity) throw new VaultResolutionError("not-registered", "Vault is not registered with Steer");
|
|
20831
20931
|
if (identity.chainId !== steer.context.chainId || addressKey(identity.vaultAddress) !== addressKey(address) || addressKey(identity.registryAddress) !== addressKey(steer.contracts.VaultRegistry.address) || addressKey(identity.beaconAddress) !== addressKey(identity.proxyBeaconAddress)) throw new VaultResolutionError("identity-mismatch", "Vault identity does not match the request");
|
|
@@ -20837,9 +20937,12 @@ var VaultResolver = class {
|
|
|
20837
20937
|
lending: /* @__PURE__ */ new Map()
|
|
20838
20938
|
};
|
|
20839
20939
|
const resolvedUnderlying = Object.freeze(underlying.map((position) => this.#resolveUnderlying(position, adapters)));
|
|
20940
|
+
const metadata = await loadMetadata();
|
|
20941
|
+
if (metadata.id.toLowerCase() !== address.toLowerCase()) throw new Error("Vault metadata belongs to another vault");
|
|
20840
20942
|
const resolved = Object.freeze({
|
|
20841
20943
|
...context,
|
|
20842
|
-
underlying: resolvedUnderlying
|
|
20944
|
+
underlying: resolvedUnderlying,
|
|
20945
|
+
metadata
|
|
20843
20946
|
});
|
|
20844
20947
|
const vault = binding.createVault(resolved);
|
|
20845
20948
|
this.#validateVault(vault, binding, resolved);
|
|
@@ -20859,21 +20962,39 @@ var VaultResolver = class {
|
|
|
20859
20962
|
steer: vault.steer,
|
|
20860
20963
|
address: vault.address,
|
|
20861
20964
|
underlying: vault.underlying,
|
|
20965
|
+
metadata: vault.metadata,
|
|
20862
20966
|
blockNumber
|
|
20863
20967
|
});
|
|
20864
20968
|
if (snapshot.blockNumber !== blockNumber || !vault.underlying.some((position) => position.kind === "liquidity" && sameLiquidityPool(position.pool, snapshot.pool))) throw new Error("Vault returned another accounting snapshot");
|
|
20865
20969
|
return readValidatedVaultSnapshot(vault, snapshot, vault.steer.context);
|
|
20866
20970
|
}
|
|
20971
|
+
#metadata(steer, addresses, supplied) {
|
|
20972
|
+
let pending;
|
|
20973
|
+
return async (address) => {
|
|
20974
|
+
if (supplied) {
|
|
20975
|
+
const metadata = supplied.get(address.toLowerCase());
|
|
20976
|
+
if (!metadata) throw new Error(`Supplied vault metadata is missing address: ${address}`);
|
|
20977
|
+
return metadata;
|
|
20978
|
+
}
|
|
20979
|
+
pending ??= steer.dataAdapter.getVaultMetadata(addresses);
|
|
20980
|
+
const metadata = (await pending).find((record) => record.id.toLowerCase() === address.toLowerCase());
|
|
20981
|
+
if (!metadata) throw new Error("Vault metadata is not indexed");
|
|
20982
|
+
return metadata;
|
|
20983
|
+
};
|
|
20984
|
+
}
|
|
20867
20985
|
/** Bounded bulk identity pages at one block; compatibility uses four active loads. */
|
|
20868
20986
|
async resolveMany(steer, addresses, options) {
|
|
20869
20987
|
const inputs = [...addresses];
|
|
20870
20988
|
for (const address of inputs) addressKey(address);
|
|
20989
|
+
const supplied = options?.metadata;
|
|
20990
|
+
const metadata = supplied === void 0 ? void 0 : captureVaultMetadata(inputs, supplied);
|
|
20871
20991
|
if (!inputs.length) return Object.freeze([]);
|
|
20872
20992
|
const blockNumber = await this.#blockNumber(steer, options);
|
|
20873
20993
|
const result = [];
|
|
20874
20994
|
let bulkSupported = !!this.#reader.readMany;
|
|
20875
20995
|
for (let start = 0; start < inputs.length; start += 64) {
|
|
20876
20996
|
const batch = inputs.slice(start, start + 64);
|
|
20997
|
+
const loadMetadata = this.#metadata(steer, batch, metadata);
|
|
20877
20998
|
const contexts = batch.map((address) => Object.freeze({
|
|
20878
20999
|
steer,
|
|
20879
21000
|
address,
|
|
@@ -20885,13 +21006,13 @@ var VaultResolver = class {
|
|
|
20885
21006
|
for (const [i, context] of contexts.entries()) {
|
|
20886
21007
|
const read = reads[i];
|
|
20887
21008
|
if (!read) throw new VaultResolutionError("identity-mismatch", "Missing bulk vault identity");
|
|
20888
|
-
result.push((await this.#createVault(context, read.identity, read)).vault);
|
|
21009
|
+
result.push((await this.#createVault(context, read.identity, () => loadMetadata(context.address), read)).vault);
|
|
20889
21010
|
}
|
|
20890
21011
|
} else {
|
|
20891
21012
|
bulkSupported = false;
|
|
20892
21013
|
for (let i = 0; i < batch.length; i += 4) result.push(...await Promise.all(contexts.slice(i, i + 4).map(async (context) => {
|
|
20893
21014
|
const identity = await this.#reader.readIdentity(context);
|
|
20894
|
-
return (await this.#createVault(context, identity)).vault;
|
|
21015
|
+
return (await this.#createVault(context, identity, () => loadMetadata(context.address))).vault;
|
|
20895
21016
|
})));
|
|
20896
21017
|
}
|
|
20897
21018
|
}
|
|
@@ -20907,6 +21028,7 @@ var VaultResolver = class {
|
|
|
20907
21028
|
let bulkSupported = !!this.#reader.readSnapshots;
|
|
20908
21029
|
for (let start = 0; start < inputs.length; start += 16) {
|
|
20909
21030
|
const batch = inputs.slice(start, start + 16);
|
|
21031
|
+
const loadMetadata = this.#metadata(steer, batch);
|
|
20910
21032
|
const contexts = batch.map((address) => Object.freeze({
|
|
20911
21033
|
steer,
|
|
20912
21034
|
address,
|
|
@@ -20918,7 +21040,7 @@ var VaultResolver = class {
|
|
|
20918
21040
|
for (const [i, context] of contexts.entries()) {
|
|
20919
21041
|
const read = reads[i];
|
|
20920
21042
|
if (!read) throw new Error("Missing bulk vault snapshot");
|
|
20921
|
-
const { vault } = await this.#createVault(context, read.identity, read);
|
|
21043
|
+
const { vault } = await this.#createVault(context, read.identity, () => loadMetadata(context.address), read);
|
|
20922
21044
|
if (vault.kind !== "liquidity") throw new VaultResolutionError("invalid-vault", "Vault does not support liquidity snapshots");
|
|
20923
21045
|
result.push(await read.readSnapshot(vault));
|
|
20924
21046
|
}
|
|
@@ -20931,7 +21053,7 @@ var VaultResolver = class {
|
|
|
20931
21053
|
blockNumber
|
|
20932
21054
|
});
|
|
20933
21055
|
const identity = await this.#reader.readIdentity(context);
|
|
20934
|
-
const { vault, binding, context: resolved } = await this.#createVault(context, identity);
|
|
21056
|
+
const { vault, binding, context: resolved } = await this.#createVault(context, identity, () => loadMetadata(context.address));
|
|
20935
21057
|
if (vault.kind !== "liquidity") throw new VaultResolutionError("invalid-vault", "Vault does not support liquidity snapshots");
|
|
20936
21058
|
if (!binding.readSnapshot) throw new VaultResolutionError("not-configured", "No verified snapshot reader configured for vault implementation");
|
|
20937
21059
|
const snapshot = await binding.readSnapshot(resolved);
|
|
@@ -21029,7 +21151,10 @@ var SteerNetwork = class {
|
|
|
21029
21151
|
return this.#vaultResolver().resolve(this, address, options);
|
|
21030
21152
|
}
|
|
21031
21153
|
async getVaults(addresses, options) {
|
|
21032
|
-
if (!addresses.length)
|
|
21154
|
+
if (!addresses.length) {
|
|
21155
|
+
if (options?.metadata !== void 0) captureVaultMetadata(addresses, options.metadata);
|
|
21156
|
+
return Object.freeze([]);
|
|
21157
|
+
}
|
|
21033
21158
|
return this.#vaultResolver().resolveMany(this, addresses, options);
|
|
21034
21159
|
}
|
|
21035
21160
|
getVaultSnapshots(addresses, options) {
|