@steerprotocol/sdk 4.0.0-next.2 → 4.0.0-next.3
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 +47 -7
- package/dist/index.cjs +126 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -5
- package/dist/index.d.mts +26 -5
- package/dist/index.mjs +126 -25
- 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
|
|
|
@@ -500,6 +537,9 @@ shared internally; no snapshot is preloaded or passed through discovery. Each op
|
|
|
500
537
|
the RPC chain, with no cross-operation identity cache or additional consumer options.
|
|
501
538
|
|
|
502
539
|
Quotes read pool identity, the token pair and fee, verify factory registration, and call QuoterV2.
|
|
540
|
+
V3 and Aerodrome Slipstream quotes reject results at either default price boundary because
|
|
541
|
+
QuoterV2 does not establish the consumed input there. Requests that cannot establish full input
|
|
542
|
+
consumption fail explicitly.
|
|
503
543
|
They don't load token decimals, balances, ticks, or liquidity. Discovering a pool and then quoting
|
|
504
544
|
at its block uses 9 RPC calls in total. Counts exclude retries and provider-specific transport
|
|
505
545
|
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,36 @@ 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
|
+
//#endregion
|
|
11402
11437
|
//#region src/vault/resolver-types.ts
|
|
11403
11438
|
let ProtocolContractRole = /* @__PURE__ */ function(ProtocolContractRole) {
|
|
11404
11439
|
ProtocolContractRole["AmmFactory"] = "amm-factory";
|
|
@@ -11786,18 +11821,25 @@ async function filterVaultsByFactory(context, vaults, factory, blockNumber) {
|
|
|
11786
11821
|
//#endregion
|
|
11787
11822
|
//#region src/vault/data-adapter.ts
|
|
11788
11823
|
const nonShareBeacons = ["ScheduledJobs", "DynamicJobs"];
|
|
11789
|
-
const
|
|
11824
|
+
const metadataFields = `id name symbol decimals beaconName createdAt state pool poolKey token0 token1 payloadIpfs strategyToken { id }`;
|
|
11825
|
+
const metadataQuery = `query VaultMetadata($ids: [ID!]!, $first: Int!) {
|
|
11826
|
+
vaults(first: $first, orderBy: id, orderDirection: asc, where: {id_in: $ids}) { ${metadataFields} }
|
|
11827
|
+
_meta { ${metaFields} }
|
|
11828
|
+
}`;
|
|
11829
|
+
const vaultsQuery = (balances) => `query Vaults($first: Int!, $where: Vault_filter!, $block: Int!) {
|
|
11790
11830
|
vaults(first: $first, orderBy: id, orderDirection: asc, where: $where, block: {number: $block}) {
|
|
11791
|
-
id beaconName token0 token1
|
|
11831
|
+
${balances ? "id beaconName token0 token1" : metadataFields}
|
|
11832
|
+
token0Symbol token1Symbol token0Name token1Name
|
|
11792
11833
|
}
|
|
11793
11834
|
_meta(block: {number: $block}) { ${metaFields} }
|
|
11794
11835
|
}`;
|
|
11795
11836
|
function matchesToken(row, token, side) {
|
|
11796
|
-
|
|
11837
|
+
if (row[`token${side}`] === null) return false;
|
|
11838
|
+
const address = indexedAddress(row[`token${side}`], true);
|
|
11797
11839
|
const symbol = row[`token${side}Symbol`];
|
|
11798
11840
|
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));
|
|
11841
|
+
if (symbol !== null && typeof symbol !== "string" || name !== null && typeof name !== "string") throw new Error("Malformed vault token metadata");
|
|
11842
|
+
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
11843
|
}
|
|
11802
11844
|
function vaultFilter(token, after, beacons, balances) {
|
|
11803
11845
|
const base = {
|
|
@@ -11886,7 +11928,7 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11886
11928
|
...cursor === void 0 ? {} : { cursor },
|
|
11887
11929
|
limit: input.limit ?? 1e3
|
|
11888
11930
|
}, options, true);
|
|
11889
|
-
const items = await scanVaultBalances(this.#context, page.items, input.account, page.indexedBlock);
|
|
11931
|
+
const items = await scanVaultBalances(this.#context, page.items.map((row) => indexedAddress(row.id)), input.account, page.indexedBlock);
|
|
11890
11932
|
return Object.freeze({
|
|
11891
11933
|
items,
|
|
11892
11934
|
indexedBlock: page.indexedBlock,
|
|
@@ -11897,9 +11939,44 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11897
11939
|
}) } : {}
|
|
11898
11940
|
});
|
|
11899
11941
|
}
|
|
11942
|
+
async #metadataQuery(query, variables, options) {
|
|
11943
|
+
try {
|
|
11944
|
+
return await this.#query(query, variables, options);
|
|
11945
|
+
} catch (error) {
|
|
11946
|
+
const errors = error instanceof Error ? error.cause : void 0;
|
|
11947
|
+
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;
|
|
11948
|
+
return this.#query(query.replace(/\bpoolKey\b/g, ""), variables, options);
|
|
11949
|
+
}
|
|
11950
|
+
}
|
|
11951
|
+
async getVaultMetadata(addresses, options = {}) {
|
|
11952
|
+
const ids = addresses.map((address) => indexedAddress(address));
|
|
11953
|
+
const unique = [...new Set(ids)];
|
|
11954
|
+
if (!unique.length) return Object.freeze([]);
|
|
11955
|
+
if (unique.length > 1e3) throw new Error("Vault metadata lookup supports at most 1000 addresses");
|
|
11956
|
+
const data = await this.#metadataQuery(metadataQuery, {
|
|
11957
|
+
ids: unique,
|
|
11958
|
+
first: unique.length
|
|
11959
|
+
}, options);
|
|
11960
|
+
snapshot(data);
|
|
11961
|
+
if (!Array.isArray(data.vaults) || data.vaults.length > unique.length) throw new Error("Malformed vault metadata response");
|
|
11962
|
+
const records = /* @__PURE__ */ new Map();
|
|
11963
|
+
for (const value of data.vaults) {
|
|
11964
|
+
const metadata = vaultMetadata(value);
|
|
11965
|
+
if (!unique.includes(metadata.id) || records.has(metadata.id)) throw new Error("Unexpected or duplicate vault metadata");
|
|
11966
|
+
records.set(metadata.id, metadata);
|
|
11967
|
+
}
|
|
11968
|
+
return Object.freeze(ids.map((id) => {
|
|
11969
|
+
const metadata = records.get(id);
|
|
11970
|
+
if (!metadata) throw new Error("Vault metadata is not indexed");
|
|
11971
|
+
return metadata;
|
|
11972
|
+
}));
|
|
11973
|
+
}
|
|
11900
11974
|
async listVaults(request = {}, options = {}) {
|
|
11901
|
-
const { indexedBlock: _, ...page } = await this.#listVaults(request, options, false);
|
|
11902
|
-
return Object.freeze(
|
|
11975
|
+
const { indexedBlock: _, items, ...page } = await this.#listVaults(request, options, false);
|
|
11976
|
+
return Object.freeze({
|
|
11977
|
+
...page,
|
|
11978
|
+
items: Object.freeze(items.map(vaultMetadata))
|
|
11979
|
+
});
|
|
11903
11980
|
}
|
|
11904
11981
|
async #listVaults(request = {}, options = {}, balances = false) {
|
|
11905
11982
|
const limit = request.limit ?? 100;
|
|
@@ -11932,7 +12009,7 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11932
12009
|
} });
|
|
11933
12010
|
} else expected = snapshot(await this.#query(snapshotQuery, {}, options));
|
|
11934
12011
|
const beacons = amm ? this.#beacons.get(amm) : void 0;
|
|
11935
|
-
const data = await this.#
|
|
12012
|
+
const data = await this.#metadataQuery(vaultsQuery(balances), {
|
|
11936
12013
|
first: limit,
|
|
11937
12014
|
where: vaultFilter(token, after, beacons, balances),
|
|
11938
12015
|
block: expected.block
|
|
@@ -11949,13 +12026,15 @@ var CatalogSteerDataAdapter = class extends SteerDataAdapter {
|
|
|
11949
12026
|
if (balances && (typeof row.beaconName !== "string" || nonShareBeacons.includes(row.beaconName))) throw new Error("User vault listing returned a non-share deployment");
|
|
11950
12027
|
if (beacons && (typeof row.beaconName !== "string" || !beacons.includes(row.beaconName))) throw new Error("Vault listing does not match the AMM beacon mapping");
|
|
11951
12028
|
if (token && !matchesToken(row, token, 0) && !matchesToken(row, token, 1)) throw new Error("Vault listing does not match the token filter");
|
|
11952
|
-
|
|
12029
|
+
if (!balances) vaultMetadata(row);
|
|
12030
|
+
return row;
|
|
11953
12031
|
});
|
|
11954
|
-
const last = items.at(-1);
|
|
12032
|
+
const last = items.at(-1)?.id;
|
|
11955
12033
|
const factory = amm ? this.#factories.get(amm) : void 0;
|
|
11956
|
-
const filtered = factory ? await filterVaultsByFactory(this.#context, items, factory, BigInt(expected.block)) : items;
|
|
12034
|
+
const filtered = factory ? await filterVaultsByFactory(this.#context, items.map((row) => indexedAddress(row.id)), factory, BigInt(expected.block)) : items.map((row) => indexedAddress(row.id));
|
|
12035
|
+
const included = new Set(filtered);
|
|
11957
12036
|
return Object.freeze({
|
|
11958
|
-
items: Object.freeze(
|
|
12037
|
+
items: Object.freeze(items.filter((row) => included.has(indexedAddress(row.id)))),
|
|
11959
12038
|
indexedBlock: BigInt(expected.block),
|
|
11960
12039
|
...items.length === limit && last ? { nextCursor: JSON.stringify({
|
|
11961
12040
|
version: 1,
|
|
@@ -13759,10 +13838,13 @@ var SteerVault = class {
|
|
|
13759
13838
|
chain;
|
|
13760
13839
|
steer;
|
|
13761
13840
|
underlying;
|
|
13762
|
-
|
|
13841
|
+
metadata;
|
|
13842
|
+
constructor(address, chain, steer, underlying, metadata) {
|
|
13763
13843
|
this.address = address;
|
|
13764
13844
|
this.chain = chain;
|
|
13765
13845
|
this.steer = steer;
|
|
13846
|
+
this.metadata = vaultMetadata(metadata);
|
|
13847
|
+
if (this.metadata.id !== address.toLowerCase()) throw new Error("Vault metadata belongs to another vault");
|
|
13766
13848
|
this.underlying = Object.freeze([...underlying]);
|
|
13767
13849
|
}
|
|
13768
13850
|
};
|
|
@@ -14424,7 +14506,7 @@ var AerodromeVault = class extends LiquidityVault {
|
|
|
14424
14506
|
#positionManager;
|
|
14425
14507
|
/** @internal */
|
|
14426
14508
|
constructor(context, identity) {
|
|
14427
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
14509
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
14428
14510
|
const underlying = context.underlying[0];
|
|
14429
14511
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !(underlying.amm instanceof AerodromeSlipstreamAdapter) || typeof underlying.pool !== "string") identityMismatch("Expected one Aerodrome vault pool");
|
|
14430
14512
|
requireAddress(underlying.pool);
|
|
@@ -14528,6 +14610,7 @@ async function readAerodromeBulkSnapshot(vault, data, rpc, beacon) {
|
|
|
14528
14610
|
steer: vault.steer,
|
|
14529
14611
|
address: vault.address,
|
|
14530
14612
|
underlying: vault.underlying,
|
|
14613
|
+
metadata: vault.metadata,
|
|
14531
14614
|
blockNumber: rpc.blockNumber
|
|
14532
14615
|
}, identity, {
|
|
14533
14616
|
raw: decodeAerodromeSnapshot(data),
|
|
@@ -15567,6 +15650,7 @@ async function readIntegralBulkSnapshot(vault, data, rpc) {
|
|
|
15567
15650
|
steer: vault.steer,
|
|
15568
15651
|
address: vault.address,
|
|
15569
15652
|
underlying: vault.underlying,
|
|
15653
|
+
metadata: vault.metadata,
|
|
15570
15654
|
blockNumber: rpc.blockNumber
|
|
15571
15655
|
}, {
|
|
15572
15656
|
raw,
|
|
@@ -15580,7 +15664,7 @@ var AlgebraIntegralVault = class extends LiquidityVault {
|
|
|
15580
15664
|
amm;
|
|
15581
15665
|
pool;
|
|
15582
15666
|
constructor(context) {
|
|
15583
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
15667
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
15584
15668
|
const underlying = context.underlying[0];
|
|
15585
15669
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isAlgebraIntegralAdapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one Integral vault pool");
|
|
15586
15670
|
requireAddress(underlying.pool);
|
|
@@ -17179,6 +17263,7 @@ async function readV3BulkSnapshot(vault, data, rpc) {
|
|
|
17179
17263
|
steer: vault.steer,
|
|
17180
17264
|
address: vault.address,
|
|
17181
17265
|
underlying: vault.underlying,
|
|
17266
|
+
metadata: vault.metadata,
|
|
17182
17267
|
blockNumber: rpc.blockNumber
|
|
17183
17268
|
}, {
|
|
17184
17269
|
raw,
|
|
@@ -17192,7 +17277,7 @@ var UniswapV3Vault = class extends LiquidityVault {
|
|
|
17192
17277
|
amm;
|
|
17193
17278
|
pool;
|
|
17194
17279
|
constructor(context) {
|
|
17195
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
17280
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
17196
17281
|
const underlying = context.underlying[0];
|
|
17197
17282
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isUniswapV3Adapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one V3 vault pool");
|
|
17198
17283
|
requireAddress(underlying.pool);
|
|
@@ -18825,7 +18910,7 @@ var UniswapV4Vault = class extends LiquidityVault {
|
|
|
18825
18910
|
amm;
|
|
18826
18911
|
pool;
|
|
18827
18912
|
constructor(context) {
|
|
18828
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
18913
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
18829
18914
|
const underlying = context.underlying[0];
|
|
18830
18915
|
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
18916
|
this.amm = underlying.amm;
|
|
@@ -20205,6 +20290,7 @@ async function readLegacyBulkSnapshot(vault, data, rpc, beacon) {
|
|
|
20205
20290
|
steer: vault.steer,
|
|
20206
20291
|
address: vault.address,
|
|
20207
20292
|
underlying: vault.underlying,
|
|
20293
|
+
metadata: vault.metadata,
|
|
20208
20294
|
blockNumber: rpc.blockNumber
|
|
20209
20295
|
}, {
|
|
20210
20296
|
raw,
|
|
@@ -20218,7 +20304,7 @@ var AlgebraLegacyVault = class extends LiquidityVault {
|
|
|
20218
20304
|
amm;
|
|
20219
20305
|
pool;
|
|
20220
20306
|
constructor(context) {
|
|
20221
|
-
super(context.address, context.steer.context, context.steer, context.underlying);
|
|
20307
|
+
super(context.address, context.steer.context, context.steer, context.underlying, context.metadata);
|
|
20222
20308
|
const underlying = context.underlying[0];
|
|
20223
20309
|
if (context.underlying.length !== 1 || underlying?.kind !== "liquidity" || !isAlgebraLegacyAdapter(underlying.amm) || typeof underlying.pool !== "string") identityMismatch("Expected one Legacy vault pool");
|
|
20224
20310
|
requireAddress(underlying.pool);
|
|
@@ -20825,7 +20911,7 @@ var VaultResolver = class {
|
|
|
20825
20911
|
if (!vault) throw new Error("Missing resolved vault");
|
|
20826
20912
|
return vault;
|
|
20827
20913
|
}
|
|
20828
|
-
async #createVault(context, identity, verified) {
|
|
20914
|
+
async #createVault(context, identity, loadMetadata, verified) {
|
|
20829
20915
|
const { steer, address } = context;
|
|
20830
20916
|
if (!identity) throw new VaultResolutionError("not-registered", "Vault is not registered with Steer");
|
|
20831
20917
|
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 +20923,12 @@ var VaultResolver = class {
|
|
|
20837
20923
|
lending: /* @__PURE__ */ new Map()
|
|
20838
20924
|
};
|
|
20839
20925
|
const resolvedUnderlying = Object.freeze(underlying.map((position) => this.#resolveUnderlying(position, adapters)));
|
|
20926
|
+
const metadata = await loadMetadata();
|
|
20927
|
+
if (metadata.id.toLowerCase() !== address.toLowerCase()) throw new Error("Vault metadata belongs to another vault");
|
|
20840
20928
|
const resolved = Object.freeze({
|
|
20841
20929
|
...context,
|
|
20842
|
-
underlying: resolvedUnderlying
|
|
20930
|
+
underlying: resolvedUnderlying,
|
|
20931
|
+
metadata
|
|
20843
20932
|
});
|
|
20844
20933
|
const vault = binding.createVault(resolved);
|
|
20845
20934
|
this.#validateVault(vault, binding, resolved);
|
|
@@ -20859,11 +20948,21 @@ var VaultResolver = class {
|
|
|
20859
20948
|
steer: vault.steer,
|
|
20860
20949
|
address: vault.address,
|
|
20861
20950
|
underlying: vault.underlying,
|
|
20951
|
+
metadata: vault.metadata,
|
|
20862
20952
|
blockNumber
|
|
20863
20953
|
});
|
|
20864
20954
|
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
20955
|
return readValidatedVaultSnapshot(vault, snapshot, vault.steer.context);
|
|
20866
20956
|
}
|
|
20957
|
+
#metadata(steer, addresses) {
|
|
20958
|
+
let pending;
|
|
20959
|
+
return async (address) => {
|
|
20960
|
+
pending ??= steer.dataAdapter.getVaultMetadata(addresses);
|
|
20961
|
+
const metadata = (await pending).find((record) => record.id.toLowerCase() === address.toLowerCase());
|
|
20962
|
+
if (!metadata) throw new Error("Vault metadata is not indexed");
|
|
20963
|
+
return metadata;
|
|
20964
|
+
};
|
|
20965
|
+
}
|
|
20867
20966
|
/** Bounded bulk identity pages at one block; compatibility uses four active loads. */
|
|
20868
20967
|
async resolveMany(steer, addresses, options) {
|
|
20869
20968
|
const inputs = [...addresses];
|
|
@@ -20874,6 +20973,7 @@ var VaultResolver = class {
|
|
|
20874
20973
|
let bulkSupported = !!this.#reader.readMany;
|
|
20875
20974
|
for (let start = 0; start < inputs.length; start += 64) {
|
|
20876
20975
|
const batch = inputs.slice(start, start + 64);
|
|
20976
|
+
const loadMetadata = this.#metadata(steer, batch);
|
|
20877
20977
|
const contexts = batch.map((address) => Object.freeze({
|
|
20878
20978
|
steer,
|
|
20879
20979
|
address,
|
|
@@ -20885,13 +20985,13 @@ var VaultResolver = class {
|
|
|
20885
20985
|
for (const [i, context] of contexts.entries()) {
|
|
20886
20986
|
const read = reads[i];
|
|
20887
20987
|
if (!read) throw new VaultResolutionError("identity-mismatch", "Missing bulk vault identity");
|
|
20888
|
-
result.push((await this.#createVault(context, read.identity, read)).vault);
|
|
20988
|
+
result.push((await this.#createVault(context, read.identity, () => loadMetadata(context.address), read)).vault);
|
|
20889
20989
|
}
|
|
20890
20990
|
} else {
|
|
20891
20991
|
bulkSupported = false;
|
|
20892
20992
|
for (let i = 0; i < batch.length; i += 4) result.push(...await Promise.all(contexts.slice(i, i + 4).map(async (context) => {
|
|
20893
20993
|
const identity = await this.#reader.readIdentity(context);
|
|
20894
|
-
return (await this.#createVault(context, identity)).vault;
|
|
20994
|
+
return (await this.#createVault(context, identity, () => loadMetadata(context.address))).vault;
|
|
20895
20995
|
})));
|
|
20896
20996
|
}
|
|
20897
20997
|
}
|
|
@@ -20907,6 +21007,7 @@ var VaultResolver = class {
|
|
|
20907
21007
|
let bulkSupported = !!this.#reader.readSnapshots;
|
|
20908
21008
|
for (let start = 0; start < inputs.length; start += 16) {
|
|
20909
21009
|
const batch = inputs.slice(start, start + 16);
|
|
21010
|
+
const loadMetadata = this.#metadata(steer, batch);
|
|
20910
21011
|
const contexts = batch.map((address) => Object.freeze({
|
|
20911
21012
|
steer,
|
|
20912
21013
|
address,
|
|
@@ -20918,7 +21019,7 @@ var VaultResolver = class {
|
|
|
20918
21019
|
for (const [i, context] of contexts.entries()) {
|
|
20919
21020
|
const read = reads[i];
|
|
20920
21021
|
if (!read) throw new Error("Missing bulk vault snapshot");
|
|
20921
|
-
const { vault } = await this.#createVault(context, read.identity, read);
|
|
21022
|
+
const { vault } = await this.#createVault(context, read.identity, () => loadMetadata(context.address), read);
|
|
20922
21023
|
if (vault.kind !== "liquidity") throw new VaultResolutionError("invalid-vault", "Vault does not support liquidity snapshots");
|
|
20923
21024
|
result.push(await read.readSnapshot(vault));
|
|
20924
21025
|
}
|
|
@@ -20931,7 +21032,7 @@ var VaultResolver = class {
|
|
|
20931
21032
|
blockNumber
|
|
20932
21033
|
});
|
|
20933
21034
|
const identity = await this.#reader.readIdentity(context);
|
|
20934
|
-
const { vault, binding, context: resolved } = await this.#createVault(context, identity);
|
|
21035
|
+
const { vault, binding, context: resolved } = await this.#createVault(context, identity, () => loadMetadata(context.address));
|
|
20935
21036
|
if (vault.kind !== "liquidity") throw new VaultResolutionError("invalid-vault", "Vault does not support liquidity snapshots");
|
|
20936
21037
|
if (!binding.readSnapshot) throw new VaultResolutionError("not-configured", "No verified snapshot reader configured for vault implementation");
|
|
20937
21038
|
const snapshot = await binding.readSnapshot(resolved);
|