@whisk/steakhouse 0.3.9 → 0.4.0

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.
@@ -0,0 +1,222 @@
1
+ import { graphql, type ResultOf } from "@whisk/graphql"
2
+ import type { SteakhouseClient } from "../client.js"
3
+ import type { VaultConfig } from "../metadata/types.js"
4
+ import {
5
+ apyFragment,
6
+ erc20Fragment,
7
+ morphoMarketAllocationFragment,
8
+ type Prettify,
9
+ resolveVaultConfig,
10
+ riskAssessmentFragment,
11
+ tokenAmountFragment,
12
+ type VaultKeyVariables,
13
+ vaultSummaryFragment,
14
+ } from "./fragments.js"
15
+
16
+ const vaultDetailQuery = graphql(
17
+ `
18
+ query GetSteakhouseVault($keys: [Erc4626VaultKey!]!) {
19
+ erc4626Vaults(where: { keys: $keys }) {
20
+ items {
21
+ ...VaultSummaryFields
22
+
23
+ # Morpho Vault V1 - full details
24
+ ... on MorphoVault {
25
+ totalLiquidity {
26
+ ...TokenAmountFields
27
+ }
28
+ deploymentTimestamp
29
+ performanceFeeV1: performanceFee
30
+ curatorAddress
31
+ guardianAddress
32
+ allocations: marketAllocations {
33
+ supplyCap {
34
+ ...TokenAmountFields
35
+ }
36
+ market {
37
+ ...MorphoMarketAllocationFields
38
+ }
39
+ position {
40
+ supplyAmount {
41
+ ...TokenAmountFields
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ # Box Vault - full details
48
+ ... on BoxVault {
49
+ leverage
50
+ allocations {
51
+ token {
52
+ ...Erc20Fields
53
+ yield {
54
+ intrinsicApy
55
+ }
56
+ }
57
+ balance {
58
+ ...TokenAmountFields
59
+ }
60
+ }
61
+ fundingModules {
62
+ fundingModuleAddress
63
+ nav {
64
+ ...TokenAmountFields
65
+ }
66
+ ... on BoxMorphoFundingModule {
67
+ positions {
68
+ market {
69
+ ...MorphoMarketAllocationFields
70
+ borrowApy {
71
+ ...ApyFields
72
+ }
73
+ borrowApy1d {
74
+ ...ApyFields
75
+ }
76
+ borrowApy7d {
77
+ ...ApyFields
78
+ }
79
+ borrowApy30d {
80
+ ...ApyFields
81
+ }
82
+ }
83
+ loopingLeverage
84
+ loopingNetApy
85
+ collateralAmount {
86
+ ...TokenAmountFields
87
+ }
88
+ borrowAmount {
89
+ ...TokenAmountFields
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ # Morpho Vault V2 - full details for market adapter, user meant to fetch vault adapter details separately if they want those
97
+ ... on MorphoVaultV2 {
98
+ deploymentTimestamp
99
+ performanceFee {
100
+ raw
101
+ formatted
102
+ }
103
+ managementFee {
104
+ raw
105
+ formatted
106
+ }
107
+ nav {
108
+ ...TokenAmountFields
109
+ }
110
+ liquidityAssets {
111
+ ...TokenAmountFields
112
+ }
113
+ idleAssets {
114
+ ...TokenAmountFields
115
+ }
116
+ allocations {
117
+ adapterAddress
118
+ name
119
+ riskAssessment {
120
+ ...RiskAssessmentFields
121
+ }
122
+ adapterCap {
123
+ allocation {
124
+ ...TokenAmountFields
125
+ }
126
+ absoluteCap {
127
+ ...TokenAmountFields
128
+ }
129
+ relativeCap {
130
+ raw
131
+ formatted
132
+ }
133
+ }
134
+
135
+ # For ERC-4626, expect user to call this again if they want full details on the adapters vault
136
+ ... on Erc4626VaultAdapter {
137
+ vault {
138
+ __typename
139
+ address
140
+ }
141
+ }
142
+
143
+ ... on MarketV1Adapter {
144
+ marketCaps {
145
+ allocation {
146
+ ...TokenAmountFields
147
+ }
148
+ absoluteCap {
149
+ ...TokenAmountFields
150
+ }
151
+ market {
152
+ ...MorphoMarketAllocationFields
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ `,
162
+ [
163
+ vaultSummaryFragment,
164
+ erc20Fragment,
165
+ tokenAmountFragment,
166
+ riskAssessmentFragment,
167
+ apyFragment,
168
+ morphoMarketAllocationFragment,
169
+ ],
170
+ )
171
+
172
+ type VaultDetailItem = NonNullable<
173
+ ResultOf<typeof vaultDetailQuery>["erc4626Vaults"]["items"][number]
174
+ >
175
+
176
+ export type SteakhouseVaultDetail = Prettify<
177
+ VaultDetailItem & {
178
+ strategy: VaultConfig["strategy"] | undefined
179
+ description: VaultConfig["description"] | undefined
180
+ isListed: boolean
181
+ }
182
+ >
183
+
184
+ export interface GetVaultVariables extends VaultKeyVariables {
185
+ isBox?: boolean
186
+ }
187
+
188
+ export async function getVault(
189
+ client: SteakhouseClient,
190
+ variables: GetVaultVariables,
191
+ ): Promise<SteakhouseVaultDetail> {
192
+ const config = resolveVaultConfig(variables)
193
+
194
+ if (!config && !variables.isBox) {
195
+ throw new Error(
196
+ `Vault ${variables.vaultAddress} on chain ${variables.chainId} is not a known Steakhouse vault`,
197
+ )
198
+ }
199
+
200
+ const result = await client.query(vaultDetailQuery, {
201
+ keys: [
202
+ {
203
+ chainId: variables.chainId,
204
+ vaultAddress: config?.address ?? variables.vaultAddress,
205
+ protocol: config?.protocol ?? "box",
206
+ },
207
+ ],
208
+ })
209
+
210
+ const item = result.erc4626Vaults.items[0]
211
+ if (!item) {
212
+ throw new Error(`Vault ${variables.vaultAddress} on chain ${variables.chainId} was not found`)
213
+ }
214
+
215
+ return {
216
+ ...item,
217
+ name: config?.name ?? item.name,
218
+ strategy: config?.strategy,
219
+ description: config?.description,
220
+ isListed: config?.isListed ?? false,
221
+ }
222
+ }
@@ -0,0 +1,72 @@
1
+ import { type FragmentOf, graphql } from "@whisk/graphql"
2
+ import type { SteakhouseClient } from "../client.js"
3
+ import {
4
+ morphoVaultHistoricalEntryFragment,
5
+ resolveVaultConfig,
6
+ type VaultKeyVariables,
7
+ } from "./fragments.js"
8
+
9
+ const vaultHistoricalQuery = graphql(
10
+ `
11
+ query GetSteakhouseVaultHistory($keys: [Erc4626VaultKey!]!) {
12
+ erc4626Vaults(where: { keys: $keys }) {
13
+ items {
14
+ address
15
+ historical {
16
+ ... on MorphoVaultHistorical {
17
+ daily {
18
+ ...MorphoVaultHistoricalEntryFields
19
+ }
20
+ weekly {
21
+ ...MorphoVaultHistoricalEntryFields
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ `,
29
+ [morphoVaultHistoricalEntryFragment],
30
+ )
31
+
32
+ type HistoricalEntry = FragmentOf<typeof morphoVaultHistoricalEntryFragment>
33
+
34
+ export interface SteakhouseVaultHistory {
35
+ daily: ReadonlyArray<HistoricalEntry> | null
36
+ weekly: ReadonlyArray<HistoricalEntry> | null
37
+ }
38
+
39
+ export type GetVaultHistoryVariables = VaultKeyVariables
40
+
41
+ export async function getVaultHistory(
42
+ client: SteakhouseClient,
43
+ variables: GetVaultHistoryVariables,
44
+ ): Promise<SteakhouseVaultHistory> {
45
+ const config = resolveVaultConfig(variables)
46
+
47
+ if (!config) {
48
+ return { daily: null, weekly: null }
49
+ }
50
+
51
+ const result = await client.query(vaultHistoricalQuery, {
52
+ keys: [
53
+ {
54
+ chainId: config.chainId,
55
+ vaultAddress: config.address,
56
+ protocol: config.protocol,
57
+ },
58
+ ],
59
+ })
60
+
61
+ const item = result.erc4626Vaults.items[0]
62
+ if (!item?.historical) {
63
+ return { daily: null, weekly: null }
64
+ }
65
+
66
+ const historical = item.historical
67
+
68
+ return {
69
+ daily: "daily" in historical ? (historical.daily ?? null) : null,
70
+ weekly: "weekly" in historical ? (historical.weekly ?? null) : null,
71
+ }
72
+ }
@@ -0,0 +1,64 @@
1
+ import { graphql, type ResultOf } from "@whisk/graphql"
2
+ import type { SteakhouseClient } from "../client.js"
3
+ import { STEAKHOUSE_VAULTS } from "../metadata/generated/vaults.js"
4
+ import type { VaultConfig } from "../metadata/types.js"
5
+ import { type Prettify, vaultSummaryFragment } from "./fragments.js"
6
+
7
+ const vaultsQuery = graphql(
8
+ `
9
+ query GetSteakhouseVaults($keys: [Erc4626VaultKey!]!) {
10
+ erc4626Vaults(where: { keys: $keys }) {
11
+ items {
12
+ ...VaultSummaryFields
13
+ }
14
+ }
15
+ }
16
+ `,
17
+ [vaultSummaryFragment],
18
+ )
19
+
20
+ type VaultItem = NonNullable<ResultOf<typeof vaultsQuery>["erc4626Vaults"]["items"][number]>
21
+
22
+ export type SteakhouseVaultSummary = Prettify<
23
+ VaultItem & {
24
+ strategy: VaultConfig["strategy"]
25
+ isListed: boolean
26
+ }
27
+ >
28
+
29
+ export async function getVaults(client: SteakhouseClient): Promise<SteakhouseVaultSummary[]> {
30
+ const configs = STEAKHOUSE_VAULTS
31
+
32
+ const keys = configs.map((v) => ({
33
+ chainId: v.chainId,
34
+ vaultAddress: v.address,
35
+ protocol: v.protocol,
36
+ }))
37
+
38
+ const result = await client.query(vaultsQuery, { keys })
39
+
40
+ const configByChainAndAddress = new Map(
41
+ configs.map((v) => [`${v.chainId}:${v.address.toLowerCase()}`, v]),
42
+ )
43
+
44
+ const vaults: SteakhouseVaultSummary[] = []
45
+ for (const item of result.erc4626Vaults.items) {
46
+ if (!item) continue
47
+ const config = configByChainAndAddress.get(`${item.chain.id}:${item.address.toLowerCase()}`)
48
+ if (!config) {
49
+ console.warn(
50
+ `[getVaults] Vault ${item.address} on chain ${item.chain.id} has no matching config, skipping`,
51
+ )
52
+ continue
53
+ }
54
+
55
+ vaults.push({
56
+ ...item,
57
+ name: config.name ?? item.name,
58
+ strategy: config.strategy,
59
+ isListed: config.isListed,
60
+ })
61
+ }
62
+
63
+ return vaults
64
+ }
@@ -1 +1,4 @@
1
1
  export * from "./getStats.js"
2
+ export * from "./getVault.js"
3
+ export * from "./getVaultHistory.js"
4
+ export * from "./getVaults.js"