@whisk/steakhouse 0.0.6 → 0.0.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/dist/index.d.ts +1 -6
- package/dist/queries/getTvl.d.ts +34 -0
- package/dist/queries/getTvl.js +34 -0
- package/dist/queries/getTvl.js.map +1 -0
- package/dist/queries/index.d.ts +1 -6
- package/dist/queries/index.js +1 -8
- package/dist/queries/index.js.map +1 -1
- package/dist/react/hooks/index.d.ts +3 -10
- package/dist/react/hooks/index.js +2 -6
- package/dist/react/hooks/index.js.map +1 -1
- package/dist/react/hooks/useTvl.d.ts +9 -0
- package/dist/react/hooks/useTvl.js +15 -0
- package/dist/react/hooks/useTvl.js.map +1 -0
- package/dist/react/index.d.ts +3 -10
- package/package.json +3 -3
- package/src/queries/getTvl.ts +38 -0
- package/src/queries/index.ts +1 -10
- package/src/react/hooks/index.ts +1 -3
- package/src/react/hooks/useTvl.ts +14 -0
- package/dist/queries/fragments/vaultDetail.d.ts +0 -265
- package/dist/queries/fragments/vaultDetail.js +0 -148
- package/dist/queries/fragments/vaultDetail.js.map +0 -1
- package/dist/queries/getDetailedVault.d.ts +0 -591
- package/dist/queries/getDetailedVault.js +0 -92
- package/dist/queries/getDetailedVault.js.map +0 -1
- package/dist/queries/getVaults.d.ts +0 -288
- package/dist/queries/getVaults.js +0 -47
- package/dist/queries/getVaults.js.map +0 -1
- package/dist/queries/steakhouseMetadata.d.ts +0 -14
- package/dist/queries/steakhouseMetadata.js +0 -12
- package/dist/queries/steakhouseMetadata.js.map +0 -1
- package/dist/queries/types.d.ts +0 -5
- package/dist/queries/types.js +0 -1
- package/dist/queries/types.js.map +0 -1
- package/dist/react/hooks/useDetailedVault.d.ts +0 -12
- package/dist/react/hooks/useDetailedVault.js +0 -16
- package/dist/react/hooks/useDetailedVault.js.map +0 -1
- package/dist/react/hooks/useSteakhouseQuery.d.ts +0 -11
- package/dist/react/hooks/useSteakhouseQuery.js +0 -14
- package/dist/react/hooks/useSteakhouseQuery.js.map +0 -1
- package/dist/react/hooks/useVaults.d.ts +0 -12
- package/dist/react/hooks/useVaults.js +0 -16
- package/dist/react/hooks/useVaults.js.map +0 -1
- package/src/queries/fragments/vaultDetail.ts +0 -151
- package/src/queries/getDetailedVault.test.ts +0 -202
- package/src/queries/getDetailedVault.ts +0 -131
- package/src/queries/getVaults.test.ts +0 -167
- package/src/queries/getVaults.ts +0 -77
- package/src/queries/steakhouseMetadata.ts +0 -19
- package/src/queries/types.ts +0 -6
- package/src/react/hooks/useDetailedVault.ts +0 -19
- package/src/react/hooks/useSteakhouseQuery.ts +0 -17
- package/src/react/hooks/useVaults.ts +0 -17
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
|
-
import { createMockClient } from "../../test/mocks.js"
|
|
3
|
-
import { getDetailedVault } from "./getDetailedVault.js"
|
|
4
|
-
|
|
5
|
-
// Mock the generated vaults module
|
|
6
|
-
vi.mock("../metadata/generated/vaults.js", () => ({
|
|
7
|
-
STEAKHOUSE_VAULTS: [
|
|
8
|
-
{
|
|
9
|
-
chainId: 1,
|
|
10
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
11
|
-
protocol: "morpho_v1",
|
|
12
|
-
name: "Steakhouse USDC",
|
|
13
|
-
description: "A USDC vault",
|
|
14
|
-
type: "Prime",
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
chainId: 1,
|
|
18
|
-
address: "0x2222222222222222222222222222222222222222",
|
|
19
|
-
protocol: "morpho_v1",
|
|
20
|
-
name: "Steakhouse ETH",
|
|
21
|
-
description: "An ETH vault",
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
}))
|
|
25
|
-
|
|
26
|
-
function createMockVaultDetailResponse(vault: { vaultAddress: string } | null) {
|
|
27
|
-
if (!vault) {
|
|
28
|
-
return { erc4626Vaults: { items: [] } }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
erc4626Vaults: {
|
|
33
|
-
items: [
|
|
34
|
-
{
|
|
35
|
-
chain: { id: 1, name: "Ethereum", icon: "https://eth.icon" },
|
|
36
|
-
vaultAddress: vault.vaultAddress,
|
|
37
|
-
name: "API Vault Name",
|
|
38
|
-
symbol: "VAULT",
|
|
39
|
-
decimals: 18,
|
|
40
|
-
asset: {
|
|
41
|
-
address: "0xasset",
|
|
42
|
-
symbol: "USDC",
|
|
43
|
-
name: "USD Coin",
|
|
44
|
-
icon: "https://icon.url",
|
|
45
|
-
priceUsd: 1.0,
|
|
46
|
-
decimals: 6,
|
|
47
|
-
},
|
|
48
|
-
totalAssets: { raw: "1000000000000", formatted: "1000000", usd: 1000000 },
|
|
49
|
-
totalLiquidity: { formatted: "500000", usd: 500000 },
|
|
50
|
-
apy: {
|
|
51
|
-
base: 0.05,
|
|
52
|
-
total: 0.08,
|
|
53
|
-
rewards: [{ token: { symbol: "MORPHO", icon: "https://morpho.icon" }, apr: 0.03 }],
|
|
54
|
-
fee: 0.01,
|
|
55
|
-
},
|
|
56
|
-
performanceFee: 0.1,
|
|
57
|
-
feeRecipientAddress: "0xfee",
|
|
58
|
-
ownerAddress: "0xowner",
|
|
59
|
-
curatorAddress: "0xcurator",
|
|
60
|
-
guardianAddress: "0xguardian",
|
|
61
|
-
metadata: {
|
|
62
|
-
description: "API description",
|
|
63
|
-
image: "https://image.url",
|
|
64
|
-
forumLink: "https://forum.url",
|
|
65
|
-
curator: {
|
|
66
|
-
name: "Curator",
|
|
67
|
-
image: "https://curator.image",
|
|
68
|
-
url: "https://curator.url",
|
|
69
|
-
},
|
|
70
|
-
curators: [],
|
|
71
|
-
},
|
|
72
|
-
marketAllocations: [],
|
|
73
|
-
riskAssessment: { steakhouse: { score: 0.8, rating: "A" } },
|
|
74
|
-
// Historical data (only in historical query)
|
|
75
|
-
historical: {
|
|
76
|
-
daily: [
|
|
77
|
-
{
|
|
78
|
-
bucketTimestamp: 1704067200,
|
|
79
|
-
totalSupplied: { formatted: "1000000", usd: 1000000 },
|
|
80
|
-
supplyApy7d: { total: 0.08 },
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
describe("getDetailedVault", () => {
|
|
91
|
-
beforeEach(() => {
|
|
92
|
-
vi.clearAllMocks()
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it("returns null for vault not in whitelist", async () => {
|
|
96
|
-
const client = createMockClient({})
|
|
97
|
-
|
|
98
|
-
const result = await getDetailedVault(client, {
|
|
99
|
-
chainId: 1,
|
|
100
|
-
address: "0x9999999999999999999999999999999999999999",
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
expect(result).toBeNull()
|
|
104
|
-
expect(client.query).not.toHaveBeenCalled()
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it("returns null for vault on wrong chain", async () => {
|
|
108
|
-
const client = createMockClient({})
|
|
109
|
-
|
|
110
|
-
const result = await getDetailedVault(client, {
|
|
111
|
-
chainId: 8453, // Wrong chain
|
|
112
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
expect(result).toBeNull()
|
|
116
|
-
expect(client.query).not.toHaveBeenCalled()
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it("returns vault with historical data by default", async () => {
|
|
120
|
-
const client = createMockClient(
|
|
121
|
-
createMockVaultDetailResponse({
|
|
122
|
-
vaultAddress: "0x1111111111111111111111111111111111111111",
|
|
123
|
-
}),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
const result = await getDetailedVault(client, {
|
|
127
|
-
chainId: 1,
|
|
128
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
expect(result).not.toBeNull()
|
|
132
|
-
expect(result?.historical).toBeDefined()
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it("can exclude historical data", async () => {
|
|
136
|
-
const mockResponse = createMockVaultDetailResponse({
|
|
137
|
-
vaultAddress: "0x1111111111111111111111111111111111111111",
|
|
138
|
-
})
|
|
139
|
-
// Remove historical from response
|
|
140
|
-
// biome-ignore lint/complexity/useLiteralKeys: Needed for index signature access
|
|
141
|
-
delete (mockResponse.erc4626Vaults.items[0] as Record<string, unknown>)["historical"]
|
|
142
|
-
|
|
143
|
-
const client = createMockClient(mockResponse)
|
|
144
|
-
|
|
145
|
-
const result = await getDetailedVault(client, {
|
|
146
|
-
chainId: 1,
|
|
147
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
148
|
-
historical: false,
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
expect(result).not.toBeNull()
|
|
152
|
-
// Different query should be called (without historical fragment)
|
|
153
|
-
expect(client.query).toHaveBeenCalled()
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it("augments result with steakhouse metadata", async () => {
|
|
157
|
-
const client = createMockClient(
|
|
158
|
-
createMockVaultDetailResponse({
|
|
159
|
-
vaultAddress: "0x1111111111111111111111111111111111111111",
|
|
160
|
-
}),
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
const result = await getDetailedVault(client, {
|
|
164
|
-
chainId: 1,
|
|
165
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
expect(result?.steakhouseMetadata).toEqual({
|
|
169
|
-
name: "Steakhouse USDC",
|
|
170
|
-
description: "A USDC vault",
|
|
171
|
-
type: "Prime",
|
|
172
|
-
protocol: "morpho_v1",
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it("handles case-insensitive address matching", async () => {
|
|
177
|
-
const client = createMockClient(
|
|
178
|
-
createMockVaultDetailResponse({
|
|
179
|
-
vaultAddress: "0x1111111111111111111111111111111111111111",
|
|
180
|
-
}),
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
const result = await getDetailedVault(client, {
|
|
184
|
-
chainId: 1,
|
|
185
|
-
// Uppercase address
|
|
186
|
-
address: "0x1111111111111111111111111111111111111111".toUpperCase() as `0x${string}`,
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
expect(result).not.toBeNull()
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
it("returns null when API returns no results", async () => {
|
|
193
|
-
const client = createMockClient(createMockVaultDetailResponse(null))
|
|
194
|
-
|
|
195
|
-
const result = await getDetailedVault(client, {
|
|
196
|
-
chainId: 1,
|
|
197
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
expect(result).toBeNull()
|
|
201
|
-
})
|
|
202
|
-
})
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { graphql } from "@whisk/graphql"
|
|
2
|
-
import type { SteakhouseClient } from "../client.js"
|
|
3
|
-
import { STEAKHOUSE_VAULTS } from "../metadata/generated/vaults.js"
|
|
4
|
-
import type { Address } from "../metadata/types.js"
|
|
5
|
-
import { type VaultDetail, vaultDetailFragment } from "./fragments/vaultDetail.js"
|
|
6
|
-
import { buildSteakhouseMetadata, type SteakhouseMetadata } from "./steakhouseMetadata.js"
|
|
7
|
-
|
|
8
|
-
/** Historical data fragment for vault charts (works for both v1 and v2) */
|
|
9
|
-
const vaultHistoricalFragment = graphql(`
|
|
10
|
-
fragment VaultHistoricalFragment on Erc4626Vault {
|
|
11
|
-
... on MorphoVault {
|
|
12
|
-
historical {
|
|
13
|
-
daily {
|
|
14
|
-
bucketTimestamp
|
|
15
|
-
totalSupplied {
|
|
16
|
-
formatted
|
|
17
|
-
usd
|
|
18
|
-
}
|
|
19
|
-
supplyApy7d {
|
|
20
|
-
total
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
... on MorphoVaultV2 {
|
|
26
|
-
historical {
|
|
27
|
-
daily {
|
|
28
|
-
bucketTimestamp
|
|
29
|
-
totalSupplied {
|
|
30
|
-
formatted
|
|
31
|
-
usd
|
|
32
|
-
}
|
|
33
|
-
supplyApy7d {
|
|
34
|
-
total
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
`)
|
|
41
|
-
|
|
42
|
-
/** Historical data type extracted from the fragment */
|
|
43
|
-
type VaultHistoricalData = {
|
|
44
|
-
daily: Array<{
|
|
45
|
-
bucketTimestamp: number
|
|
46
|
-
totalSupplied: { formatted: string; usd: number | null }
|
|
47
|
-
supplyApy7d: { total: number }
|
|
48
|
-
}>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Query for fetching a single vault without historical data */
|
|
52
|
-
export const vaultQuery = graphql(
|
|
53
|
-
`
|
|
54
|
-
query GetVault($where: Erc4626VaultFilter) {
|
|
55
|
-
erc4626Vaults(where: $where, limit: 1) {
|
|
56
|
-
items {
|
|
57
|
-
...VaultDetailFragment
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
`,
|
|
62
|
-
[vaultDetailFragment],
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
/** Query for fetching a single vault with historical data */
|
|
66
|
-
export const vaultWithHistoricalQuery = graphql(
|
|
67
|
-
`
|
|
68
|
-
query GetVaultWithHistorical($where: Erc4626VaultFilter) {
|
|
69
|
-
erc4626Vaults(where: $where, limit: 1) {
|
|
70
|
-
items {
|
|
71
|
-
...VaultDetailFragment
|
|
72
|
-
...VaultHistoricalFragment
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
`,
|
|
77
|
-
[vaultDetailFragment, vaultHistoricalFragment],
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
export type GetDetailedVaultVariables = {
|
|
81
|
-
chainId: number
|
|
82
|
-
address: Address
|
|
83
|
-
/** Include historical data for charts (default: true) */
|
|
84
|
-
historical?: boolean
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Vault detail with Steakhouse metadata and optional historical data */
|
|
88
|
-
export type DetailedVault = VaultDetail & {
|
|
89
|
-
historical?: VaultHistoricalData | null
|
|
90
|
-
steakhouseMetadata: SteakhouseMetadata
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export type GetDetailedVaultResult = DetailedVault | null
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get full details for a single Steakhouse vault with optional historical data.
|
|
97
|
-
* Returns null if the vault is not in the whitelist.
|
|
98
|
-
*/
|
|
99
|
-
export async function getDetailedVault(
|
|
100
|
-
client: SteakhouseClient,
|
|
101
|
-
variables: GetDetailedVaultVariables,
|
|
102
|
-
): Promise<GetDetailedVaultResult> {
|
|
103
|
-
const { chainId, address, historical = true } = variables
|
|
104
|
-
|
|
105
|
-
const vaultConfig = STEAKHOUSE_VAULTS.find(
|
|
106
|
-
(v) => v.chainId === chainId && v.address.toLowerCase() === address.toLowerCase(),
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
if (!vaultConfig) {
|
|
110
|
-
return null
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const where = {
|
|
114
|
-
keys: [
|
|
115
|
-
{
|
|
116
|
-
chainId,
|
|
117
|
-
vaultAddress: address,
|
|
118
|
-
protocol: vaultConfig.protocol,
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
}
|
|
122
|
-
const query = historical ? vaultWithHistoricalQuery : vaultQuery
|
|
123
|
-
const result = await client.query(query, { where })
|
|
124
|
-
const vault = result.erc4626Vaults.items[0]
|
|
125
|
-
|
|
126
|
-
if (!vault) {
|
|
127
|
-
return null
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return { ...vault, steakhouseMetadata: buildSteakhouseMetadata(vaultConfig) }
|
|
131
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
|
-
import { createMockClient } from "../../test/mocks.js"
|
|
3
|
-
import { getVaults } from "./getVaults.js"
|
|
4
|
-
|
|
5
|
-
// Mock the generated vaults module
|
|
6
|
-
vi.mock("../metadata/generated/vaults.js", () => ({
|
|
7
|
-
STEAKHOUSE_VAULTS: [
|
|
8
|
-
{
|
|
9
|
-
chainId: 1,
|
|
10
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
11
|
-
protocol: "morpho_v1",
|
|
12
|
-
name: "Steakhouse USDC",
|
|
13
|
-
description: "A USDC vault",
|
|
14
|
-
type: "Prime",
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
chainId: 1,
|
|
18
|
-
address: "0x2222222222222222222222222222222222222222",
|
|
19
|
-
protocol: "morpho_v1",
|
|
20
|
-
name: "Steakhouse ETH",
|
|
21
|
-
description: "An ETH vault",
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
chainId: 8453,
|
|
25
|
-
address: "0x3333333333333333333333333333333333333333",
|
|
26
|
-
protocol: "morpho_v2",
|
|
27
|
-
name: "Base Vault",
|
|
28
|
-
description: "A Base vault",
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
}))
|
|
32
|
-
|
|
33
|
-
function createMockVaultResponse(vaults: Array<{ vaultAddress: string }>) {
|
|
34
|
-
return {
|
|
35
|
-
erc4626Vaults: {
|
|
36
|
-
items: vaults.map((v) => ({
|
|
37
|
-
chain: { id: 1, name: "Ethereum", icon: "https://chain.icon" },
|
|
38
|
-
vaultAddress: v.vaultAddress,
|
|
39
|
-
name: "API Vault Name",
|
|
40
|
-
symbol: "VAULT",
|
|
41
|
-
decimals: 18,
|
|
42
|
-
asset: {
|
|
43
|
-
address: "0xasset",
|
|
44
|
-
symbol: "USDC",
|
|
45
|
-
name: "USD Coin",
|
|
46
|
-
icon: "https://icon.url",
|
|
47
|
-
priceUsd: 1.0,
|
|
48
|
-
decimals: 6,
|
|
49
|
-
},
|
|
50
|
-
totalAssets: { raw: "1000000000000", formatted: "1000000", usd: 1000000 },
|
|
51
|
-
totalLiquidity: { formatted: "500000", usd: 500000 },
|
|
52
|
-
apy: { base: 0.05, total: 0.08, rewards: [], fee: 0.1 },
|
|
53
|
-
performanceFee: 0.1,
|
|
54
|
-
feeRecipientAddress: "0xfee",
|
|
55
|
-
ownerAddress: "0xowner",
|
|
56
|
-
curatorAddress: "0xcurator",
|
|
57
|
-
guardianAddress: "0xguardian",
|
|
58
|
-
metadata: {
|
|
59
|
-
description: "A vault",
|
|
60
|
-
image: null,
|
|
61
|
-
forumLink: null,
|
|
62
|
-
curator: null,
|
|
63
|
-
curators: [],
|
|
64
|
-
},
|
|
65
|
-
marketAllocations: [],
|
|
66
|
-
riskAssessment: { steakhouse: { score: 0.8, rating: "A" } },
|
|
67
|
-
})),
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe("getVaults", () => {
|
|
73
|
-
beforeEach(() => {
|
|
74
|
-
vi.clearAllMocks()
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it("returns empty array when no vaults in whitelist match filter", async () => {
|
|
78
|
-
const client = createMockClient({ erc4626Vaults: { items: [] } })
|
|
79
|
-
|
|
80
|
-
// Filter for a chain with no vaults
|
|
81
|
-
const result = await getVaults(client, { chainId: 999 })
|
|
82
|
-
|
|
83
|
-
expect(result).toEqual([])
|
|
84
|
-
// Should not call API if no vaults match
|
|
85
|
-
expect(client.query).not.toHaveBeenCalled()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it("filters vaults by chainId", async () => {
|
|
89
|
-
const client = createMockClient(
|
|
90
|
-
createMockVaultResponse([
|
|
91
|
-
{ vaultAddress: "0x1111111111111111111111111111111111111111" },
|
|
92
|
-
{ vaultAddress: "0x2222222222222222222222222222222222222222" },
|
|
93
|
-
]),
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
const result = await getVaults(client, { chainId: 1 })
|
|
97
|
-
|
|
98
|
-
expect(result).toHaveLength(2)
|
|
99
|
-
expect(client.query).toHaveBeenCalledWith(
|
|
100
|
-
expect.anything(),
|
|
101
|
-
expect.objectContaining({
|
|
102
|
-
where: expect.objectContaining({
|
|
103
|
-
keys: expect.arrayContaining([
|
|
104
|
-
expect.objectContaining({
|
|
105
|
-
chainId: 1,
|
|
106
|
-
vaultAddress: "0x1111111111111111111111111111111111111111",
|
|
107
|
-
protocol: "morpho_v1",
|
|
108
|
-
}),
|
|
109
|
-
expect.objectContaining({
|
|
110
|
-
chainId: 1,
|
|
111
|
-
vaultAddress: "0x2222222222222222222222222222222222222222",
|
|
112
|
-
protocol: "morpho_v1",
|
|
113
|
-
}),
|
|
114
|
-
]),
|
|
115
|
-
}),
|
|
116
|
-
}),
|
|
117
|
-
)
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it("augments results with steakhouse metadata", async () => {
|
|
121
|
-
const client = createMockClient(
|
|
122
|
-
createMockVaultResponse([{ vaultAddress: "0x1111111111111111111111111111111111111111" }]),
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
const result = await getVaults(client, { chainId: 1 })
|
|
126
|
-
|
|
127
|
-
expect(result[0]?.steakhouseMetadata).toEqual({
|
|
128
|
-
name: "Steakhouse USDC",
|
|
129
|
-
description: "A USDC vault",
|
|
130
|
-
type: "Prime",
|
|
131
|
-
protocol: "morpho_v1",
|
|
132
|
-
})
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it("handles vaults without type", async () => {
|
|
136
|
-
const client = createMockClient(
|
|
137
|
-
createMockVaultResponse([{ vaultAddress: "0x3333333333333333333333333333333333333333" }]),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
const result = await getVaults(client, { chainId: 8453 })
|
|
141
|
-
|
|
142
|
-
expect(result[0]?.steakhouseMetadata).toEqual({
|
|
143
|
-
name: "Base Vault",
|
|
144
|
-
description: "A Base vault",
|
|
145
|
-
protocol: "morpho_v2",
|
|
146
|
-
})
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it("returns all vaults when no chainId filter", async () => {
|
|
150
|
-
const client = createMockClient(
|
|
151
|
-
createMockVaultResponse([
|
|
152
|
-
{ vaultAddress: "0x1111111111111111111111111111111111111111" },
|
|
153
|
-
{ vaultAddress: "0x2222222222222222222222222222222222222222" },
|
|
154
|
-
{ vaultAddress: "0x3333333333333333333333333333333333333333" },
|
|
155
|
-
]),
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
await getVaults(client)
|
|
159
|
-
|
|
160
|
-
expect(client.query).toHaveBeenCalledWith(
|
|
161
|
-
expect.anything(),
|
|
162
|
-
expect.objectContaining({
|
|
163
|
-
limit: 3,
|
|
164
|
-
}),
|
|
165
|
-
)
|
|
166
|
-
})
|
|
167
|
-
})
|
package/src/queries/getVaults.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { graphql } from "@whisk/graphql"
|
|
2
|
-
import type { SteakhouseClient } from "../client.js"
|
|
3
|
-
import { STEAKHOUSE_VAULTS } from "../metadata/generated/vaults.js"
|
|
4
|
-
import type { Address, VaultConfig } from "../metadata/types.js"
|
|
5
|
-
import { type VaultDetail, vaultDetailFragment } from "./fragments/vaultDetail.js"
|
|
6
|
-
import { buildSteakhouseMetadata, type SteakhouseMetadata } from "./steakhouseMetadata.js"
|
|
7
|
-
|
|
8
|
-
/** GraphQL query for fetching Steakhouse vaults */
|
|
9
|
-
export const vaultsQuery = graphql(
|
|
10
|
-
`
|
|
11
|
-
query GetVaults($where: Erc4626VaultFilter, $limit: Int) {
|
|
12
|
-
erc4626Vaults(where: $where, limit: $limit) {
|
|
13
|
-
items {
|
|
14
|
-
...VaultDetailFragment
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
`,
|
|
19
|
-
[vaultDetailFragment],
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
export type GetVaultsVariables = {
|
|
23
|
-
/** Filter by chain ID */
|
|
24
|
-
chainId?: number
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Vault with Steakhouse metadata */
|
|
28
|
-
export type VaultWithMetadata = VaultDetail & {
|
|
29
|
-
steakhouseMetadata?: SteakhouseMetadata
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type GetVaultsResult = VaultWithMetadata[]
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get all Steakhouse-curated vaults.
|
|
36
|
-
* Results are filtered to the SDK whitelist and augmented with Steakhouse metadata.
|
|
37
|
-
*/
|
|
38
|
-
export async function getVaults(
|
|
39
|
-
client: SteakhouseClient,
|
|
40
|
-
variables: GetVaultsVariables = {},
|
|
41
|
-
): Promise<GetVaultsResult> {
|
|
42
|
-
const { chainId } = variables
|
|
43
|
-
|
|
44
|
-
const filteredVaults = chainId
|
|
45
|
-
? STEAKHOUSE_VAULTS.filter((v) => v.chainId === chainId)
|
|
46
|
-
: STEAKHOUSE_VAULTS
|
|
47
|
-
|
|
48
|
-
if (filteredVaults.length === 0) {
|
|
49
|
-
return []
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Build keys array for erc4626Vaults filter
|
|
53
|
-
const keys = filteredVaults.map((v) => ({
|
|
54
|
-
chainId: v.chainId,
|
|
55
|
-
vaultAddress: v.address,
|
|
56
|
-
protocol: v.protocol,
|
|
57
|
-
}))
|
|
58
|
-
|
|
59
|
-
const result = await client.query(vaultsQuery, {
|
|
60
|
-
where: { keys },
|
|
61
|
-
limit: keys.length,
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const metadataMap = new Map<Address, VaultConfig>(
|
|
65
|
-
filteredVaults.map((v) => [v.address.toLowerCase() as Address, v]),
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
return result.erc4626Vaults.items
|
|
69
|
-
.filter((vault): vault is NonNullable<typeof vault> => vault !== null)
|
|
70
|
-
.map((vault) => {
|
|
71
|
-
const config = metadataMap.get(vault.vaultAddress.toLowerCase() as Address)
|
|
72
|
-
if (config) {
|
|
73
|
-
return { ...vault, steakhouseMetadata: buildSteakhouseMetadata(config) }
|
|
74
|
-
}
|
|
75
|
-
return vault
|
|
76
|
-
})
|
|
77
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { VaultConfig } from "../metadata/types.js"
|
|
2
|
-
|
|
3
|
-
/** Steakhouse-specific metadata augmented onto vault data */
|
|
4
|
-
export type SteakhouseMetadata = {
|
|
5
|
-
name?: string
|
|
6
|
-
description?: string
|
|
7
|
-
type?: VaultConfig["type"]
|
|
8
|
-
protocol: VaultConfig["protocol"]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Build metadata object from vault config */
|
|
12
|
-
export function buildSteakhouseMetadata(config: VaultConfig): SteakhouseMetadata {
|
|
13
|
-
return {
|
|
14
|
-
protocol: config.protocol,
|
|
15
|
-
...(config.name !== undefined && { name: config.name }),
|
|
16
|
-
...(config.description !== undefined && { description: config.description }),
|
|
17
|
-
...(config.type !== undefined && { type: config.type }),
|
|
18
|
-
}
|
|
19
|
-
}
|
package/src/queries/types.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { UseQueryResult } from "@tanstack/react-query"
|
|
4
|
-
import {
|
|
5
|
-
type GetDetailedVaultResult,
|
|
6
|
-
type GetDetailedVaultVariables,
|
|
7
|
-
getDetailedVault,
|
|
8
|
-
} from "../../queries/getDetailedVault.js"
|
|
9
|
-
import { useSteakhouseQuery } from "./useSteakhouseQuery.js"
|
|
10
|
-
|
|
11
|
-
export function useDetailedVault(
|
|
12
|
-
variables: GetDetailedVaultVariables,
|
|
13
|
-
): UseQueryResult<GetDetailedVaultResult, Error> {
|
|
14
|
-
return useSteakhouseQuery({
|
|
15
|
-
queryName: "detailedVault",
|
|
16
|
-
queryFn: getDetailedVault,
|
|
17
|
-
variables,
|
|
18
|
-
})
|
|
19
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { type UseQueryResult, useQuery } from "@tanstack/react-query"
|
|
4
|
-
import type { SteakhouseQueryFn } from "../../queries/types.js"
|
|
5
|
-
import { useSteakhouse } from "../provider.js"
|
|
6
|
-
|
|
7
|
-
export function useSteakhouseQuery<TData, TVariables>(options: {
|
|
8
|
-
queryName: string
|
|
9
|
-
queryFn: SteakhouseQueryFn<TData, TVariables>
|
|
10
|
-
variables: TVariables
|
|
11
|
-
}): UseQueryResult<TData, Error> {
|
|
12
|
-
const { client } = useSteakhouse()
|
|
13
|
-
return useQuery({
|
|
14
|
-
queryKey: ["steakhouse", options.queryName, options.variables],
|
|
15
|
-
queryFn: () => options.queryFn(client, options.variables),
|
|
16
|
-
})
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { UseQueryResult } from "@tanstack/react-query"
|
|
4
|
-
import {
|
|
5
|
-
type GetVaultsResult,
|
|
6
|
-
type GetVaultsVariables,
|
|
7
|
-
getVaults,
|
|
8
|
-
} from "../../queries/getVaults.js"
|
|
9
|
-
import { useSteakhouseQuery } from "./useSteakhouseQuery.js"
|
|
10
|
-
|
|
11
|
-
export function useVaults(variables: GetVaultsVariables): UseQueryResult<GetVaultsResult, Error> {
|
|
12
|
-
return useSteakhouseQuery({
|
|
13
|
-
queryName: "vaults",
|
|
14
|
-
queryFn: getVaults,
|
|
15
|
-
variables,
|
|
16
|
-
})
|
|
17
|
-
}
|