tapimo 0.8.0 → 0.10.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.
- package/dist/App.d.ts +7410 -7613
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +0 -1
- package/dist/App.js.map +1 -1
- package/dist/Webhooks.d.ts +21 -20
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +66 -23
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +74 -74
- package/dist/apps/Zones.d.ts +34 -22
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +9 -8
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +3120 -2782
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +4 -5
- package/dist/apps/data/FxOracle.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.js +19 -4
- package/dist/apps/data/FxOracle.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +32 -27
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +283 -52
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +291 -21
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +232 -66
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +4 -4
- package/dist/apps/data/routes/coingecko.d.ts +54 -54
- package/dist/apps/data/routes/earn.d.ts +642 -126
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +627 -58
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +120 -120
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +114 -118
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +68 -47
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +2 -0
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +59 -51
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +25 -23
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +40 -18
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +618 -105
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +21 -21
- package/dist/apps/data/routes/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +2 -2
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +37 -37
- package/dist/apps/data/routes/webhooks.d.ts +102 -505
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +484 -342
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +12 -12
- package/dist/apps/data/routes/zones.d.ts.map +1 -1
- package/dist/apps/data/routes/zones.js +3 -1
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/funding/App.d.ts +127 -386
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/App.js +20 -5
- package/dist/apps/funding/App.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +141 -532
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +21 -103
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +4 -4
- package/dist/apps/funding/routes/transfers.js +5 -5
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +429 -727
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +24 -24
- package/dist/apps/management/routes/billing.d.ts +72 -72
- package/dist/apps/management/routes/faucet.d.ts +6 -6
- package/dist/apps/management/routes/invite-links.d.ts +38 -38
- package/dist/apps/management/routes/me.d.ts +6 -6
- package/dist/apps/management/routes/members.d.ts +30 -30
- package/dist/apps/management/routes/projects.d.ts +20 -20
- package/dist/apps/management/routes/usage.d.ts +24 -24
- package/dist/apps/management/routes/webhooks.d.ts +158 -573
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/mpp/App.d.ts +12 -0
- package/dist/apps/mpp/App.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.d.ts +3 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.js +3 -0
- package/dist/apps/mpp/Mpp.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +9 -2
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +2 -2
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +25 -15
- package/dist/db/Db.js.map +1 -1
- package/dist/db/tables/fundingDeposits.d.ts +18 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +46 -2
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +4 -1
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +56 -3
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +76 -3
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +4 -0
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +11 -2
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +112 -3
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +116 -3
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/Schema.js +1 -1
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Tidx.d.ts +1 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +1 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +2 -1
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/WebhookDestination.d.ts +2 -0
- package/dist/internal/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +11 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +55 -0
- package/dist/internal/WebhookTransfer.d.ts.map +1 -0
- package/dist/internal/WebhookTransfer.js +296 -0
- package/dist/internal/WebhookTransfer.js.map +1 -0
- package/dist/internal/Webhooks.d.ts +37 -0
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +87 -13
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +10 -6
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +11 -7
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +229 -16
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +3 -0
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +18 -3
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +3 -3
- package/src/App.test.ts +32 -0
- package/src/App.ts +4 -1
- package/src/Client.test-d.ts +116 -38
- package/src/Webhooks.bench.ts +714 -0
- package/src/Webhooks.memory.bench.ts +250 -0
- package/src/Webhooks.test.ts +77 -6
- package/src/Webhooks.ts +83 -29
- package/src/apps/Zones.test.ts +5 -4
- package/src/apps/Zones.ts +9 -8
- package/src/apps/composition.test.ts +39 -10
- package/src/apps/data/FxOracle.test.ts +18 -0
- package/src/apps/data/FxOracle.ts +28 -5
- package/src/apps/data/routes/activities.test.ts +19 -0
- package/src/apps/data/routes/activities.ts +34 -30
- package/src/apps/data/routes/balances.test.ts +161 -10
- package/src/apps/data/routes/balances.ts +339 -147
- package/src/apps/data/routes/blocks.test.ts +12 -6
- package/src/apps/data/routes/earn.test.ts +286 -9
- package/src/apps/data/routes/earn.ts +841 -58
- package/src/apps/data/routes/exchanges.test.ts +7 -6
- package/src/apps/data/routes/exchanges.ts +124 -117
- package/src/apps/data/routes/receipts.test.ts +24 -7
- package/src/apps/data/routes/receipts.ts +67 -50
- package/src/apps/data/routes/tokens.test.ts +51 -0
- package/src/apps/data/routes/transactions.test.ts +42 -0
- package/src/apps/data/routes/transactions.ts +44 -39
- package/src/apps/data/routes/transfers.test.ts +402 -24
- package/src/apps/data/routes/transfers.ts +708 -101
- package/src/apps/data/routes/valuation.test.ts +26 -0
- package/src/apps/data/routes/valuation.ts +6 -2
- package/src/apps/data/routes/webhooks.test.ts +414 -130
- package/src/apps/data/routes/webhooks.ts +603 -419
- package/src/apps/data/routes/zones.ts +6 -1
- package/src/apps/funding/App.ts +20 -5
- package/src/apps/funding/routes/deposit-addresses.test.ts +43 -22
- package/src/apps/funding/routes/deposit-addresses.ts +34 -117
- package/src/apps/funding/routes/quotes.test.ts +1 -1
- package/src/apps/funding/routes/transfers.test.ts +6 -25
- package/src/apps/funding/routes/transfers.ts +5 -5
- package/src/apps/mpp/Mpp.test.ts +13 -1
- package/src/apps/mpp/Mpp.ts +8 -0
- package/src/apps/mpp/internal/Adapter.ts +10 -2
- package/src/db/Db.test.ts +40 -6
- package/src/db/Db.ts +42 -19
- package/src/db/tables/fundingDeposits.test.ts +101 -0
- package/src/db/tables/fundingDeposits.ts +71 -2
- package/src/db/tables/sponsoredTransactions.ts +9 -2
- package/src/db/tables/webhookSubscriptions.ts +116 -4
- package/src/internal/Auth.test.ts +28 -0
- package/src/internal/Auth.ts +15 -1
- package/src/internal/EarnVaults.test.ts +2 -2
- package/src/internal/MetricSink.test.ts +518 -0
- package/src/internal/MetricSink.ts +266 -9
- package/src/internal/Schema.ts +1 -1
- package/src/internal/Tidx.test.ts +4 -4
- package/src/internal/Tidx.ts +1 -3
- package/src/internal/Viem.test.ts +8 -0
- package/src/internal/Viem.ts +2 -1
- package/src/internal/WebhookDestination.test.ts +20 -0
- package/src/internal/WebhookDestination.ts +10 -0
- package/src/internal/WebhookTransfer.ts +426 -0
- package/src/internal/Webhooks.test.ts +137 -0
- package/src/internal/Webhooks.ts +131 -17
- package/src/internal/funding/Provider.test.ts +47 -7
- package/src/internal/funding/Provider.ts +16 -11
- package/src/internal/funding/Reconciliation.local.test.ts +3 -1
- package/src/internal/funding/Reconciliation.test.ts +1532 -54
- package/src/internal/funding/Reconciliation.ts +296 -53
- package/src/internal/funding/SourceObservation.test.ts +3 -2
- package/src/internal/funding/providers/relay.test.ts +1 -4
- package/src/internal/funding/providers/relay.ts +23 -3
|
@@ -28,6 +28,7 @@ type OpenApiDocument = {
|
|
|
28
28
|
string,
|
|
29
29
|
{
|
|
30
30
|
get?: {
|
|
31
|
+
description?: string
|
|
31
32
|
parameters?: readonly {
|
|
32
33
|
description?: string
|
|
33
34
|
name?: string
|
|
@@ -87,6 +88,33 @@ describe('schema.getEarnVaults.Query', () => {
|
|
|
87
88
|
})
|
|
88
89
|
})
|
|
89
90
|
|
|
91
|
+
describe('schema.getEarnAddressPositions.Query', () => {
|
|
92
|
+
test('parses collection filters', () => {
|
|
93
|
+
const query = Earn.schema.getEarnAddressPositions.Query.parse({
|
|
94
|
+
limit: '5',
|
|
95
|
+
'valuation.currency': 'usd',
|
|
96
|
+
verified: 'true',
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(query.limit).toBe(5)
|
|
100
|
+
expect(query['valuation.currency']).toBe('USD')
|
|
101
|
+
expect(query.verified).toBe(true)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('defaults the page size', () => {
|
|
105
|
+
expect(Earn.schema.getEarnAddressPositions.Query.parse({}).limit).toBe(10)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('rejects vault-collection filters', () => {
|
|
109
|
+
expect(() =>
|
|
110
|
+
Earn.schema.getEarnAddressPositions.Query.parse({ capability: 'deposit' }),
|
|
111
|
+
).toThrow()
|
|
112
|
+
expect(() =>
|
|
113
|
+
Earn.schema.getEarnAddressPositions.Query.parse({ asset: `0x${'aa'.repeat(20)}` }),
|
|
114
|
+
).toThrow()
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
90
118
|
describe('schema.getEarnVaultEarnings.Response', () => {
|
|
91
119
|
test('enforces status-specific earnings fields', () => {
|
|
92
120
|
const response = {
|
|
@@ -109,12 +137,6 @@ describe('schema.getEarnVaultEarnings.Response', () => {
|
|
|
109
137
|
period: 'active',
|
|
110
138
|
status: 'complete',
|
|
111
139
|
}).success,
|
|
112
|
-
lifetime: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
113
|
-
...response,
|
|
114
|
-
lifetimeEarnings: '0',
|
|
115
|
-
period: 'lifetime',
|
|
116
|
-
status: 'complete',
|
|
117
|
-
}).success,
|
|
118
140
|
completeWithoutEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
119
141
|
...response,
|
|
120
142
|
period: 'lifetime',
|
|
@@ -125,12 +147,23 @@ describe('schema.getEarnVaultEarnings.Response', () => {
|
|
|
125
147
|
period: 'active',
|
|
126
148
|
status: 'incomplete_cost_basis',
|
|
127
149
|
}).success,
|
|
150
|
+
incompleteWindow: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
151
|
+
...response,
|
|
152
|
+
period: '30d',
|
|
153
|
+
status: 'incomplete_cost_basis',
|
|
154
|
+
}).success,
|
|
128
155
|
incompleteWithEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
129
156
|
...response,
|
|
130
157
|
lifetimeEarnings: '0',
|
|
131
158
|
period: 'lifetime',
|
|
132
159
|
status: 'incomplete_cost_basis',
|
|
133
160
|
}).success,
|
|
161
|
+
lifetime: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
162
|
+
...response,
|
|
163
|
+
lifetimeEarnings: '0',
|
|
164
|
+
period: 'lifetime',
|
|
165
|
+
status: 'complete',
|
|
166
|
+
}).success,
|
|
134
167
|
pending: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
135
168
|
...response,
|
|
136
169
|
period: 'lifetime',
|
|
@@ -141,34 +174,77 @@ describe('schema.getEarnVaultEarnings.Response', () => {
|
|
|
141
174
|
period: 'active',
|
|
142
175
|
status: 'pending_redemption',
|
|
143
176
|
}).success,
|
|
177
|
+
pendingWindow: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
178
|
+
...response,
|
|
179
|
+
period: '30d',
|
|
180
|
+
status: 'pending_redemption',
|
|
181
|
+
}).success,
|
|
144
182
|
pendingWithEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
145
183
|
...response,
|
|
146
184
|
lifetimeEarnings: '0',
|
|
147
185
|
period: 'lifetime',
|
|
148
186
|
status: 'pending_redemption',
|
|
149
187
|
}).success,
|
|
188
|
+
window: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
189
|
+
...response,
|
|
190
|
+
period: '30d',
|
|
191
|
+
status: 'complete',
|
|
192
|
+
windowEarnings: '0',
|
|
193
|
+
}).success,
|
|
194
|
+
windowWithActiveEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
195
|
+
...response,
|
|
196
|
+
activeEarnings: '0',
|
|
197
|
+
period: '30d',
|
|
198
|
+
status: 'complete',
|
|
199
|
+
}).success,
|
|
200
|
+
windowWithoutEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
|
|
201
|
+
...response,
|
|
202
|
+
period: '30d',
|
|
203
|
+
status: 'complete',
|
|
204
|
+
}).success,
|
|
150
205
|
}).toMatchInlineSnapshot(`
|
|
151
206
|
{
|
|
152
207
|
"active": true,
|
|
153
208
|
"activeWithLifetimeEarnings": false,
|
|
154
209
|
"completeWithoutEarnings": false,
|
|
155
210
|
"incomplete": true,
|
|
211
|
+
"incompleteWindow": true,
|
|
156
212
|
"incompleteWithEarnings": false,
|
|
157
213
|
"lifetime": true,
|
|
158
214
|
"pending": true,
|
|
159
215
|
"pendingActive": false,
|
|
216
|
+
"pendingWindow": true,
|
|
160
217
|
"pendingWithEarnings": false,
|
|
218
|
+
"window": true,
|
|
219
|
+
"windowWithActiveEarnings": false,
|
|
220
|
+
"windowWithoutEarnings": false,
|
|
161
221
|
}
|
|
162
222
|
`)
|
|
163
223
|
})
|
|
164
224
|
})
|
|
165
225
|
|
|
166
226
|
describe('schema.getEarnVaultEarnings.Query', () => {
|
|
167
|
-
test('defaults to lifetime and accepts active earnings', () => {
|
|
227
|
+
test('defaults to lifetime and accepts active and trailing earnings', () => {
|
|
168
228
|
expect([
|
|
169
229
|
Earn.schema.getEarnVaultEarnings.Query.parse({}).period,
|
|
230
|
+
Earn.schema.getEarnVaultEarnings.Query.parse({ period: '30d' }).period,
|
|
170
231
|
Earn.schema.getEarnVaultEarnings.Query.parse({ period: 'active' }).period,
|
|
171
|
-
]).toStrictEqual(['lifetime', 'active'])
|
|
232
|
+
]).toStrictEqual(['lifetime', '30d', 'active'])
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
describe('calculateWindowEarnings', () => {
|
|
237
|
+
test('adjusts snapshot growth for deposits and realized assets', () => {
|
|
238
|
+
expect(
|
|
239
|
+
Earn.calculateWindowEarnings({
|
|
240
|
+
deposited: 50n,
|
|
241
|
+
endingValue: 130n,
|
|
242
|
+
finalized: 10n,
|
|
243
|
+
openingValue: 100n,
|
|
244
|
+
redeemed: 15n,
|
|
245
|
+
withdrewExact: 5n,
|
|
246
|
+
}),
|
|
247
|
+
).toBe(10n)
|
|
172
248
|
})
|
|
173
249
|
})
|
|
174
250
|
|
|
@@ -491,6 +567,144 @@ describe('GET /v1/earn/vaults/verified', () => {
|
|
|
491
567
|
)
|
|
492
568
|
})
|
|
493
569
|
|
|
570
|
+
describe('GET /v1/earn/addresses/:address/positions', () => {
|
|
571
|
+
test('returns chain selection errors', async () => {
|
|
572
|
+
const app = TestApp.create({ auth: false, cache })
|
|
573
|
+
const invalid = await app.request(
|
|
574
|
+
`/v1/earn/addresses/${TestEarn.positionAddress}/positions?chainId=abc`,
|
|
575
|
+
)
|
|
576
|
+
const unsupported = await app.request(
|
|
577
|
+
`/v1/earn/addresses/${TestEarn.positionAddress}/positions?chainId=999999`,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
expect([
|
|
581
|
+
(await TestApp.json(invalid, Schema.ErrorResponse)).error.code,
|
|
582
|
+
(await TestApp.json(unsupported, Schema.ErrorResponse)).error.code,
|
|
583
|
+
]).toStrictEqual(['chain_id_invalid', 'chain_id_unsupported'])
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
test('rejects a malformed account address', async () => {
|
|
587
|
+
const response = await TestApp.create({ auth: false, cache }).request(
|
|
588
|
+
'/v1/earn/addresses/not-an-address/positions',
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
expect(response.status).toBe(400)
|
|
592
|
+
expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
|
|
593
|
+
error: { code: 'positions_invalid' },
|
|
594
|
+
})
|
|
595
|
+
})
|
|
596
|
+
|
|
597
|
+
test('returns an empty verified page without curated vaults', async () => {
|
|
598
|
+
const app = TestApp.create({ auth: false, cache })
|
|
599
|
+
const response = await app.request(
|
|
600
|
+
`/v1/earn/addresses/${TestEarn.positionAddress}/positions?verified=true`,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
expect(response.status).toBe(200)
|
|
604
|
+
expect(
|
|
605
|
+
await TestApp.json(response, Earn.schema.getEarnAddressPositions.Response),
|
|
606
|
+
).toStrictEqual({ data: [], nextCursor: null })
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
test.runIf(Runtime.get().mode === 'testnet')(
|
|
610
|
+
'lists non-zero verified positions for an account',
|
|
611
|
+
async () => {
|
|
612
|
+
const db = TestApp.database()
|
|
613
|
+
// Valuation resolves through the curated verified list, so seed it.
|
|
614
|
+
await TestApp.verifiedSeed(db, TestEarn.chain.id)
|
|
615
|
+
await core_EarnVaults.upsert(db, TestEarn.verifiedVault)
|
|
616
|
+
await core_EarnVaults.upsert(db, {
|
|
617
|
+
...TestEarn.verifiedVault,
|
|
618
|
+
description: 'Active-earnings Earn deployment.',
|
|
619
|
+
label: 'Active btPATHUSD Earn',
|
|
620
|
+
vaultAddress: TestEarn.activeEarningsVaultAddress,
|
|
621
|
+
})
|
|
622
|
+
const app = TestApp.create({ auth: false, cache, db, zones: [TestEarn.zone] })
|
|
623
|
+
const response = await app.request(
|
|
624
|
+
`/v1/earn/addresses/${TestEarn.activeEarningsAddress}/positions?chainId=${TestEarn.chain.id}&verified=true&valuation.currency=USD`,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
expect(response.status).toBe(200)
|
|
628
|
+
const body = await TestApp.json(response, Earn.schema.getEarnAddressPositions.Response)
|
|
629
|
+
expect(body.data.every((position) => BigInt(position.shareAmount.amount) > 0n)).toBe(true)
|
|
630
|
+
const row = body.data.find(
|
|
631
|
+
(position) => position.vaultAddress === TestEarn.activeEarningsVaultAddress,
|
|
632
|
+
)
|
|
633
|
+
expect(row).toBeDefined()
|
|
634
|
+
if (!row) return
|
|
635
|
+
// Amounts render in their own token's display currency and precision.
|
|
636
|
+
expect(row.shareAmount.currency).toBe(row.shareToken.currency)
|
|
637
|
+
expect(row.shareAmount.decimals).toBe(row.shareToken.decimals)
|
|
638
|
+
expect(row.assetAmount.currency).toBe(row.assetToken.currency)
|
|
639
|
+
expect(row.assetAmount.decimals).toBe(row.assetToken.decimals)
|
|
640
|
+
// A USD-denominated asset values identically without consulting rates.
|
|
641
|
+
expect(row.valuation).toStrictEqual({ amount: row.assetAmount.formatted, currency: 'USD' })
|
|
642
|
+
expect(body.meta).toBeUndefined()
|
|
643
|
+
expect({
|
|
644
|
+
assetToken: row.assetToken.address,
|
|
645
|
+
formattedShape: /^\d+(\.\d+)?$/.test(row.assetAmount.formatted),
|
|
646
|
+
id: row.id,
|
|
647
|
+
sharesHeld: BigInt(row.shareAmount.amount) > 0n,
|
|
648
|
+
valued: BigInt(row.assetAmount.amount) > 0n,
|
|
649
|
+
vaultAddress: row.vaultAddress,
|
|
650
|
+
verified: row.verified,
|
|
651
|
+
}).toMatchInlineSnapshot(`
|
|
652
|
+
{
|
|
653
|
+
"assetToken": "0x20c0000000000000000000000000000000000001",
|
|
654
|
+
"formattedShape": true,
|
|
655
|
+
"id": "0x3e4405801a1bee1b58394228ea0b44b667e222fc",
|
|
656
|
+
"sharesHeld": true,
|
|
657
|
+
"valued": true,
|
|
658
|
+
"vaultAddress": "0x3e4405801a1bee1b58394228ea0b44b667e222fc",
|
|
659
|
+
"verified": true,
|
|
660
|
+
}
|
|
661
|
+
`)
|
|
662
|
+
},
|
|
663
|
+
60_000,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
test.runIf(Runtime.get().mode === 'testnet')(
|
|
667
|
+
'discovers held vaults from indexed share balances',
|
|
668
|
+
async () => {
|
|
669
|
+
const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
|
|
670
|
+
const response = await app.request(
|
|
671
|
+
`/v1/earn/addresses/${TestEarn.activeEarningsAddress}/positions?chainId=${TestEarn.chain.id}`,
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
expect(response.status).toBe(200)
|
|
675
|
+
const body = await TestApp.json(response, Earn.schema.getEarnAddressPositions.Response)
|
|
676
|
+
expect(body.data.map((position) => position.vaultAddress)).toContain(
|
|
677
|
+
TestEarn.activeEarningsVaultAddress,
|
|
678
|
+
)
|
|
679
|
+
expect(
|
|
680
|
+
body.data.every(
|
|
681
|
+
(position) =>
|
|
682
|
+
position.id === position.vaultAddress &&
|
|
683
|
+
!position.verified &&
|
|
684
|
+
/^\d+$/.test(position.assetAmount.amount) &&
|
|
685
|
+
BigInt(position.shareAmount.amount) > 0n &&
|
|
686
|
+
position.assetToken.address.startsWith('0x20c0') &&
|
|
687
|
+
position.shareToken.address.startsWith('0x20c0'),
|
|
688
|
+
),
|
|
689
|
+
).toBe(true)
|
|
690
|
+
|
|
691
|
+
// An address-keyset cursor excludes every vault at or before it.
|
|
692
|
+
const cursor = Cursor.encode([TestEarn.activeEarningsVaultAddress])
|
|
693
|
+
const paged = await app.request(
|
|
694
|
+
`/v1/earn/addresses/${TestEarn.activeEarningsAddress}/positions?chainId=${TestEarn.chain.id}&cursor=${encodeURIComponent(cursor)}`,
|
|
695
|
+
)
|
|
696
|
+
expect(paged.status).toBe(200)
|
|
697
|
+
const pagedBody = await TestApp.json(paged, Earn.schema.getEarnAddressPositions.Response)
|
|
698
|
+
expect(
|
|
699
|
+
pagedBody.data.every(
|
|
700
|
+
(position) => position.vaultAddress > TestEarn.activeEarningsVaultAddress,
|
|
701
|
+
),
|
|
702
|
+
).toBe(true)
|
|
703
|
+
},
|
|
704
|
+
90_000,
|
|
705
|
+
)
|
|
706
|
+
})
|
|
707
|
+
|
|
494
708
|
describe('GET /v1/earn/vaults/:vaultId', () => {
|
|
495
709
|
test('rejects a malformed vault address', async () => {
|
|
496
710
|
const app = TestApp.create({ auth: false, cache })
|
|
@@ -1098,6 +1312,30 @@ describe('GET /v1/earn/vaults/:vaultId/earnings/:address', () => {
|
|
|
1098
1312
|
60_000,
|
|
1099
1313
|
)
|
|
1100
1314
|
|
|
1315
|
+
test.runIf(Runtime.get().mode === 'testnet')(
|
|
1316
|
+
'returns cash-flow-adjusted trailing earnings',
|
|
1317
|
+
async () => {
|
|
1318
|
+
const app = TestApp.create({ auth: false, cache })
|
|
1319
|
+
const response = await app.request(
|
|
1320
|
+
`/v1/earn/vaults/${TestEarn.activeEarningsVaultAddress}/earnings/${TestEarn.activeEarningsAddress}?chainId=${TestEarn.chain.id}&period=30d`,
|
|
1321
|
+
)
|
|
1322
|
+
|
|
1323
|
+
expect(response.status).toBe(200)
|
|
1324
|
+
const earnings = await TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
|
|
1325
|
+
if (earnings.status !== 'complete' || earnings.period !== '30d')
|
|
1326
|
+
throw new Error('Expected complete trailing earnings history.')
|
|
1327
|
+
expect(earnings).toMatchObject({
|
|
1328
|
+
account: TestEarn.activeEarningsAddress,
|
|
1329
|
+
period: '30d',
|
|
1330
|
+
status: 'complete',
|
|
1331
|
+
windowEarnings: expect.stringMatching(/^-?\d+$/),
|
|
1332
|
+
})
|
|
1333
|
+
expect(earnings).not.toHaveProperty('activeEarnings')
|
|
1334
|
+
expect(earnings).not.toHaveProperty('lifetimeEarnings')
|
|
1335
|
+
},
|
|
1336
|
+
60_000,
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1101
1339
|
test.runIf(Runtime.get().mode === 'testnet')(
|
|
1102
1340
|
'omits lifetime earnings when venue shares make the cost basis incomplete',
|
|
1103
1341
|
async () => {
|
|
@@ -1302,6 +1540,7 @@ describe('OpenAPI', () => {
|
|
|
1302
1540
|
|
|
1303
1541
|
expect(Object.keys(spec.paths)).toEqual(
|
|
1304
1542
|
expect.arrayContaining([
|
|
1543
|
+
'/v1/earn/addresses/{address}/positions',
|
|
1305
1544
|
'/v1/earn/vaults',
|
|
1306
1545
|
'/v1/earn/vaults/verified',
|
|
1307
1546
|
'/v1/earn/vaults/{vaultId}',
|
|
@@ -1373,18 +1612,56 @@ describe('OpenAPI', () => {
|
|
|
1373
1612
|
expect(vaultId?.schema?.examples).toStrictEqual([TestEarn.mainnetVaultAddress])
|
|
1374
1613
|
})
|
|
1375
1614
|
|
|
1615
|
+
test('documents address-position inputs and responses', async () => {
|
|
1616
|
+
const app = TestApp.create({ auth: false, cache })
|
|
1617
|
+
const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
|
|
1618
|
+
const operation = spec.paths['/v1/earn/addresses/{address}/positions']?.get
|
|
1619
|
+
const address = operation?.parameters?.find((parameter) => parameter.name === 'address')
|
|
1620
|
+
const denomination = operation?.parameters?.find(
|
|
1621
|
+
(parameter) => parameter.name === 'valuation.currency',
|
|
1622
|
+
)
|
|
1623
|
+
const verified = operation?.parameters?.find((parameter) => parameter.name === 'verified')
|
|
1624
|
+
const response = JSON.stringify(operation?.responses)
|
|
1625
|
+
|
|
1626
|
+
expect(operation?.description).toContain('currently holds shares')
|
|
1627
|
+
expect(address?.schema?.examples).toStrictEqual([TestEarn.positionAddress])
|
|
1628
|
+
expect(denomination?.description).toContain('nominal value')
|
|
1629
|
+
expect(verified?.description).toContain('registry-curated')
|
|
1630
|
+
expect(verified?.schema?.examples).toStrictEqual([true])
|
|
1631
|
+
expect(response).toContain('"chain_id_invalid"')
|
|
1632
|
+
expect(response).toContain('"chain_id_unsupported"')
|
|
1633
|
+
expect(response).toContain('"positions_invalid"')
|
|
1634
|
+
expect(response).toContain('"query_invalid"')
|
|
1635
|
+
expect(response).toContain('Stable resource ID for this position')
|
|
1636
|
+
expect(response).toContain('Current asset value of the held shares')
|
|
1637
|
+
expect(response).toContain('"assetAmount"')
|
|
1638
|
+
expect(response).toContain('"shareAmount"')
|
|
1639
|
+
expect(response).toContain('"valuation"')
|
|
1640
|
+
expect(response).toContain('"vaultAddress"')
|
|
1641
|
+
expect(response).toContain('"nextCursor"')
|
|
1642
|
+
// Token references embed the trimmed token object, not a bare address.
|
|
1643
|
+
expect(response).toContain('"symbol"')
|
|
1644
|
+
})
|
|
1645
|
+
|
|
1376
1646
|
test('documents vault-earnings variants', async () => {
|
|
1377
1647
|
const app = TestApp.create({ auth: false, cache })
|
|
1378
1648
|
const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
|
|
1379
1649
|
const operation = spec.paths['/v1/earn/vaults/{vaultId}/earnings/{address}']?.get
|
|
1650
|
+
const period = operation?.parameters?.find((parameter) => parameter.name === 'period')
|
|
1380
1651
|
const response = JSON.stringify(operation?.responses)
|
|
1381
1652
|
|
|
1653
|
+
expect(operation?.description).toContain('the trailing 30 days')
|
|
1654
|
+
expect(period?.description).toContain('trailing 30 days')
|
|
1655
|
+
expect(period?.schema?.enum).toStrictEqual(['30d', 'active', 'lifetime'])
|
|
1656
|
+
expect(period?.schema?.examples).toStrictEqual(['lifetime'])
|
|
1382
1657
|
expect(response).toContain('"earn_vault_not_found"')
|
|
1383
1658
|
expect(response).toContain('Stable resource ID for these vault earnings')
|
|
1384
1659
|
expect(response).toContain('Current share value plus completed redemptions')
|
|
1385
1660
|
expect(response).toContain('weighted-average asset cost basis')
|
|
1386
|
-
expect(response).toContain('
|
|
1661
|
+
expect(response).toContain('Ending share value plus realized assets')
|
|
1662
|
+
expect(response).toContain('cannot reconstruct the asset cost basis for the requested period')
|
|
1387
1663
|
expect(response).toContain('"active"')
|
|
1664
|
+
expect(response).toContain('"30d"')
|
|
1388
1665
|
expect(response).toContain('"incomplete_cost_basis"')
|
|
1389
1666
|
expect(response).toContain('"pending_redemption"')
|
|
1390
1667
|
})
|