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
|
@@ -8,6 +8,7 @@ import { Addresses } from 'viem/tempo'
|
|
|
8
8
|
|
|
9
9
|
import * as TestApp from '../../../../test/App.js'
|
|
10
10
|
import * as Runtime from '../../../../test/runtime.js'
|
|
11
|
+
import * as Tidx from '../../../internal/Tidx.js'
|
|
11
12
|
import * as Transfers from './transfers.js'
|
|
12
13
|
|
|
13
14
|
// Deterministic EUR-based rate set so conversion math is exact in assertions.
|
|
@@ -79,6 +80,35 @@ describe('GET /transfers', () => {
|
|
|
79
80
|
},
|
|
80
81
|
)
|
|
81
82
|
|
|
83
|
+
test.runIf(Runtime.get().mode === 'testnet')(
|
|
84
|
+
'returns an empty page for an address with only non-TIP-20 events',
|
|
85
|
+
async () => {
|
|
86
|
+
const client = TestApp.client({
|
|
87
|
+
tidx: { auth: process.env.TIDX_AUTH, baseUrl: Tidx.url[4217] },
|
|
88
|
+
})
|
|
89
|
+
const response = await client.v1.transfers.$get(
|
|
90
|
+
{
|
|
91
|
+
query: {
|
|
92
|
+
address: '0x9ef044cb9b80230b60a46b4ed3958672419e668f',
|
|
93
|
+
chainId: '4217',
|
|
94
|
+
limit: '10',
|
|
95
|
+
page: '1',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
TestApp.auth,
|
|
99
|
+
)
|
|
100
|
+
const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
101
|
+
|
|
102
|
+
expect(response.status).toBe(200)
|
|
103
|
+
expect(body).toMatchInlineSnapshot(`
|
|
104
|
+
{
|
|
105
|
+
"data": [],
|
|
106
|
+
"nextCursor": null,
|
|
107
|
+
}
|
|
108
|
+
`)
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
82
112
|
test('degrades unavailable verification data to null valuations', async () => {
|
|
83
113
|
const db = () =>
|
|
84
114
|
Db.postgres({
|
|
@@ -87,7 +117,13 @@ describe('GET /transfers', () => {
|
|
|
87
117
|
const client = TestApp.client({ db, fx: { oracle: fixed } })
|
|
88
118
|
|
|
89
119
|
const response = await client.v1.transfers.$get(
|
|
90
|
-
{
|
|
120
|
+
{
|
|
121
|
+
query: {
|
|
122
|
+
limit: '5',
|
|
123
|
+
token: TestApp.tokenWithHolders,
|
|
124
|
+
'valuation.currency': 'USD',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
91
127
|
TestApp.auth,
|
|
92
128
|
)
|
|
93
129
|
const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
@@ -383,6 +419,87 @@ describe('GET /transfers', () => {
|
|
|
383
419
|
expect(byRecipient.data.every((entry) => entry.recipient === recipient)).toBe(true)
|
|
384
420
|
})
|
|
385
421
|
|
|
422
|
+
test('paginates recipient scans through the real indexer', async () => {
|
|
423
|
+
const runtime = Runtime.get()
|
|
424
|
+
const recipient = Schema.Address.parse(
|
|
425
|
+
runtime.mode === 'testnet'
|
|
426
|
+
? '0xabb0023644c54a20d0fe92becfa764dacaf59ba9'
|
|
427
|
+
: runtime.fixtures!.fundedAccounts[0]!,
|
|
428
|
+
)
|
|
429
|
+
const tidx = Tidx.getClient({
|
|
430
|
+
chainId: runtime.chainId,
|
|
431
|
+
tidx:
|
|
432
|
+
runtime.mode === 'localnet'
|
|
433
|
+
? { baseUrl: runtime.tidxUrl }
|
|
434
|
+
: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
|
|
435
|
+
})
|
|
436
|
+
const bounds = runtime.mode === 'testnet' ? { toBlock: 27_608_730 } : {}
|
|
437
|
+
const reference = await Transfers.scan(
|
|
438
|
+
{ tidx },
|
|
439
|
+
{ chainId: runtime.chainId, limit: 2, order: 'desc', recipient, ...bounds },
|
|
440
|
+
)
|
|
441
|
+
const first = await Transfers.scan(
|
|
442
|
+
{ tidx },
|
|
443
|
+
{ chainId: runtime.chainId, limit: 1, order: 'desc', recipient, ...bounds },
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
expect(reference.items).toHaveLength(2)
|
|
447
|
+
expect(first.items).toHaveLength(1)
|
|
448
|
+
expect(first.nextCursor).not.toBeNull()
|
|
449
|
+
if (!first.nextCursor) return
|
|
450
|
+
|
|
451
|
+
const [cursor, positional] = await Promise.all([
|
|
452
|
+
Transfers.scan(
|
|
453
|
+
{ tidx },
|
|
454
|
+
{
|
|
455
|
+
chainId: runtime.chainId,
|
|
456
|
+
cursor: first.nextCursor,
|
|
457
|
+
limit: 1,
|
|
458
|
+
order: 'desc',
|
|
459
|
+
recipient,
|
|
460
|
+
...bounds,
|
|
461
|
+
},
|
|
462
|
+
),
|
|
463
|
+
Transfers.scan(
|
|
464
|
+
{ tidx },
|
|
465
|
+
{ chainId: runtime.chainId, limit: 1, order: 'desc', page: 2, recipient, ...bounds },
|
|
466
|
+
),
|
|
467
|
+
])
|
|
468
|
+
const ids = (page: Transfers.scan.Page) =>
|
|
469
|
+
page.items.map(({ data }) => `${data.transactionHash}-${data.logIndex}`)
|
|
470
|
+
const [sameAddress, senderAddress] = await Promise.all([
|
|
471
|
+
Transfers.scan(
|
|
472
|
+
{ tidx },
|
|
473
|
+
{
|
|
474
|
+
chainId: runtime.chainId,
|
|
475
|
+
eitherSide: recipient,
|
|
476
|
+
limit: 2,
|
|
477
|
+
order: 'desc',
|
|
478
|
+
recipient,
|
|
479
|
+
...bounds,
|
|
480
|
+
},
|
|
481
|
+
),
|
|
482
|
+
Transfers.scan(
|
|
483
|
+
{ tidx },
|
|
484
|
+
{
|
|
485
|
+
chainId: runtime.chainId,
|
|
486
|
+
eitherSide: first.items[0]!.data.sender,
|
|
487
|
+
limit: 1,
|
|
488
|
+
order: 'desc',
|
|
489
|
+
recipient,
|
|
490
|
+
...bounds,
|
|
491
|
+
},
|
|
492
|
+
),
|
|
493
|
+
])
|
|
494
|
+
|
|
495
|
+
expect(ids(first)).toEqual(ids(reference).slice(0, 1))
|
|
496
|
+
expect(ids(cursor)).toEqual(ids(reference).slice(1, 2))
|
|
497
|
+
expect(ids(positional)).toEqual(ids(reference).slice(1, 2))
|
|
498
|
+
expect(ids(sameAddress)).toEqual(ids(reference))
|
|
499
|
+
expect(ids(senderAddress)).toEqual(ids(first))
|
|
500
|
+
expect([...reference.items, ...cursor.items, ...positional.items].every(({ data }) => data.recipient === recipient && data.address.startsWith('0x20c'))).toBe(true) // prettier-ignore
|
|
501
|
+
})
|
|
502
|
+
|
|
386
503
|
test('rejects mixing `address` with `sender` or `recipient`', async () => {
|
|
387
504
|
const client = TestApp.client()
|
|
388
505
|
const response = await client.v1.transfers.$get(
|
|
@@ -640,7 +757,7 @@ describe('GET /transfers', () => {
|
|
|
640
757
|
)
|
|
641
758
|
|
|
642
759
|
test.skipIf(Runtime.get().mode !== 'localnet')(
|
|
643
|
-
'counts
|
|
760
|
+
'counts address-scoped cross-token transfers',
|
|
644
761
|
async () => {
|
|
645
762
|
const crossToken = Runtime.get().fixtures?.crossTokenTransfer
|
|
646
763
|
expect(crossToken).toBeDefined()
|
|
@@ -652,7 +769,7 @@ describe('GET /transfers', () => {
|
|
|
652
769
|
query: {
|
|
653
770
|
address: crossToken.sender,
|
|
654
771
|
include: 'crossToken,totalCount',
|
|
655
|
-
limit: '
|
|
772
|
+
limit: '200',
|
|
656
773
|
},
|
|
657
774
|
},
|
|
658
775
|
TestApp.auth,
|
|
@@ -661,9 +778,72 @@ describe('GET /transfers', () => {
|
|
|
661
778
|
|
|
662
779
|
expect(response.status).toBe(200)
|
|
663
780
|
expect(response.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(true)
|
|
664
|
-
expect(body.data.
|
|
665
|
-
expect(body.
|
|
666
|
-
expect(body.meta
|
|
781
|
+
expect(body.data.filter((entry) => entry.transactionHash === crossToken.hash)).toHaveLength(1)
|
|
782
|
+
expect(body.nextCursor).toBeNull()
|
|
783
|
+
expect(body.meta).toMatchObject({
|
|
784
|
+
totalCount: body.data.length,
|
|
785
|
+
totalCountCapped: false,
|
|
786
|
+
})
|
|
787
|
+
},
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
test.skipIf(Runtime.get().mode !== 'localnet')(
|
|
791
|
+
'applies address page offsets after merging both sides',
|
|
792
|
+
async () => {
|
|
793
|
+
const address = Runtime.get().fixtures?.crossTokenTransfer?.sender
|
|
794
|
+
expect(address).toBeDefined()
|
|
795
|
+
if (!address) return
|
|
796
|
+
|
|
797
|
+
const client = TestApp.client()
|
|
798
|
+
const [referenceResponse, firstResponse, secondResponse] = await Promise.all([
|
|
799
|
+
client.v1.transfers.$get({ query: { address, limit: '10' } }, TestApp.auth),
|
|
800
|
+
client.v1.transfers.$get({ query: { address, limit: '5' } }, TestApp.auth),
|
|
801
|
+
client.v1.transfers.$get({ query: { address, limit: '5', page: '2' } }, TestApp.auth),
|
|
802
|
+
])
|
|
803
|
+
const reference = await TestApp.json(
|
|
804
|
+
referenceResponse,
|
|
805
|
+
Transfers.schema.getTransfers.Response,
|
|
806
|
+
)
|
|
807
|
+
const first = await TestApp.json(firstResponse, Transfers.schema.getTransfers.Response)
|
|
808
|
+
const second = await TestApp.json(secondResponse, Transfers.schema.getTransfers.Response)
|
|
809
|
+
|
|
810
|
+
expect([referenceResponse.status, firstResponse.status, secondResponse.status]).toEqual([
|
|
811
|
+
200, 200, 200,
|
|
812
|
+
])
|
|
813
|
+
expect(reference.data).toHaveLength(10)
|
|
814
|
+
expect(first.data.map(({ id }) => id)).toEqual(reference.data.slice(0, 5).map(({ id }) => id))
|
|
815
|
+
expect(second.data.map(({ id }) => id)).toEqual(
|
|
816
|
+
reference.data.slice(5, 10).map(({ id }) => id),
|
|
817
|
+
)
|
|
818
|
+
},
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
test.skipIf(Runtime.get().mode !== 'localnet')(
|
|
822
|
+
'counts address-scoped plumbing from complete cross-token legs',
|
|
823
|
+
async () => {
|
|
824
|
+
const crossToken = Runtime.get().fixtures?.crossTokenTransfer
|
|
825
|
+
expect(crossToken).toBeDefined()
|
|
826
|
+
if (!crossToken) return
|
|
827
|
+
|
|
828
|
+
const client = TestApp.client()
|
|
829
|
+
const response = await client.v1.transfers.$get(
|
|
830
|
+
{
|
|
831
|
+
query: {
|
|
832
|
+
address: Addresses.stablecoinDex,
|
|
833
|
+
include: 'crossToken,totalCount',
|
|
834
|
+
limit: '50',
|
|
835
|
+
},
|
|
836
|
+
},
|
|
837
|
+
TestApp.auth,
|
|
838
|
+
)
|
|
839
|
+
const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
840
|
+
|
|
841
|
+
expect(response.status).toBe(200)
|
|
842
|
+
expect(body.data.some((entry) => entry.transactionHash === crossToken.hash)).toBe(false)
|
|
843
|
+
expect(body.meta).toMatchObject({
|
|
844
|
+
totalCount: body.data.length,
|
|
845
|
+
totalCountCapped: false,
|
|
846
|
+
})
|
|
667
847
|
},
|
|
668
848
|
)
|
|
669
849
|
|
|
@@ -694,12 +874,11 @@ describe('GET /transfers', () => {
|
|
|
694
874
|
},
|
|
695
875
|
)
|
|
696
876
|
|
|
697
|
-
test('
|
|
877
|
+
test('omits transfer valuations unless a denomination is requested', async () => {
|
|
698
878
|
const db = TestApp.database()
|
|
699
879
|
await TestApp.verifiedSeed(db, Runtime.get().chainId)
|
|
700
880
|
const client = TestApp.client({ db })
|
|
701
881
|
|
|
702
|
-
// pathUSD is curated USD, so its legs value at identity with no rates.
|
|
703
882
|
const response = await client.v1.transfers.$get(
|
|
704
883
|
{ query: { limit: '5', token: TestApp.tokenWithHolders } },
|
|
705
884
|
TestApp.auth,
|
|
@@ -708,12 +887,9 @@ describe('GET /transfers', () => {
|
|
|
708
887
|
|
|
709
888
|
expect(response.status).toBe(200)
|
|
710
889
|
expect(body.data.length).toBeGreaterThan(0)
|
|
711
|
-
for (const entry of body.data)
|
|
712
|
-
expect(entry.sourceAmount.valuation).toEqual({
|
|
713
|
-
amount: core_Value.format(BigInt(entry.sourceAmount.baseUnits), 6),
|
|
714
|
-
currency: 'USD',
|
|
715
|
-
})
|
|
890
|
+
for (const entry of body.data) expect('valuation' in entry.sourceAmount).toBe(false)
|
|
716
891
|
expect(body.meta?.valuation).toBeUndefined()
|
|
892
|
+
expect(response.headers.get('server-timing')).not.toContain('valuation_rates;dur=')
|
|
717
893
|
})
|
|
718
894
|
|
|
719
895
|
test('values transfer legs in a requested denomination', async () => {
|
|
@@ -774,7 +950,7 @@ describe('GET /transfers', () => {
|
|
|
774
950
|
(address) => address.toLowerCase() !== TestApp.tokenWithHolders,
|
|
775
951
|
)!
|
|
776
952
|
const response = await client.v1.transfers.$get(
|
|
777
|
-
{ query: { limit: '5', token } },
|
|
953
|
+
{ query: { limit: '5', token, 'valuation.currency': 'USD' } },
|
|
778
954
|
TestApp.auth,
|
|
779
955
|
)
|
|
780
956
|
const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
@@ -803,6 +979,96 @@ describe('GET /transfers', () => {
|
|
|
803
979
|
}
|
|
804
980
|
`)
|
|
805
981
|
})
|
|
982
|
+
|
|
983
|
+
test('bounds an unscoped transfer page by timestamp', async () => {
|
|
984
|
+
const client = TestApp.client()
|
|
985
|
+
const runtime = Runtime.get()
|
|
986
|
+
const tidx = Tidx.getClient({
|
|
987
|
+
chainId: runtime.chainId,
|
|
988
|
+
tidx:
|
|
989
|
+
runtime.mode === 'localnet'
|
|
990
|
+
? { baseUrl: runtime.tidxUrl }
|
|
991
|
+
: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
|
|
992
|
+
})
|
|
993
|
+
const latest = await Transfers.scan(
|
|
994
|
+
{ tidx },
|
|
995
|
+
{ chainId: runtime.chainId, limit: 1, order: 'desc' },
|
|
996
|
+
)
|
|
997
|
+
const latestTransfer = latest.items[0]?.data
|
|
998
|
+
if (latestTransfer === undefined) throw new Error('expected an indexed transfer')
|
|
999
|
+
|
|
1000
|
+
const block = await tidx.fetch({
|
|
1001
|
+
chainId: runtime.chainId,
|
|
1002
|
+
query: `SELECT timestamp FROM blocks WHERE num = ${latestTransfer.blockNumber} LIMIT 1`,
|
|
1003
|
+
})
|
|
1004
|
+
const seconds = block.rows[0]?.['timestamp']
|
|
1005
|
+
if (typeof seconds !== 'number') throw new Error('expected an indexed block timestamp')
|
|
1006
|
+
const timestamp = new Date(seconds * 1_000).toISOString()
|
|
1007
|
+
const response = await client.v1.transfers.$get(
|
|
1008
|
+
{
|
|
1009
|
+
query: {
|
|
1010
|
+
limit: '5',
|
|
1011
|
+
order: 'desc',
|
|
1012
|
+
'timestamp.from': timestamp,
|
|
1013
|
+
'timestamp.to': timestamp,
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
TestApp.auth,
|
|
1017
|
+
)
|
|
1018
|
+
const page = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
1019
|
+
|
|
1020
|
+
expect(response.status).toBe(200)
|
|
1021
|
+
expect(page.data.length).toBeGreaterThan(0)
|
|
1022
|
+
expect(page.data.every((item) => Date.parse(item.timestamp) === Date.parse(timestamp))).toBe(
|
|
1023
|
+
true,
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
const fractionalResponse = await client.v1.transfers.$get(
|
|
1027
|
+
{
|
|
1028
|
+
query: {
|
|
1029
|
+
limit: '5',
|
|
1030
|
+
order: 'desc',
|
|
1031
|
+
'timestamp.from': new Date(seconds * 1_000 + 500).toISOString(),
|
|
1032
|
+
'timestamp.to': new Date(seconds * 1_000 + 999).toISOString(),
|
|
1033
|
+
},
|
|
1034
|
+
},
|
|
1035
|
+
TestApp.auth,
|
|
1036
|
+
)
|
|
1037
|
+
const fractional = await TestApp.json(
|
|
1038
|
+
fractionalResponse,
|
|
1039
|
+
Transfers.schema.getTransfers.Response,
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
expect(fractionalResponse.status).toBe(200)
|
|
1043
|
+
expect(fractional.data).toEqual([])
|
|
1044
|
+
|
|
1045
|
+
const emptyResponse = await client.v1.transfers.$get(
|
|
1046
|
+
{
|
|
1047
|
+
query: {
|
|
1048
|
+
limit: '5',
|
|
1049
|
+
order: 'desc',
|
|
1050
|
+
'timestamp.to': '1970-01-01T00:00:00Z',
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
TestApp.auth,
|
|
1054
|
+
)
|
|
1055
|
+
const empty = await TestApp.json(emptyResponse, Transfers.schema.getTransfers.Response)
|
|
1056
|
+
|
|
1057
|
+
expect(emptyResponse.status).toBe(200)
|
|
1058
|
+
expect(empty.data).toEqual([])
|
|
1059
|
+
})
|
|
1060
|
+
|
|
1061
|
+
test('keeps the largest positional window within the indexer row limit', async () => {
|
|
1062
|
+
const client = TestApp.client()
|
|
1063
|
+
const response = await client.v1.transfers.$get(
|
|
1064
|
+
{ query: { limit: '200', order: 'desc', page: '50' } },
|
|
1065
|
+
TestApp.auth,
|
|
1066
|
+
)
|
|
1067
|
+
const page = await TestApp.json(response, Transfers.schema.getTransfers.Response)
|
|
1068
|
+
|
|
1069
|
+
expect(response.status).toBe(200)
|
|
1070
|
+
expect(page.data.length).toBeLessThanOrEqual(200)
|
|
1071
|
+
})
|
|
806
1072
|
})
|
|
807
1073
|
|
|
808
1074
|
describe('collate', () => {
|
|
@@ -816,7 +1082,11 @@ describe('collate', () => {
|
|
|
816
1082
|
test('passes a lone engine-paged stream through untouched', () => {
|
|
817
1083
|
const rows = [row(40, 4), row(20, 2)]
|
|
818
1084
|
expect(
|
|
819
|
-
Transfers.collate([{ rows, sample: false }], {
|
|
1085
|
+
Transfers.collate([{ capped: false, rows, sample: false }], {
|
|
1086
|
+
limit: 3,
|
|
1087
|
+
offset: 0,
|
|
1088
|
+
order: 'desc',
|
|
1089
|
+
}),
|
|
820
1090
|
).toEqual({
|
|
821
1091
|
capped: false,
|
|
822
1092
|
rows,
|
|
@@ -827,8 +1097,8 @@ describe('collate', () => {
|
|
|
827
1097
|
const shared = row(30, 3)
|
|
828
1098
|
const { capped, rows } = Transfers.collate(
|
|
829
1099
|
[
|
|
830
|
-
{ rows: [row(40, 4), shared], sample: false },
|
|
831
|
-
{ rows: [{ ...shared }, row(10, 1)], sample: false },
|
|
1100
|
+
{ capped: false, rows: [row(40, 4), shared], sample: false },
|
|
1101
|
+
{ capped: false, rows: [{ ...shared }, row(10, 1)], sample: false },
|
|
832
1102
|
],
|
|
833
1103
|
{ limit: 2, offset: 1, order: 'desc' },
|
|
834
1104
|
)
|
|
@@ -837,21 +1107,92 @@ describe('collate', () => {
|
|
|
837
1107
|
})
|
|
838
1108
|
|
|
839
1109
|
test('orders and pages a below-cap fallback sample', () => {
|
|
840
|
-
const { capped, rows } = Transfers.collate(
|
|
841
|
-
|
|
842
|
-
offset: 0,
|
|
843
|
-
|
|
844
|
-
})
|
|
1110
|
+
const { capped, rows } = Transfers.collate(
|
|
1111
|
+
[{ capped: false, rows: [row(10, 1), row(30, 3)], sample: true }],
|
|
1112
|
+
{ limit: 2, offset: 0, order: 'desc' },
|
|
1113
|
+
)
|
|
845
1114
|
expect(capped).toBe(false)
|
|
846
1115
|
expect(rows.map((entry) => entry['block_num'])).toEqual([30, 10])
|
|
847
1116
|
})
|
|
848
1117
|
|
|
1118
|
+
test('preserves the cap marker on an ordered stream', () => {
|
|
1119
|
+
const rows = [row(30, 3), row(10, 1)]
|
|
1120
|
+
expect(
|
|
1121
|
+
Transfers.collate([{ capped: true, rows, sample: false }], {
|
|
1122
|
+
limit: 2,
|
|
1123
|
+
offset: 0,
|
|
1124
|
+
order: 'desc',
|
|
1125
|
+
}),
|
|
1126
|
+
).toEqual({ capped: true, rows })
|
|
1127
|
+
})
|
|
1128
|
+
|
|
1129
|
+
test('preserves resumable progress on a lone ordered stream', () => {
|
|
1130
|
+
const rows = [row(30, 3), row(10, 1)]
|
|
1131
|
+
const resume = { offset: 4, position: [10, 1] as const }
|
|
1132
|
+
expect(
|
|
1133
|
+
Transfers.collate([{ capped: true, resume, rows, sample: false }], {
|
|
1134
|
+
limit: 2,
|
|
1135
|
+
offset: 0,
|
|
1136
|
+
order: 'desc',
|
|
1137
|
+
}),
|
|
1138
|
+
).toEqual({ capped: true, resume, rows })
|
|
1139
|
+
})
|
|
1140
|
+
|
|
1141
|
+
test('preserves common resumable progress across split ordered streams', () => {
|
|
1142
|
+
const result = Transfers.collate(
|
|
1143
|
+
[
|
|
1144
|
+
{
|
|
1145
|
+
capped: true,
|
|
1146
|
+
resume: { offset: 0, position: [20, 2] },
|
|
1147
|
+
rows: [row(50, 5), row(30, 3)],
|
|
1148
|
+
sample: false,
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
capped: true,
|
|
1152
|
+
resume: { offset: 0, position: [25, 2] },
|
|
1153
|
+
rows: [row(45, 4), row(25, 2)],
|
|
1154
|
+
sample: false,
|
|
1155
|
+
},
|
|
1156
|
+
],
|
|
1157
|
+
{ limit: 5, offset: 1, order: 'desc' },
|
|
1158
|
+
)
|
|
1159
|
+
expect(result).toEqual({
|
|
1160
|
+
capped: true,
|
|
1161
|
+
resume: { offset: 0, position: [25, 2] },
|
|
1162
|
+
rows: [row(45, 4), row(30, 3), row(25, 2)],
|
|
1163
|
+
})
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1166
|
+
test('returns a complete split window before the common resume point', () => {
|
|
1167
|
+
const result = Transfers.collate(
|
|
1168
|
+
[
|
|
1169
|
+
{
|
|
1170
|
+
capped: true,
|
|
1171
|
+
resume: { offset: 0, position: [20, 2] },
|
|
1172
|
+
rows: [row(50, 5), row(30, 3)],
|
|
1173
|
+
sample: false,
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
capped: true,
|
|
1177
|
+
resume: { offset: 0, position: [25, 2] },
|
|
1178
|
+
rows: [row(45, 4), row(25, 2)],
|
|
1179
|
+
sample: false,
|
|
1180
|
+
},
|
|
1181
|
+
],
|
|
1182
|
+
{ limit: 3, offset: 0, order: 'desc' },
|
|
1183
|
+
)
|
|
1184
|
+
expect(result).toEqual({
|
|
1185
|
+
capped: false,
|
|
1186
|
+
rows: [row(50, 5), row(45, 4), row(30, 3)],
|
|
1187
|
+
})
|
|
1188
|
+
})
|
|
1189
|
+
|
|
849
1190
|
test('flags a fallback sample that hit the cap', () => {
|
|
850
1191
|
const sample = Array.from({ length: Schema.countCap }, (_, index) => row(index + 1, index))
|
|
851
1192
|
const { capped, rows } = Transfers.collate(
|
|
852
1193
|
[
|
|
853
|
-
{ rows: [row(40_000, 20_000)], sample: false },
|
|
854
|
-
{ rows: sample, sample: true },
|
|
1194
|
+
{ capped: false, rows: [row(40_000, 20_000)], sample: false },
|
|
1195
|
+
{ capped: true, rows: sample, sample: true },
|
|
855
1196
|
],
|
|
856
1197
|
{ limit: 3, offset: 0, order: 'desc' },
|
|
857
1198
|
)
|
|
@@ -893,6 +1234,43 @@ describe('foldCount', () => {
|
|
|
893
1234
|
})
|
|
894
1235
|
})
|
|
895
1236
|
|
|
1237
|
+
test('classifies filtered plumbing rows from complete transaction legs', () => {
|
|
1238
|
+
const txHash = `0x${'dd'.repeat(32)}`
|
|
1239
|
+
const rows = [
|
|
1240
|
+
leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken),
|
|
1241
|
+
leg(txHash, 1, Addresses.stablecoinDex, swapper, destinationToken),
|
|
1242
|
+
leg(txHash, 2, swapper, recipient, destinationToken),
|
|
1243
|
+
leg(txHash, 3, swapper, Addresses.feeManager, sourceToken),
|
|
1244
|
+
]
|
|
1245
|
+
expect(
|
|
1246
|
+
Transfers.foldCount(rows.slice(0, 2), { capped: false, classificationRows: rows }),
|
|
1247
|
+
).toEqual({
|
|
1248
|
+
totalCount: 0,
|
|
1249
|
+
totalCountCapped: false,
|
|
1250
|
+
})
|
|
1251
|
+
})
|
|
1252
|
+
|
|
1253
|
+
test('uses log order when classifying complete transaction legs', () => {
|
|
1254
|
+
const txHash = `0x${'dd'.repeat(32)}`
|
|
1255
|
+
const thirdToken = '0x20c0000000000000000000000000000000000003'
|
|
1256
|
+
const rows = [
|
|
1257
|
+
leg(txHash, 1, swapper, Addresses.stablecoinDex, sourceToken),
|
|
1258
|
+
leg(txHash, 4, Addresses.stablecoinDex, swapper, destinationToken),
|
|
1259
|
+
leg(txHash, 5, swapper, Addresses.stablecoinDex, destinationToken),
|
|
1260
|
+
leg(txHash, 7, Addresses.stablecoinDex, swapper, thirdToken),
|
|
1261
|
+
]
|
|
1262
|
+
const delivery = leg(txHash, 8, swapper, recipient, thirdToken)
|
|
1263
|
+
expect(
|
|
1264
|
+
Transfers.foldCount(rows, {
|
|
1265
|
+
capped: false,
|
|
1266
|
+
classificationRows: [rows[2]!, rows[3]!, delivery, rows[0]!, rows[1]!],
|
|
1267
|
+
}),
|
|
1268
|
+
).toEqual({
|
|
1269
|
+
totalCount: 4,
|
|
1270
|
+
totalCountCapped: false,
|
|
1271
|
+
})
|
|
1272
|
+
})
|
|
1273
|
+
|
|
896
1274
|
test('keeps a cap-truncated sample conservative', () => {
|
|
897
1275
|
const txHash = `0x${'dd'.repeat(32)}`
|
|
898
1276
|
// A partial bundle: the DEX return leg fell outside the cap, defeating
|