tapimo 0.11.0 → 0.13.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 +6396 -4575
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +39 -4
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +15 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +7 -0
- package/dist/Assets.js.map +1 -1
- package/dist/Scope.d.ts +2 -0
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +17 -0
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +176 -78
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +84 -4
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +54 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +123 -9
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +60 -7
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +4 -2
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +90 -90
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +71 -71
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/Zones.d.ts +16 -16
- package/dist/apps/Zones.js +4 -4
- package/dist/apps/data/App.d.ts +1699 -749
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +58 -9
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +62 -0
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
- package/dist/apps/data/ZoneSelection.js +44 -0
- package/dist/apps/data/ZoneSelection.js.map +1 -0
- package/dist/apps/data/routes/activities.d.ts +2417 -50
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +1896 -1293
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +471 -340
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +12 -12
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +44 -17
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +42 -42
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +22 -12
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +179 -179
- package/dist/apps/data/routes/earn.d.ts +144 -19
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +160 -28
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +719 -13
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +16 -13
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +9 -11
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +34 -34
- package/dist/apps/data/routes/receipts.d.ts +363 -263
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +29 -11
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.js +24 -10
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +13 -13
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +16 -15
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +112 -98
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +85 -35
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +7 -2
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +38 -18
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +1 -1
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +25 -21
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- package/dist/apps/data/routes/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +12 -7
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +3 -3
- package/dist/apps/data/routes/webhooks.d.ts +15 -15
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +2 -2
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +32 -32
- package/dist/apps/data/routes/zones.js +1 -1
- package/dist/apps/funding/App.d.ts +38 -27
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.js +5 -4
- package/dist/apps/funding/routes/chains.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +16 -16
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +24 -19
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts +13 -13
- package/dist/apps/funding/routes/providers.d.ts.map +1 -1
- package/dist/apps/funding/routes/providers.js +3 -2
- package/dist/apps/funding/routes/providers.js.map +1 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
- package/dist/apps/funding/routes/quotes.js +2 -2
- package/dist/apps/funding/routes/quotes.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +60 -48
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
- package/dist/apps/funding/routes/transfers.js +27 -19
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +4558 -3700
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +19 -6
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +786 -175
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +312 -34
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +103 -30
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +91 -32
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +23 -12
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +46 -13
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +79 -29
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +120 -75
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +100 -41
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +5 -5
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +14 -10
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +99 -78
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +37 -18
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +48 -8
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +48 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +108 -46
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +72 -21
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +8 -5
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +82 -82
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +41 -31
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +35 -7
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +36 -17
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +200 -183
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +80 -35
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +12 -0
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +52 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +26 -3
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +24 -5
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +5 -0
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/fundingCatalog.d.ts +4 -2
- package/dist/db/tables/fundingCatalog.d.ts.map +1 -1
- package/dist/db/tables/fundingCatalog.js +5 -1
- package/dist/db/tables/fundingCatalog.js.map +1 -1
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +10 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
- package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueCompletions.js +32 -0
- package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
- package/dist/db/tables/webhookQueueEvents.d.ts +14 -20
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +68 -47
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +16 -5
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +29 -5
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +9 -0
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/internal/Auth.d.ts +11 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +19 -6
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +11 -7
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +43 -4
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +167 -0
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/Fetch.d.ts +23 -0
- package/dist/internal/Fetch.d.ts.map +1 -0
- package/dist/internal/Fetch.js +26 -0
- package/dist/internal/Fetch.js.map +1 -0
- package/dist/internal/Log.d.ts +3 -1
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +16 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +30 -24
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +140 -41
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +14 -9
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +16 -5
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +11 -10
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +14 -9
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +28 -6
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +121 -25
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +2 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +23 -6
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Timeout.d.ts +36 -0
- package/dist/internal/Timeout.d.ts.map +1 -0
- package/dist/internal/Timeout.js +37 -0
- package/dist/internal/Timeout.js.map +1 -0
- package/dist/internal/Timing.d.ts +9 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +7 -0
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +3 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +29 -12
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +37 -20
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Action.d.ts +9 -0
- package/dist/internal/funding/Action.d.ts.map +1 -1
- package/dist/internal/funding/Action.js +16 -8
- package/dist/internal/funding/Action.js.map +1 -1
- package/dist/internal/funding/Catalog.d.ts +9 -2
- package/dist/internal/funding/Catalog.d.ts.map +1 -1
- package/dist/internal/funding/Catalog.js +19 -8
- package/dist/internal/funding/Catalog.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +2 -2
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +9 -8
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/DepositAddress.d.ts +4 -4
- package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
- package/dist/internal/funding/DepositAddress.js +9 -8
- package/dist/internal/funding/DepositAddress.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +68 -13
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +42 -20
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Transfer.d.ts +13 -12
- package/dist/internal/funding/Transfer.d.ts.map +1 -1
- package/dist/internal/funding/Transfer.js +23 -21
- package/dist/internal/funding/Transfer.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +38 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
- package/dist/internal/funding/providers/stargate.js +277 -0
- package/dist/internal/funding/providers/stargate.js.map +1 -0
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -0
- package/dist/internal/index.js.map +1 -1
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +3 -3
- package/src/App.test.ts +148 -0
- package/src/App.ts +45 -4
- package/src/Assets.test.ts +18 -0
- package/src/Assets.ts +22 -0
- package/src/Client.test-d.ts +311 -16
- package/src/Scope.test.ts +20 -0
- package/src/Scope.ts +16 -0
- package/src/admin/apps/api-keys.test.ts +75 -0
- package/src/admin/apps/api-keys.ts +64 -0
- package/src/admin/apps/earn-vaults.test.ts +143 -0
- package/src/admin/apps/earn-vaults.ts +84 -7
- package/src/admin/apps/organizations.ts +6 -2
- package/src/admin/ui/src/lib/api-keys.ts +19 -0
- package/src/admin/ui/src/routes/api-keys.tsx +125 -2
- package/src/admin/ui.generated.ts +71 -71
- package/src/apps/Zones.test.ts +4 -4
- package/src/apps/Zones.ts +4 -4
- package/src/apps/composition.test.ts +1 -1
- package/src/apps/data/App.test.ts +72 -0
- package/src/apps/data/App.ts +58 -10
- package/src/apps/data/ZoneSelection.test.ts +196 -0
- package/src/apps/data/ZoneSelection.ts +108 -0
- package/src/apps/data/routes/activities.test.ts +1843 -2
- package/src/apps/data/routes/activities.ts +2211 -1388
- package/src/apps/data/routes/balances.ts +134 -98
- package/src/apps/data/routes/blocks.test.ts +533 -0
- package/src/apps/data/routes/blocks.ts +36 -17
- package/src/apps/data/routes/earn.test.ts +187 -25
- package/src/apps/data/routes/earn.ts +228 -32
- package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
- package/src/apps/data/routes/exchanges.test.ts +31 -1
- package/src/apps/data/routes/exchanges.ts +19 -15
- package/src/apps/data/routes/fee-amm.ts +18 -11
- package/src/apps/data/routes/receipts.ts +48 -17
- package/src/apps/data/routes/rpc.test.ts +75 -0
- package/src/apps/data/routes/rpc.ts +23 -9
- package/src/apps/data/routes/tokenlist.ts +21 -18
- package/src/apps/data/routes/tokens.test.ts +607 -0
- package/src/apps/data/routes/tokens.ts +312 -231
- package/src/apps/data/routes/transactions.ts +69 -30
- package/src/apps/data/routes/transfers.test.ts +424 -3
- package/src/apps/data/routes/transfers.ts +57 -36
- package/src/apps/data/routes/valuation.ts +94 -78
- package/src/apps/data/routes/webhooks.ts +6 -2
- package/src/apps/data/routes/zones.test.ts +21 -15
- package/src/apps/data/routes/zones.ts +1 -1
- package/src/apps/funding/routes/chains.test.ts +211 -7
- package/src/apps/funding/routes/chains.ts +19 -12
- package/src/apps/funding/routes/deposit-addresses.test.ts +416 -0
- package/src/apps/funding/routes/deposit-addresses.ts +101 -84
- package/src/apps/funding/routes/providers.ts +9 -5
- package/src/apps/funding/routes/quotes.test.ts +42 -0
- package/src/apps/funding/routes/quotes.ts +8 -5
- package/src/apps/funding/routes/transfers.test.ts +430 -40
- package/src/apps/funding/routes/transfers.ts +76 -59
- package/src/apps/management/Billing.test.ts +26 -0
- package/src/apps/management/Billing.ts +24 -7
- package/src/apps/management/routes/api-keys.test.ts +398 -0
- package/src/apps/management/routes/api-keys.ts +481 -145
- package/src/apps/management/routes/billing.test.ts +209 -0
- package/src/apps/management/routes/billing.ts +254 -177
- package/src/apps/management/routes/faucet.test.ts +39 -0
- package/src/apps/management/routes/faucet.ts +75 -41
- package/src/apps/management/routes/invitations.test.ts +216 -0
- package/src/apps/management/routes/invitations.ts +186 -109
- package/src/apps/management/routes/invite-links.test.ts +263 -1
- package/src/apps/management/routes/invite-links.ts +295 -214
- package/src/apps/management/routes/me.test.ts +49 -0
- package/src/apps/management/routes/me.ts +42 -32
- package/src/apps/management/routes/members.test.ts +124 -1
- package/src/apps/management/routes/members.ts +81 -51
- package/src/apps/management/routes/orgs.test.ts +176 -0
- package/src/apps/management/routes/orgs.ts +101 -59
- package/src/apps/management/routes/projects.test.ts +183 -0
- package/src/apps/management/routes/projects.ts +124 -60
- package/src/apps/management/routes/scopes.test.ts +44 -0
- package/src/apps/management/routes/scopes.ts +37 -28
- package/src/apps/management/routes/usage.test.ts +92 -0
- package/src/apps/management/routes/usage.ts +186 -151
- package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
- package/src/apps/management/routes/verified-token-requests.ts +166 -133
- package/src/apps/management/routes/webhooks.test.ts +234 -0
- package/src/apps/management/routes/webhooks.ts +191 -78
- package/src/apps/mpp/internal/Adapter.ts +13 -1
- package/src/db/Db.test.ts +41 -0
- package/src/db/Db.ts +75 -1
- package/src/db/Schema.ts +33 -5
- package/src/db/tables/earnVaults.test.ts +22 -0
- package/src/db/tables/earnVaults.ts +11 -1
- package/src/db/tables/fundingCatalog.test.ts +16 -2
- package/src/db/tables/fundingCatalog.ts +9 -3
- package/src/db/tables/webhookDeliveries.ts +13 -0
- package/src/db/tables/webhookQueueCompletions.ts +55 -0
- package/src/db/tables/webhookQueueEvents.ts +95 -61
- package/src/db/tables/webhookSubscriptions.ts +45 -6
- package/src/handlers/internal/utils.test.ts +53 -0
- package/src/handlers/internal/utils.ts +15 -0
- package/src/internal/Auth.ts +35 -9
- package/src/internal/Cache.ts +14 -7
- package/src/internal/EarnVaults.capabilities.test.ts +33 -0
- package/src/internal/EarnVaults.test.ts +79 -0
- package/src/internal/EarnVaults.ts +277 -0
- package/src/internal/Fetch.test.ts +46 -0
- package/src/internal/Fetch.ts +56 -0
- package/src/internal/Log.test.ts +21 -0
- package/src/internal/Log.ts +20 -5
- package/src/internal/OpenApi.ts +177 -51
- package/src/internal/Response.test.ts +95 -0
- package/src/internal/Response.ts +41 -3
- package/src/internal/Schema.test.ts +18 -0
- package/src/internal/Schema.ts +19 -9
- package/src/internal/Store.test.ts +364 -0
- package/src/internal/Store.ts +153 -27
- package/src/internal/Tidx.test.ts +21 -1
- package/src/internal/Tidx.ts +23 -6
- package/src/internal/Timeout.test.ts +35 -0
- package/src/internal/Timeout.ts +73 -0
- package/src/internal/Timing.ts +16 -0
- package/src/internal/WebhookDestination.ts +3 -0
- package/src/internal/Webhooks.test.ts +122 -8
- package/src/internal/Webhooks.ts +59 -29
- package/src/internal/funding/Action.test.ts +41 -1
- package/src/internal/funding/Action.ts +69 -41
- package/src/internal/funding/Catalog.test.ts +34 -0
- package/src/internal/funding/Catalog.ts +28 -11
- package/src/internal/funding/Deposit.ts +71 -64
- package/src/internal/funding/DepositAddress.ts +50 -43
- package/src/internal/funding/Provider.test.ts +73 -0
- package/src/internal/funding/Provider.ts +246 -170
- package/src/internal/funding/Transfer.ts +133 -110
- package/src/internal/funding/providers/index.test.ts +22 -0
- package/src/internal/funding/providers/relay.test.ts +2 -2
- package/src/internal/funding/providers/stargate.test.ts +163 -0
- package/src/internal/funding/providers/stargate.ts +409 -0
- package/src/internal/index.test.ts +1 -0
- package/src/internal/index.ts +1 -0
- package/src/providers/index.test.ts +1 -0
- package/src/providers/index.ts +9 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Address, Hex, Value as core_Value } from 'ox'
|
|
4
4
|
import { FxOracle } from 'tapimo/apps'
|
|
5
5
|
import { createClient, encodeAbiParameters, encodeEventTopics, http } from 'viem'
|
|
6
|
-
import { sendTransactionSync } from 'viem/actions'
|
|
6
|
+
import { getLogs, sendTransactionSync } from 'viem/actions'
|
|
7
7
|
import { Abis, Actions, Addresses } from 'viem/tempo'
|
|
8
8
|
|
|
9
9
|
import * as TestApp from '../../../../test/App.js'
|
|
@@ -143,6 +143,9 @@ describe('GET /addresses/:address/activities', () => {
|
|
|
143
143
|
expect(
|
|
144
144
|
Activities.schema.getAddressActivities.Query.safeParse({ include: 'token' }).success,
|
|
145
145
|
).toBe(false)
|
|
146
|
+
expect(
|
|
147
|
+
Activities.schema.getAddressActivities.Query.parse({ include: 'zones' }).include,
|
|
148
|
+
).toStrictEqual(['zones'])
|
|
146
149
|
})
|
|
147
150
|
|
|
148
151
|
test('returns a newest-first, schema-valid feed with timing', async () => {
|
|
@@ -536,6 +539,7 @@ describe('GET /transactions/:transactionHash/activities', () => {
|
|
|
536
539
|
)
|
|
537
540
|
|
|
538
541
|
const body = await TestApp.json(response, Activities.schema.getTransactionActivities.Response)
|
|
542
|
+
expect(body.chainId).toBe(runtime.chainId)
|
|
539
543
|
expect(body.data.length).toBeGreaterThan(0)
|
|
540
544
|
// Every classified item belongs to the requested transaction.
|
|
541
545
|
expect(body.data.every((item) => item.transactionHash === transactionHash)).toBe(true)
|
|
@@ -607,6 +611,249 @@ describe('GET /transactions/:transactionHash/activities', () => {
|
|
|
607
611
|
const response = await transactionActivitiesRequest(`0x${'0'.repeat(64)}`)
|
|
608
612
|
expect(response.status).toBe(404)
|
|
609
613
|
})
|
|
614
|
+
|
|
615
|
+
test.skipIf(runtime.mode !== 'localnet')(
|
|
616
|
+
'infers indexed Zone activity by transaction and address',
|
|
617
|
+
async () => {
|
|
618
|
+
const account = Tempo.accounts[0]
|
|
619
|
+
const [zoneCreated] = await getLogs(Tempo.client, {
|
|
620
|
+
event: {
|
|
621
|
+
inputs: [
|
|
622
|
+
{ indexed: true, name: 'zoneId', type: 'uint32' },
|
|
623
|
+
{ indexed: true, name: 'portal', type: 'address' },
|
|
624
|
+
{ indexed: false, name: 'initialToken', type: 'address' },
|
|
625
|
+
{ indexed: false, name: 'admin', type: 'address' },
|
|
626
|
+
{ indexed: false, name: 'sequencer', type: 'address' },
|
|
627
|
+
{ indexed: false, name: 'verifier', type: 'address' },
|
|
628
|
+
{ indexed: false, name: 'genesisBlockHash', type: 'bytes32' },
|
|
629
|
+
{ indexed: false, name: 'genesisTempoBlockHash', type: 'bytes32' },
|
|
630
|
+
{ indexed: false, name: 'genesisTempoBlockNumber', type: 'uint64' },
|
|
631
|
+
],
|
|
632
|
+
name: 'ZoneCreated',
|
|
633
|
+
type: 'event',
|
|
634
|
+
},
|
|
635
|
+
fromBlock: 0n,
|
|
636
|
+
strict: true,
|
|
637
|
+
toBlock: 'latest',
|
|
638
|
+
})
|
|
639
|
+
expect(zoneCreated).toBeDefined()
|
|
640
|
+
if (!zoneCreated) return
|
|
641
|
+
|
|
642
|
+
const zone = TestApp.zone({
|
|
643
|
+
chainId: runtime.zone.chainId,
|
|
644
|
+
rpcUrl: runtime.zone.internalRpcUrl,
|
|
645
|
+
sourceChainId: runtime.chainId,
|
|
646
|
+
tidxUrl: runtime.tidxUrl,
|
|
647
|
+
})
|
|
648
|
+
const zoneClient = createClient({
|
|
649
|
+
account,
|
|
650
|
+
chain: zone,
|
|
651
|
+
pollingInterval: 100,
|
|
652
|
+
transport: http(runtime.zone.internalRpcUrl),
|
|
653
|
+
})
|
|
654
|
+
const balanceBefore = await Actions.token.getBalance(zoneClient, {
|
|
655
|
+
account: account.address,
|
|
656
|
+
token: Addresses.pathUsd,
|
|
657
|
+
})
|
|
658
|
+
const zoneDeposit = await Actions.zone.depositSync(Tempo.getClient({ account }), {
|
|
659
|
+
amount: 1_000_000n,
|
|
660
|
+
portalAddress: zoneCreated.args.portal,
|
|
661
|
+
token: Addresses.pathUsd,
|
|
662
|
+
zoneId: zoneCreated.args.zoneId,
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
let balance = balanceBefore
|
|
666
|
+
const balanceDeadline = Date.now() + 60_000
|
|
667
|
+
while (balance.amount <= balanceBefore.amount && Date.now() < balanceDeadline) {
|
|
668
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
669
|
+
balance = await Actions.token.getBalance(zoneClient, {
|
|
670
|
+
account: account.address,
|
|
671
|
+
token: Addresses.pathUsd,
|
|
672
|
+
})
|
|
673
|
+
}
|
|
674
|
+
expect(balance.amount).toBeGreaterThan(balanceBefore.amount)
|
|
675
|
+
|
|
676
|
+
const { receipt } = await Actions.token.transferSync(zoneClient, {
|
|
677
|
+
amount: 1n,
|
|
678
|
+
to: Tempo.accounts[1].address,
|
|
679
|
+
token: Addresses.pathUsd,
|
|
680
|
+
})
|
|
681
|
+
const zoneReader = {
|
|
682
|
+
...TestApp.key,
|
|
683
|
+
id: 'key_inferred_zone',
|
|
684
|
+
scopes: [...TestApp.key.scopes, `zone:${runtime.zone.chainId}:read`],
|
|
685
|
+
token: 'secret_inferred_zone',
|
|
686
|
+
} satisfies TestApp.kvStore.Key
|
|
687
|
+
const client = TestApp.client({
|
|
688
|
+
auth: { keys: [zoneReader] },
|
|
689
|
+
zones: [zone],
|
|
690
|
+
})
|
|
691
|
+
let response = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
692
|
+
{
|
|
693
|
+
param: { transactionHash: receipt.transactionHash },
|
|
694
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
695
|
+
},
|
|
696
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
697
|
+
)
|
|
698
|
+
const deadline = Date.now() + 60_000
|
|
699
|
+
while ((response.status === 404 || response.status === 502) && Date.now() < deadline) {
|
|
700
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
701
|
+
response = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
702
|
+
{
|
|
703
|
+
param: { transactionHash: receipt.transactionHash },
|
|
704
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
705
|
+
},
|
|
706
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
707
|
+
)
|
|
708
|
+
}
|
|
709
|
+
const body = await TestApp.json(response, Activities.schema.getTransactionActivities.Response)
|
|
710
|
+
|
|
711
|
+
expect(response.status).toBe(200)
|
|
712
|
+
expect(response.headers.get('cache-control')).toBe('no-store')
|
|
713
|
+
expect(body.chainId).toBe(runtime.zone.chainId)
|
|
714
|
+
const cached = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
715
|
+
{
|
|
716
|
+
param: { transactionHash: receipt.transactionHash },
|
|
717
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
718
|
+
},
|
|
719
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
720
|
+
)
|
|
721
|
+
expect(cached.status).toBe(200)
|
|
722
|
+
expect(cached.headers.get('server-timing')).not.toContain('tidx;dur=')
|
|
723
|
+
|
|
724
|
+
const zoneOnlyTransaction = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
725
|
+
{
|
|
726
|
+
param: { transactionHash: receipt.transactionHash },
|
|
727
|
+
query: { chainId: String(runtime.zone.chainId) },
|
|
728
|
+
},
|
|
729
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
730
|
+
)
|
|
731
|
+
const zoneOnlyTransactionBody = await TestApp.json(
|
|
732
|
+
zoneOnlyTransaction,
|
|
733
|
+
Activities.schema.getTransactionActivities.Response,
|
|
734
|
+
)
|
|
735
|
+
expect(zoneOnlyTransaction.status).toBe(200)
|
|
736
|
+
expect(zoneOnlyTransactionBody.chainId).toBe(runtime.zone.chainId)
|
|
737
|
+
|
|
738
|
+
let parentTransaction = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
739
|
+
{
|
|
740
|
+
param: { transactionHash: zoneDeposit.receipt.transactionHash },
|
|
741
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
742
|
+
},
|
|
743
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
744
|
+
)
|
|
745
|
+
const parentDeadline = Date.now() + 60_000
|
|
746
|
+
while (
|
|
747
|
+
(parentTransaction.status === 404 || parentTransaction.status === 502) &&
|
|
748
|
+
Date.now() < parentDeadline
|
|
749
|
+
) {
|
|
750
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
751
|
+
parentTransaction = await client.v1.transactions[':transactionHash'].activities.$get(
|
|
752
|
+
{
|
|
753
|
+
param: { transactionHash: zoneDeposit.receipt.transactionHash },
|
|
754
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
755
|
+
},
|
|
756
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
757
|
+
)
|
|
758
|
+
}
|
|
759
|
+
const parentTransactionBody = await TestApp.json(
|
|
760
|
+
parentTransaction,
|
|
761
|
+
Activities.schema.getTransactionActivities.Response,
|
|
762
|
+
)
|
|
763
|
+
expect(parentTransaction.status).toBe(200)
|
|
764
|
+
expect(parentTransactionBody.chainId).toBe(runtime.chainId)
|
|
765
|
+
|
|
766
|
+
const missingHash = `0x${'ff'.repeat(32)}` as Hex.Hex
|
|
767
|
+
const getMissing = () =>
|
|
768
|
+
client.v1.transactions[':transactionHash'].activities.$get(
|
|
769
|
+
{
|
|
770
|
+
param: { transactionHash: missingHash },
|
|
771
|
+
query: { chainId: String(runtime.chainId), include: 'zones' },
|
|
772
|
+
},
|
|
773
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
774
|
+
)
|
|
775
|
+
const missing = [await getMissing(), await getMissing()]
|
|
776
|
+
expect(missing.map(({ status }) => status)).toStrictEqual([404, 404])
|
|
777
|
+
expect(
|
|
778
|
+
missing.every((item) => item.headers.get('server-timing')?.includes('tidx;dur=')),
|
|
779
|
+
).toBe(true)
|
|
780
|
+
|
|
781
|
+
const transfer = body.data.find((item) => item.type === 'transfer')
|
|
782
|
+
expect(transfer?.transactionHash).toBe(receipt.transactionHash)
|
|
783
|
+
if (transfer?.type !== 'transfer') return
|
|
784
|
+
expect(transfer.data.recipient).toBe(Tempo.accounts[1].address.toLowerCase())
|
|
785
|
+
|
|
786
|
+
const zoneAddressActivities = await (async () => {
|
|
787
|
+
const deadline = Date.now() + 60_000
|
|
788
|
+
while (Date.now() < deadline) {
|
|
789
|
+
const response = await client.v1.addresses[':address'].activities.$get(
|
|
790
|
+
{
|
|
791
|
+
param: { address: account.address },
|
|
792
|
+
query: {
|
|
793
|
+
chainId: String(runtime.chainId),
|
|
794
|
+
include: 'zones',
|
|
795
|
+
limit: '50',
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
799
|
+
)
|
|
800
|
+
if (response.status === 200) {
|
|
801
|
+
const body = await TestApp.json(
|
|
802
|
+
response,
|
|
803
|
+
Activities.schema.getAddressActivities.Response,
|
|
804
|
+
)
|
|
805
|
+
const hashes = body.data.flatMap((item) =>
|
|
806
|
+
item.type === 'group'
|
|
807
|
+
? item.data.items.map(({ transactionHash }) => transactionHash)
|
|
808
|
+
: [item.transactionHash],
|
|
809
|
+
)
|
|
810
|
+
if (
|
|
811
|
+
hashes.includes(receipt.transactionHash) &&
|
|
812
|
+
hashes.includes(zoneDeposit.receipt.transactionHash)
|
|
813
|
+
)
|
|
814
|
+
return { body, response }
|
|
815
|
+
}
|
|
816
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
817
|
+
}
|
|
818
|
+
throw new Error('Timed out waiting for inferred Zone address activity')
|
|
819
|
+
})()
|
|
820
|
+
const addressTransfer = zoneAddressActivities.body.data.find(
|
|
821
|
+
(item) => item.type !== 'group' && item.transactionHash === receipt.transactionHash,
|
|
822
|
+
)
|
|
823
|
+
expect(zoneAddressActivities.response.headers.get('cache-control')).toBe('no-store')
|
|
824
|
+
expect(addressTransfer?.chainId).toBe(runtime.zone.chainId)
|
|
825
|
+
const parentActivity = zoneAddressActivities.body.data.find(
|
|
826
|
+
(item) =>
|
|
827
|
+
item.type !== 'group' && item.transactionHash === zoneDeposit.receipt.transactionHash,
|
|
828
|
+
)
|
|
829
|
+
expect(parentActivity?.chainId).toBe(runtime.chainId)
|
|
830
|
+
|
|
831
|
+
const zoneOnlyActivities = await client.v1.addresses[':address'].activities.$get(
|
|
832
|
+
{
|
|
833
|
+
param: { address: account.address },
|
|
834
|
+
query: { chainId: String(runtime.zone.chainId), limit: '50' },
|
|
835
|
+
},
|
|
836
|
+
{ headers: { authorization: `Bearer ${zoneReader.token}` } },
|
|
837
|
+
)
|
|
838
|
+
const zoneOnlyActivitiesBody = await TestApp.json(
|
|
839
|
+
zoneOnlyActivities,
|
|
840
|
+
Activities.schema.getAddressActivities.Response,
|
|
841
|
+
)
|
|
842
|
+
expect(zoneOnlyActivities.status).toBe(200)
|
|
843
|
+
expect(
|
|
844
|
+
zoneOnlyActivitiesBody.data.some(
|
|
845
|
+
(item) => item.type !== 'group' && item.transactionHash === receipt.transactionHash,
|
|
846
|
+
),
|
|
847
|
+
).toBe(true)
|
|
848
|
+
expect(
|
|
849
|
+
zoneOnlyActivitiesBody.data.some(
|
|
850
|
+
(item) =>
|
|
851
|
+
item.type !== 'group' && item.transactionHash === zoneDeposit.receipt.transactionHash,
|
|
852
|
+
),
|
|
853
|
+
).toBe(false)
|
|
854
|
+
},
|
|
855
|
+
180_000,
|
|
856
|
+
)
|
|
610
857
|
})
|
|
611
858
|
|
|
612
859
|
describe('Activities.list classification', () => {
|
|
@@ -627,6 +874,192 @@ describe('Activities.list classification', () => {
|
|
|
627
874
|
}
|
|
628
875
|
}
|
|
629
876
|
|
|
877
|
+
function pagedProvider(rows: Activities.Row[]): Activities.Provider {
|
|
878
|
+
return {
|
|
879
|
+
cursorForGroup: (group) => `${group.blockNum}:${group.txIdx}`,
|
|
880
|
+
fetchRows: async ({ cursor, limit }) => {
|
|
881
|
+
const [blockNum, txIdx] = cursor?.split(':').map(Number) ?? []
|
|
882
|
+
return {
|
|
883
|
+
rows: rows
|
|
884
|
+
.filter(
|
|
885
|
+
(row) =>
|
|
886
|
+
blockNum === undefined ||
|
|
887
|
+
row.blockNum < blockNum ||
|
|
888
|
+
(row.blockNum === blockNum && row.txIdx < txIdx!),
|
|
889
|
+
)
|
|
890
|
+
.slice(0, limit + 1),
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function unknownRow(options: {
|
|
897
|
+
blockNum: number
|
|
898
|
+
blockTimestamp: number
|
|
899
|
+
hash: string
|
|
900
|
+
}): Activities.Row {
|
|
901
|
+
return {
|
|
902
|
+
blockNum: options.blockNum,
|
|
903
|
+
blockTimestamp: options.blockTimestamp,
|
|
904
|
+
data: '0x',
|
|
905
|
+
feePayer: null,
|
|
906
|
+
logAddress: '0x2222222222222222222222222222222222222222',
|
|
907
|
+
logIdx: 0,
|
|
908
|
+
selector: null,
|
|
909
|
+
source: 'q1',
|
|
910
|
+
topic1: null,
|
|
911
|
+
topic2: null,
|
|
912
|
+
topic3: null,
|
|
913
|
+
txHash: `0x${options.hash.repeat(64)}` as Hex.Hex,
|
|
914
|
+
txIdx: 0,
|
|
915
|
+
txSender: viewer,
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
test('classifies every transfer row of a receipt-less (Q2) transaction', async () => {
|
|
920
|
+
const sender = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
921
|
+
const token = '0x2222222222222222222222222222222222222222' as Address.Address
|
|
922
|
+
const q2Row = (options: { amount: bigint; logIdx: number }): Activities.Row => ({
|
|
923
|
+
blockNum: 100,
|
|
924
|
+
blockTimestamp: 1_700_000_000,
|
|
925
|
+
data: null,
|
|
926
|
+
feePayer: null,
|
|
927
|
+
logAddress: token,
|
|
928
|
+
logIdx: options.logIdx,
|
|
929
|
+
selector: null,
|
|
930
|
+
source: 'q2',
|
|
931
|
+
topic1: null,
|
|
932
|
+
topic2: null,
|
|
933
|
+
topic3: null,
|
|
934
|
+
transferAmount: options.amount,
|
|
935
|
+
transferFrom: sender,
|
|
936
|
+
transferTo: viewer,
|
|
937
|
+
transferToken: token,
|
|
938
|
+
txHash: `0x${'e'.repeat(64)}` as Hex.Hex,
|
|
939
|
+
txIdx: 0,
|
|
940
|
+
txSender: sender,
|
|
941
|
+
})
|
|
942
|
+
const { items } = await Activities.list(
|
|
943
|
+
provider([q2Row({ amount: 10n, logIdx: 0 }), q2Row({ amount: 20n, logIdx: 1 })]),
|
|
944
|
+
{ address: viewer, store: store() },
|
|
945
|
+
)
|
|
946
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
947
|
+
expect(new Set(items.map((item) => item.id)).size).toBe(2)
|
|
948
|
+
})
|
|
949
|
+
|
|
950
|
+
test('merges chain pages and retains each provider cursor', async () => {
|
|
951
|
+
const providers = [
|
|
952
|
+
{
|
|
953
|
+
chainId: 10,
|
|
954
|
+
provider: pagedProvider([
|
|
955
|
+
unknownRow({ blockNum: 30, blockTimestamp: 300, hash: 'a' }),
|
|
956
|
+
unknownRow({ blockNum: 10, blockTimestamp: 100, hash: 'c' }),
|
|
957
|
+
]),
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
chainId: 20,
|
|
961
|
+
provider: pagedProvider([unknownRow({ blockNum: 20, blockTimestamp: 200, hash: 'b' })]),
|
|
962
|
+
},
|
|
963
|
+
]
|
|
964
|
+
const first = await Activities.listChains({
|
|
965
|
+
address: viewer,
|
|
966
|
+
limit: 2,
|
|
967
|
+
providers,
|
|
968
|
+
store: store(),
|
|
969
|
+
})
|
|
970
|
+
const second = await Activities.listChains({
|
|
971
|
+
address: viewer,
|
|
972
|
+
cursor: first.nextCursor!,
|
|
973
|
+
limit: 2,
|
|
974
|
+
providers,
|
|
975
|
+
store: store(),
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
expect(first.items.map((item) => [item.chainId, item.timestamp])).toStrictEqual([
|
|
979
|
+
[10, '1970-01-01T00:05:00.000Z'],
|
|
980
|
+
[20, '1970-01-01T00:03:20.000Z'],
|
|
981
|
+
])
|
|
982
|
+
expect(first.nextCursor).not.toBeNull()
|
|
983
|
+
expect(second.items.map((item) => item.chainId)).toStrictEqual([10])
|
|
984
|
+
expect(second.nextCursor).toBeNull()
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
test('retains unused chain buffers while filling a grouped page', async () => {
|
|
988
|
+
const signer = '0x3333333333333333333333333333333333333333' as Address.Address
|
|
989
|
+
const calls = new Map<number, number>()
|
|
990
|
+
const providers = [
|
|
991
|
+
{ chainId: 10, hashes: ['1', '3', '5'], timestamps: [600, 400, 200] },
|
|
992
|
+
{ chainId: 20, hashes: ['2', '4', '6'], timestamps: [500, 300, 100] },
|
|
993
|
+
].map(({ chainId, hashes, timestamps }) => {
|
|
994
|
+
const source = pagedProvider(
|
|
995
|
+
timestamps.map((blockTimestamp, index) => ({
|
|
996
|
+
...unknownRow({ blockNum: blockTimestamp, blockTimestamp, hash: hashes[index]! }),
|
|
997
|
+
feePayer: viewer,
|
|
998
|
+
txSender: signer,
|
|
999
|
+
})),
|
|
1000
|
+
)
|
|
1001
|
+
return {
|
|
1002
|
+
chainId,
|
|
1003
|
+
provider: {
|
|
1004
|
+
cursorForGroup: (group) => source.cursorForGroup(group),
|
|
1005
|
+
fetchRows: async (options) => {
|
|
1006
|
+
calls.set(chainId, (calls.get(chainId) ?? 0) + 1)
|
|
1007
|
+
return source.fetchRows(options)
|
|
1008
|
+
},
|
|
1009
|
+
} satisfies Activities.Provider,
|
|
1010
|
+
}
|
|
1011
|
+
})
|
|
1012
|
+
|
|
1013
|
+
const result = await Activities.listChains({
|
|
1014
|
+
address: viewer,
|
|
1015
|
+
group: true,
|
|
1016
|
+
limit: 2,
|
|
1017
|
+
providers,
|
|
1018
|
+
store: store(),
|
|
1019
|
+
})
|
|
1020
|
+
|
|
1021
|
+
expect(result.items.map((item) => item.timestamp)).toStrictEqual([
|
|
1022
|
+
'1970-01-01T00:10:00.000Z',
|
|
1023
|
+
'1970-01-01T00:08:20.000Z',
|
|
1024
|
+
'1970-01-01T00:06:40.000Z',
|
|
1025
|
+
'1970-01-01T00:05:00.000Z',
|
|
1026
|
+
'1970-01-01T00:03:20.000Z',
|
|
1027
|
+
'1970-01-01T00:01:40.000Z',
|
|
1028
|
+
])
|
|
1029
|
+
expect([...calls]).toStrictEqual([
|
|
1030
|
+
[10, 2],
|
|
1031
|
+
[20, 2],
|
|
1032
|
+
])
|
|
1033
|
+
expect(result.nextCursor).toBeNull()
|
|
1034
|
+
})
|
|
1035
|
+
|
|
1036
|
+
test('does not fold access-key activity across chains', async () => {
|
|
1037
|
+
const signer = '0x3333333333333333333333333333333333333333' as Address.Address
|
|
1038
|
+
const providers = [10, 20].map((chainId) => ({
|
|
1039
|
+
chainId,
|
|
1040
|
+
provider: pagedProvider([
|
|
1041
|
+
{
|
|
1042
|
+
...unknownRow({ blockNum: chainId, blockTimestamp: 300, hash: String(chainId / 10) }),
|
|
1043
|
+
feePayer: viewer,
|
|
1044
|
+
txSender: signer,
|
|
1045
|
+
},
|
|
1046
|
+
]),
|
|
1047
|
+
}))
|
|
1048
|
+
const result = await Activities.listChains({
|
|
1049
|
+
address: viewer,
|
|
1050
|
+
providers,
|
|
1051
|
+
store: store(),
|
|
1052
|
+
})
|
|
1053
|
+
const grouped = Activities.groupByAccessKey(
|
|
1054
|
+
result.items.map((item) => Activities.schema.Item.parse(item)),
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
expect(grouped.map((item) => [item.chainId, item.type])).toStrictEqual([
|
|
1058
|
+
[10, 'unknown'],
|
|
1059
|
+
[20, 'unknown'],
|
|
1060
|
+
])
|
|
1061
|
+
})
|
|
1062
|
+
|
|
630
1063
|
test('classifies an unrecognized Q1 tx as `unknown` with decoded events', async () => {
|
|
631
1064
|
// A gas-fee Transfer to the fee manager decodes as a known `Transfer`
|
|
632
1065
|
// event, but the classifier excludes fee-manager transfers, so the tx has
|
|
@@ -795,7 +1228,31 @@ describe('Activities.list precompile classification', () => {
|
|
|
795
1228
|
logIdx: number
|
|
796
1229
|
}): Activities.Row {
|
|
797
1230
|
type Param = { indexed?: boolean; name?: string; type: string }
|
|
798
|
-
|
|
1231
|
+
// Standard ERC-4626 events, mirrored from the classifier's decode ABI.
|
|
1232
|
+
const erc4626Events = [
|
|
1233
|
+
{
|
|
1234
|
+
inputs: [
|
|
1235
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1236
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1237
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1238
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1239
|
+
],
|
|
1240
|
+
name: 'Deposit',
|
|
1241
|
+
type: 'event',
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
inputs: [
|
|
1245
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1246
|
+
{ indexed: true, name: 'receiver', type: 'address' },
|
|
1247
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1248
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1249
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1250
|
+
],
|
|
1251
|
+
name: 'Withdraw',
|
|
1252
|
+
type: 'event',
|
|
1253
|
+
},
|
|
1254
|
+
] as const
|
|
1255
|
+
const abi = [...Abis.abis, ...Abis.earnRouter, ...Abis.earnVault, ...erc4626Events]
|
|
799
1256
|
const event = abi.find((a) => a.type === 'event' && a.name === options.eventName) as {
|
|
800
1257
|
inputs: readonly Param[]
|
|
801
1258
|
}
|
|
@@ -1483,4 +1940,1388 @@ describe('Activities.list precompile classification', () => {
|
|
|
1483
1940
|
if (items[0]!.type !== 'initialized') return
|
|
1484
1941
|
expect(items[0]!.data.height).toBe('42')
|
|
1485
1942
|
})
|
|
1943
|
+
|
|
1944
|
+
test('keeps duplicate identical payments as separate activities', async () => {
|
|
1945
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
1946
|
+
const items = await classify([
|
|
1947
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenA, value: 10n }),
|
|
1948
|
+
transferRow({ from: viewer, logIdx: 1, to: recipient, token: tokenA, value: 10n }),
|
|
1949
|
+
])
|
|
1950
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
1951
|
+
expect(new Set(items.map((item) => item.id)).size).toBe(2)
|
|
1952
|
+
})
|
|
1953
|
+
|
|
1954
|
+
test('folds zero-address transfer duplicates into explicit mint and burn events', async () => {
|
|
1955
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
1956
|
+
const minted = await classify([
|
|
1957
|
+
transferRow({ from: zero, logIdx: 0, to: viewer, token: tokenA, value: 10n }),
|
|
1958
|
+
logRow({ address: tokenA, args: { amount: 10n, to: viewer }, eventName: 'Mint', logIdx: 1 }),
|
|
1959
|
+
])
|
|
1960
|
+
const burned = await classify([
|
|
1961
|
+
transferRow({ from: viewer, logIdx: 0, to: zero, token: tokenA, value: 10n }),
|
|
1962
|
+
logRow({
|
|
1963
|
+
address: tokenA,
|
|
1964
|
+
args: { amount: 10n, from: viewer },
|
|
1965
|
+
eventName: 'Burn',
|
|
1966
|
+
logIdx: 1,
|
|
1967
|
+
}),
|
|
1968
|
+
])
|
|
1969
|
+
expect(minted.map((item) => item.type)).toEqual(['mint'])
|
|
1970
|
+
expect(burned.map((item) => item.type)).toEqual(['burn'])
|
|
1971
|
+
})
|
|
1972
|
+
|
|
1973
|
+
test('classifies a sender-initiated mint to another recipient', async () => {
|
|
1974
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
1975
|
+
const items = await classify([
|
|
1976
|
+
logRow({
|
|
1977
|
+
address: tokenA,
|
|
1978
|
+
args: { amount: 10n, to: recipient },
|
|
1979
|
+
eventName: 'Mint',
|
|
1980
|
+
logIdx: 0,
|
|
1981
|
+
}),
|
|
1982
|
+
])
|
|
1983
|
+
expect(items).toHaveLength(1)
|
|
1984
|
+
expect(items[0]?.type === 'mint' && items[0].data.recipient).toBe(recipient)
|
|
1985
|
+
})
|
|
1986
|
+
|
|
1987
|
+
test('keeps a payment batched after a share-burn withdrawal', async () => {
|
|
1988
|
+
const asset = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
1989
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
1990
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
1991
|
+
const items = await classify([
|
|
1992
|
+
transferRow({ from: viewer, logIdx: 0, to: zero, token: tokenA, value: 100n }),
|
|
1993
|
+
transferRow({ from: tokenA, logIdx: 1, to: viewer, token: asset, value: 100n }),
|
|
1994
|
+
transferRow({ from: viewer, logIdx: 2, to: recipient, token: asset, value: 100n }),
|
|
1995
|
+
])
|
|
1996
|
+
expect(items.map((item) => item.type)).toEqual(['burn', 'transfer', 'transfer'])
|
|
1997
|
+
const payment = items[2]
|
|
1998
|
+
expect(payment?.type).toBe('transfer')
|
|
1999
|
+
if (payment?.type !== 'transfer') return
|
|
2000
|
+
expect(payment.data.direction).toBe('out')
|
|
2001
|
+
expect(payment.data.recipient).toBe(recipient)
|
|
2002
|
+
})
|
|
2003
|
+
|
|
2004
|
+
test('folds vault token legs without hiding an independent payment', async () => {
|
|
2005
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2006
|
+
const recipient = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2007
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2008
|
+
const items = await classify([
|
|
2009
|
+
transferRow({ from: viewer, logIdx: 0, to: tokenA, token: asset, value: 100n }),
|
|
2010
|
+
transferRow({ from: zero, logIdx: 1, to: viewer, token: tokenA, value: 90n }),
|
|
2011
|
+
logRow({
|
|
2012
|
+
address: tokenA,
|
|
2013
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2014
|
+
eventName: 'Deposited',
|
|
2015
|
+
logIdx: 2,
|
|
2016
|
+
}),
|
|
2017
|
+
transferRow({ from: viewer, logIdx: 3, to: recipient, token: asset, value: 10n }),
|
|
2018
|
+
])
|
|
2019
|
+
expect(items.map((item) => item.type)).toEqual(['assets-deposited', 'transfer'])
|
|
2020
|
+
expect(items[1]?.type === 'transfer' && items[1].data.recipient).toBe(recipient)
|
|
2021
|
+
})
|
|
2022
|
+
|
|
2023
|
+
test('keeps an independent vault deposit beside a private Earn deposit', async () => {
|
|
2024
|
+
const actionId = `0x${'c'.repeat(64)}` as Hex.Hex
|
|
2025
|
+
const vaultB = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2026
|
+
const items = await classify([
|
|
2027
|
+
logRow({
|
|
2028
|
+
address: tokenA,
|
|
2029
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2030
|
+
eventName: 'Deposited',
|
|
2031
|
+
logIdx: 0,
|
|
2032
|
+
}),
|
|
2033
|
+
logRow({
|
|
2034
|
+
address: tokenA,
|
|
2035
|
+
args: {
|
|
2036
|
+
actionId,
|
|
2037
|
+
earnShares: 90n,
|
|
2038
|
+
earnVault: tokenA,
|
|
2039
|
+
inputAmount: 100n,
|
|
2040
|
+
inputToken: tokenA,
|
|
2041
|
+
vaultAssets: 100n,
|
|
2042
|
+
zoneDepositHash: actionId,
|
|
2043
|
+
},
|
|
2044
|
+
eventName: 'EarnDeposit',
|
|
2045
|
+
logIdx: 1,
|
|
2046
|
+
}),
|
|
2047
|
+
logRow({
|
|
2048
|
+
address: vaultB,
|
|
2049
|
+
args: { assets: 50n, caller: viewer, earnShares: 45n, receiver: viewer },
|
|
2050
|
+
eventName: 'Deposited',
|
|
2051
|
+
logIdx: 2,
|
|
2052
|
+
}),
|
|
2053
|
+
])
|
|
2054
|
+
expect(items.map((item) => item.type)).toEqual(['private-assets-deposited', 'assets-deposited'])
|
|
2055
|
+
expect(items[1]?.type === 'assets-deposited' && items[1].data.vault).toBe(vaultB)
|
|
2056
|
+
})
|
|
2057
|
+
|
|
2058
|
+
test('keeps two identical swaps through the same route distinct', async () => {
|
|
2059
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2060
|
+
const items = await classify([
|
|
2061
|
+
transferRow({ from: viewer, logIdx: 0, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2062
|
+
transferRow({ from: stablecoinDex, logIdx: 1, to: viewer, token: tokenB, value: 990n }),
|
|
2063
|
+
transferRow({ from: viewer, logIdx: 2, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2064
|
+
transferRow({ from: stablecoinDex, logIdx: 3, to: viewer, token: tokenB, value: 990n }),
|
|
2065
|
+
])
|
|
2066
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'swap'])
|
|
2067
|
+
expect(new Set(items.map((item) => item.id)).size).toBe(2)
|
|
2068
|
+
})
|
|
2069
|
+
|
|
2070
|
+
test('keeps an independent approval beside a swap', async () => {
|
|
2071
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2072
|
+
const tokenC = '0x6666666666666666666666666666666666666666' as Address.Address
|
|
2073
|
+
const spender = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2074
|
+
const items = await classify([
|
|
2075
|
+
logRow({
|
|
2076
|
+
address: tokenA,
|
|
2077
|
+
args: { amount: 1_000n, owner: viewer, spender },
|
|
2078
|
+
eventName: 'Approval',
|
|
2079
|
+
logIdx: 0,
|
|
2080
|
+
}),
|
|
2081
|
+
transferRow({ from: viewer, logIdx: 1, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2082
|
+
transferRow({ from: stablecoinDex, logIdx: 2, to: viewer, token: tokenB, value: 990n }),
|
|
2083
|
+
logRow({
|
|
2084
|
+
address: tokenC,
|
|
2085
|
+
args: { amount: 7n, owner: viewer, spender },
|
|
2086
|
+
eventName: 'Approval',
|
|
2087
|
+
logIdx: 3,
|
|
2088
|
+
}),
|
|
2089
|
+
])
|
|
2090
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'approval'])
|
|
2091
|
+
expect(items[1]?.type === 'approval' && items[1].data.sourceToken.address).toBe(tokenC)
|
|
2092
|
+
})
|
|
2093
|
+
|
|
2094
|
+
test('classifies a swap routed through a per-route account', async () => {
|
|
2095
|
+
const router = '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f' as Address.Address
|
|
2096
|
+
const executor = '0xccc88a9d1b4ed6b0eaba998850414b24f1c315be' as Address.Address
|
|
2097
|
+
const pool = '0x8888888888888888888888888888888888888888' as Address.Address
|
|
2098
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2099
|
+
// Route evidence: an undecoded event the router emits mid-swap.
|
|
2100
|
+
const routerLog: Activities.Row = {
|
|
2101
|
+
...transferRow({ from: viewer, logIdx: 2, to: router, token: tokenA, value: 0n }),
|
|
2102
|
+
data: '0x',
|
|
2103
|
+
logAddress: router,
|
|
2104
|
+
selector: `0x${'9'.repeat(64)}`,
|
|
2105
|
+
topic1: null,
|
|
2106
|
+
topic2: null,
|
|
2107
|
+
}
|
|
2108
|
+
const items = await classify([
|
|
2109
|
+
logRow({
|
|
2110
|
+
address: tokenA,
|
|
2111
|
+
args: { amount: 1_105n, owner: viewer, spender: executor },
|
|
2112
|
+
eventName: 'Approval',
|
|
2113
|
+
logIdx: 0,
|
|
2114
|
+
}),
|
|
2115
|
+
transferRow({ from: viewer, logIdx: 1, to: router, token: tokenA, value: 1_105n }),
|
|
2116
|
+
routerLog,
|
|
2117
|
+
transferRow({ from: router, logIdx: 3, to: pool, token: tokenA, value: 1_105n }),
|
|
2118
|
+
transferRow({ from: pool, logIdx: 4, to: router, token: tokenB, value: 1_104n }),
|
|
2119
|
+
transferRow({ from: router, logIdx: 5, to: viewer, token: tokenB, value: 1_104n }),
|
|
2120
|
+
])
|
|
2121
|
+
expect(items.map((item) => item.type)).toEqual(['swap'])
|
|
2122
|
+
expect(items[0]?.type).toBe('swap')
|
|
2123
|
+
if (items[0]?.type !== 'swap') return
|
|
2124
|
+
expect(items[0].data.sourceToken).toEqual({ address: tokenA, amount: '1105' })
|
|
2125
|
+
expect(items[0].data.destinationToken).toEqual({ address: tokenB, amount: '1104' })
|
|
2126
|
+
})
|
|
2127
|
+
|
|
2128
|
+
test('keeps payments through a passive counterparty as transfers', async () => {
|
|
2129
|
+
const merchant = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2130
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2131
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2132
|
+
const items = await classify([
|
|
2133
|
+
transferRow({ from: viewer, logIdx: 0, to: merchant, token: tokenA, value: 100n }),
|
|
2134
|
+
transferRow({ from: merchant, logIdx: 1, to: other, token: tokenA, value: 30n }),
|
|
2135
|
+
transferRow({ from: merchant, logIdx: 2, to: viewer, token: tokenB, value: 50n }),
|
|
2136
|
+
])
|
|
2137
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
2138
|
+
if (items[0]?.type !== 'transfer' || items[1]?.type !== 'transfer') return
|
|
2139
|
+
expect(items[0].data.direction).toBe('out')
|
|
2140
|
+
expect(items[1].data.direction).toBe('in')
|
|
2141
|
+
})
|
|
2142
|
+
|
|
2143
|
+
test('keeps interleaved DEX swaps by different swappers distinct', async () => {
|
|
2144
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2145
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2146
|
+
const items = await classify([
|
|
2147
|
+
transferRow({ from: viewer, logIdx: 0, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2148
|
+
transferRow({ from: other, logIdx: 1, to: stablecoinDex, token: tokenA, value: 500n }),
|
|
2149
|
+
transferRow({ from: stablecoinDex, logIdx: 2, to: viewer, token: tokenB, value: 990n }),
|
|
2150
|
+
transferRow({ from: stablecoinDex, logIdx: 3, to: other, token: tokenB, value: 495n }),
|
|
2151
|
+
])
|
|
2152
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'swap'])
|
|
2153
|
+
})
|
|
2154
|
+
|
|
2155
|
+
test('keeps a share-token payment beside a vault deposit of equal amounts', async () => {
|
|
2156
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2157
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2158
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2159
|
+
const items = await classify([
|
|
2160
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenA, value: 90n }),
|
|
2161
|
+
transferRow({ from: viewer, logIdx: 1, to: tokenA, token: asset, value: 100n }),
|
|
2162
|
+
transferRow({ from: zero, logIdx: 2, to: viewer, token: tokenA, value: 90n }),
|
|
2163
|
+
logRow({
|
|
2164
|
+
address: tokenA,
|
|
2165
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2166
|
+
eventName: 'Deposited',
|
|
2167
|
+
logIdx: 3,
|
|
2168
|
+
}),
|
|
2169
|
+
])
|
|
2170
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'assets-deposited'])
|
|
2171
|
+
expect(items[0]?.type === 'transfer' && items[0].data.recipient).toBe(recipient)
|
|
2172
|
+
})
|
|
2173
|
+
|
|
2174
|
+
test('keeps an inbound payment beside a refundless session close', async () => {
|
|
2175
|
+
const sender = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2176
|
+
const payment = transferRow({ from: sender, logIdx: 0, to: viewer, token: tokenA, value: 100n })
|
|
2177
|
+
const items = await classify([
|
|
2178
|
+
payment,
|
|
2179
|
+
{
|
|
2180
|
+
...payment,
|
|
2181
|
+
data: '0x',
|
|
2182
|
+
logAddress: channelReserve,
|
|
2183
|
+
logIdx: 1,
|
|
2184
|
+
selector: '0xf5a36fc00a96cbb9cf1f8f59299165e1d8ffffe94396d82904b4da524d16bbce',
|
|
2185
|
+
topic1: null,
|
|
2186
|
+
topic2: null,
|
|
2187
|
+
},
|
|
2188
|
+
])
|
|
2189
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'session-closed'])
|
|
2190
|
+
expect(items[1]?.type === 'session-closed' && items[1].data.refund).toBeUndefined()
|
|
2191
|
+
})
|
|
2192
|
+
|
|
2193
|
+
test('folds the router funding leg into a private Earn deposit', async () => {
|
|
2194
|
+
const actionId = `0x${'c'.repeat(64)}` as Hex.Hex
|
|
2195
|
+
const router = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2196
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2197
|
+
const items = await classify([
|
|
2198
|
+
transferRow({ from: viewer, logIdx: 0, to: router, token: asset, value: 100n }),
|
|
2199
|
+
logRow({
|
|
2200
|
+
address: router,
|
|
2201
|
+
args: {
|
|
2202
|
+
actionId,
|
|
2203
|
+
earnShares: 90n,
|
|
2204
|
+
earnVault: tokenA,
|
|
2205
|
+
inputAmount: 100n,
|
|
2206
|
+
inputToken: asset,
|
|
2207
|
+
vaultAssets: 100n,
|
|
2208
|
+
zoneDepositHash: actionId,
|
|
2209
|
+
},
|
|
2210
|
+
eventName: 'EarnDeposit',
|
|
2211
|
+
logIdx: 1,
|
|
2212
|
+
}),
|
|
2213
|
+
])
|
|
2214
|
+
expect(items.map((item) => item.type)).toEqual(['private-assets-deposited'])
|
|
2215
|
+
})
|
|
2216
|
+
|
|
2217
|
+
test('keeps a mint whose paired event decodes without a recipient', async () => {
|
|
2218
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2219
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2220
|
+
const malformedMint = logRow({
|
|
2221
|
+
address: tokenA,
|
|
2222
|
+
args: { amount: 10n, to: viewer },
|
|
2223
|
+
eventName: 'Mint',
|
|
2224
|
+
logIdx: 2,
|
|
2225
|
+
})
|
|
2226
|
+
const items = await classify([
|
|
2227
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenA, value: 5n }),
|
|
2228
|
+
transferRow({ from: zero, logIdx: 1, to: viewer, token: tokenA, value: 10n }),
|
|
2229
|
+
// The indexed recipient topic is stripped, so the Mint decodes without
|
|
2230
|
+
// `to` and must not fold away the zero-address transfer.
|
|
2231
|
+
{ ...malformedMint, topic1: null },
|
|
2232
|
+
])
|
|
2233
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'mint'])
|
|
2234
|
+
})
|
|
2235
|
+
|
|
2236
|
+
test('emits an external vault withdrawal paid out to the viewer', async () => {
|
|
2237
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2238
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2239
|
+
const items = await classify([
|
|
2240
|
+
transferRow({ from: tokenA, logIdx: 0, to: viewer, token: asset, value: 100n }),
|
|
2241
|
+
logRow({
|
|
2242
|
+
address: tokenA,
|
|
2243
|
+
args: { assets: 100n, owner: other, receiver: other, sender: other, shares: 90n },
|
|
2244
|
+
eventName: 'Withdraw',
|
|
2245
|
+
logIdx: 1,
|
|
2246
|
+
}),
|
|
2247
|
+
])
|
|
2248
|
+
expect(items.map((item) => item.type)).toEqual(['assets-withdrawn'])
|
|
2249
|
+
})
|
|
2250
|
+
|
|
2251
|
+
test('classifies a route delivery to a third party as a cross-token transfer', async () => {
|
|
2252
|
+
const router = '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f' as Address.Address
|
|
2253
|
+
const recipient = '0x7bc2d22a73706758505e222ed9d13a1a7f251490' as Address.Address
|
|
2254
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2255
|
+
const routerLog: Activities.Row = {
|
|
2256
|
+
...transferRow({ from: viewer, logIdx: 1, to: router, token: tokenA, value: 0n }),
|
|
2257
|
+
data: '0x',
|
|
2258
|
+
logAddress: router,
|
|
2259
|
+
selector: `0x${'9'.repeat(64)}`,
|
|
2260
|
+
topic1: null,
|
|
2261
|
+
topic2: null,
|
|
2262
|
+
}
|
|
2263
|
+
const items = await classify([
|
|
2264
|
+
transferRow({ from: viewer, logIdx: 0, to: router, token: tokenA, value: 38_129n }),
|
|
2265
|
+
routerLog,
|
|
2266
|
+
transferRow({ from: router, logIdx: 2, to: recipient, token: tokenB, value: 38_127n }),
|
|
2267
|
+
])
|
|
2268
|
+
expect(items.map((item) => item.type)).toEqual(['transfer'])
|
|
2269
|
+
const payment = items[0]
|
|
2270
|
+
if (payment?.type !== 'transfer') return
|
|
2271
|
+
expect(payment.data.recipient).toBe(recipient)
|
|
2272
|
+
expect(payment.data.sourceToken).toEqual({ address: tokenA, amount: '38129' })
|
|
2273
|
+
expect(payment.data.destinationToken).toEqual({ address: tokenB, amount: '38127' })
|
|
2274
|
+
})
|
|
2275
|
+
|
|
2276
|
+
test('folds the exact allowance granted to a vault into its deposit', async () => {
|
|
2277
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2278
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2279
|
+
const items = await classify([
|
|
2280
|
+
logRow({
|
|
2281
|
+
address: asset,
|
|
2282
|
+
args: { amount: 100n, owner: viewer, spender: tokenA },
|
|
2283
|
+
eventName: 'Approval',
|
|
2284
|
+
logIdx: 0,
|
|
2285
|
+
}),
|
|
2286
|
+
transferRow({ from: viewer, logIdx: 1, to: tokenA, token: asset, value: 100n }),
|
|
2287
|
+
transferRow({ from: zero, logIdx: 2, to: viewer, token: tokenA, value: 90n }),
|
|
2288
|
+
logRow({
|
|
2289
|
+
address: tokenA,
|
|
2290
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2291
|
+
eventName: 'Deposited',
|
|
2292
|
+
logIdx: 3,
|
|
2293
|
+
}),
|
|
2294
|
+
])
|
|
2295
|
+
expect(items.map((item) => item.type)).toEqual(['assets-deposited'])
|
|
2296
|
+
})
|
|
2297
|
+
|
|
2298
|
+
test('keeps a payment and a later refund in another token as transfers', async () => {
|
|
2299
|
+
const merchant = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2300
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2301
|
+
const items = await classify([
|
|
2302
|
+
transferRow({ from: viewer, logIdx: 0, to: merchant, token: tokenA, value: 100n }),
|
|
2303
|
+
transferRow({ from: merchant, logIdx: 1, to: viewer, token: tokenB, value: 90n }),
|
|
2304
|
+
])
|
|
2305
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
2306
|
+
})
|
|
2307
|
+
|
|
2308
|
+
test('folds an auto-flip order placement into its swap', async () => {
|
|
2309
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2310
|
+
const items = await classify([
|
|
2311
|
+
transferRow({ from: viewer, logIdx: 0, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2312
|
+
transferRow({ from: stablecoinDex, logIdx: 1, to: viewer, token: tokenB, value: 990n }),
|
|
2313
|
+
logRow({
|
|
2314
|
+
address: stablecoinDex,
|
|
2315
|
+
args: {
|
|
2316
|
+
amount: 25n,
|
|
2317
|
+
flipTick: 6,
|
|
2318
|
+
isBid: false,
|
|
2319
|
+
isFlipOrder: true,
|
|
2320
|
+
maker: viewer,
|
|
2321
|
+
orderId: 43n,
|
|
2322
|
+
tick: 5,
|
|
2323
|
+
token: tokenA,
|
|
2324
|
+
},
|
|
2325
|
+
eventName: 'OrderPlaced',
|
|
2326
|
+
logIdx: 2,
|
|
2327
|
+
}),
|
|
2328
|
+
])
|
|
2329
|
+
expect(items.map((item) => item.type)).toEqual(['swap'])
|
|
2330
|
+
})
|
|
2331
|
+
|
|
2332
|
+
test('folds both fee legs into a RebalanceSwap', async () => {
|
|
2333
|
+
const tokenB = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2334
|
+
const items = await classify([
|
|
2335
|
+
transferRow({ from: viewer, logIdx: 0, to: feeManager, token: tokenA, value: 10n }),
|
|
2336
|
+
logRow({
|
|
2337
|
+
address: feeManager,
|
|
2338
|
+
args: {
|
|
2339
|
+
amountIn: 10n,
|
|
2340
|
+
amountOut: 9n,
|
|
2341
|
+
swapper: viewer,
|
|
2342
|
+
userToken: tokenB,
|
|
2343
|
+
validatorToken: tokenA,
|
|
2344
|
+
},
|
|
2345
|
+
eventName: 'RebalanceSwap',
|
|
2346
|
+
logIdx: 1,
|
|
2347
|
+
}),
|
|
2348
|
+
transferRow({ from: feeManager, logIdx: 2, to: viewer, token: tokenB, value: 9n }),
|
|
2349
|
+
])
|
|
2350
|
+
expect(items.map((item) => item.type)).toEqual(['fee-rebalance-swap'])
|
|
2351
|
+
})
|
|
2352
|
+
|
|
2353
|
+
test('folds a CloseRequested refund into session-closed', async () => {
|
|
2354
|
+
const refund = transferRow({
|
|
2355
|
+
from: channelReserve,
|
|
2356
|
+
logIdx: 0,
|
|
2357
|
+
to: viewer,
|
|
2358
|
+
token: tokenA,
|
|
2359
|
+
value: 25n,
|
|
2360
|
+
})
|
|
2361
|
+
const items = await classify([
|
|
2362
|
+
refund,
|
|
2363
|
+
{
|
|
2364
|
+
...refund,
|
|
2365
|
+
data: '0x',
|
|
2366
|
+
logAddress: channelReserve,
|
|
2367
|
+
logIdx: 1,
|
|
2368
|
+
selector: '0xf5a36fc00a96cbb9cf1f8f59299165e1d8ffffe94396d82904b4da524d16bbce',
|
|
2369
|
+
topic1: null,
|
|
2370
|
+
topic2: null,
|
|
2371
|
+
},
|
|
2372
|
+
])
|
|
2373
|
+
expect(items.map((item) => item.type)).toEqual(['session-closed'])
|
|
2374
|
+
expect(items[0]?.type === 'session-closed' && items[0].data.refund?.amount).toBe('25')
|
|
2375
|
+
})
|
|
2376
|
+
|
|
2377
|
+
test('keeps batched channel settlements paired with their own payout legs', async () => {
|
|
2378
|
+
const payee = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2379
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2380
|
+
const channelA = `0x${'a'.repeat(64)}` as Hex.Hex
|
|
2381
|
+
const channelB = `0x${'b'.repeat(64)}` as Hex.Hex
|
|
2382
|
+
const items = await classify([
|
|
2383
|
+
transferRow({ from: channelReserve, logIdx: 0, to: payee, token: tokenA, value: 250n }),
|
|
2384
|
+
logRow({
|
|
2385
|
+
address: channelReserve,
|
|
2386
|
+
args: {
|
|
2387
|
+
channelId: channelA,
|
|
2388
|
+
cumulativeAmount: 250n,
|
|
2389
|
+
deltaPaid: 250n,
|
|
2390
|
+
newSettled: 250n,
|
|
2391
|
+
payee,
|
|
2392
|
+
payer: viewer,
|
|
2393
|
+
},
|
|
2394
|
+
eventName: 'Settled',
|
|
2395
|
+
logIdx: 1,
|
|
2396
|
+
}),
|
|
2397
|
+
transferRow({ from: channelReserve, logIdx: 2, to: payee, token: tokenB, value: 300n }),
|
|
2398
|
+
logRow({
|
|
2399
|
+
address: channelReserve,
|
|
2400
|
+
args: {
|
|
2401
|
+
channelId: channelB,
|
|
2402
|
+
cumulativeAmount: 300n,
|
|
2403
|
+
deltaPaid: 300n,
|
|
2404
|
+
newSettled: 300n,
|
|
2405
|
+
payee,
|
|
2406
|
+
payer: viewer,
|
|
2407
|
+
},
|
|
2408
|
+
eventName: 'Settled',
|
|
2409
|
+
logIdx: 3,
|
|
2410
|
+
}),
|
|
2411
|
+
])
|
|
2412
|
+
expect(items.map((item) => item.type)).toEqual(['channel-settled', 'channel-settled'])
|
|
2413
|
+
if (items[0]?.type !== 'channel-settled' || items[1]?.type !== 'channel-settled') return
|
|
2414
|
+
expect(items[0].data.sourceToken.address).toBe(tokenA)
|
|
2415
|
+
expect(items[1].data.sourceToken.address).toBe(tokenB)
|
|
2416
|
+
})
|
|
2417
|
+
|
|
2418
|
+
test('keeps a role grant made after token creation', async () => {
|
|
2419
|
+
const otherAccount = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2420
|
+
const items = await classify([
|
|
2421
|
+
logRow({
|
|
2422
|
+
address: tokenA,
|
|
2423
|
+
args: { account: viewer, hasRole: true, role: `0x${'1'.repeat(64)}`, sender: viewer },
|
|
2424
|
+
eventName: 'RoleMembershipUpdated',
|
|
2425
|
+
logIdx: 0,
|
|
2426
|
+
}),
|
|
2427
|
+
logRow({
|
|
2428
|
+
address: tip20Factory,
|
|
2429
|
+
args: {
|
|
2430
|
+
admin: viewer,
|
|
2431
|
+
currency: 'USD',
|
|
2432
|
+
name: 'Test',
|
|
2433
|
+
quoteToken: tokenA,
|
|
2434
|
+
salt: `0x${'0'.repeat(64)}`,
|
|
2435
|
+
symbol: 'TST',
|
|
2436
|
+
token: tokenA,
|
|
2437
|
+
},
|
|
2438
|
+
eventName: 'TokenCreated',
|
|
2439
|
+
logIdx: 1,
|
|
2440
|
+
}),
|
|
2441
|
+
logRow({
|
|
2442
|
+
address: tokenA,
|
|
2443
|
+
args: {
|
|
2444
|
+
account: otherAccount,
|
|
2445
|
+
hasRole: true,
|
|
2446
|
+
role: `0x${'2'.repeat(64)}`,
|
|
2447
|
+
sender: viewer,
|
|
2448
|
+
},
|
|
2449
|
+
eventName: 'RoleMembershipUpdated',
|
|
2450
|
+
logIdx: 2,
|
|
2451
|
+
}),
|
|
2452
|
+
])
|
|
2453
|
+
expect(items.map((item) => item.type)).toEqual(['token-created', 'role-membership-set'])
|
|
2454
|
+
expect(items[1]?.type === 'role-membership-set' && items[1].data.account).toBe(otherAccount)
|
|
2455
|
+
})
|
|
2456
|
+
|
|
2457
|
+
test('keeps a whitelist update made after its policy was created', async () => {
|
|
2458
|
+
const account = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2459
|
+
const items = await classify([
|
|
2460
|
+
logRow({
|
|
2461
|
+
address: tokenA,
|
|
2462
|
+
args: { account, allowed: true, policyId: 9n, updater: viewer },
|
|
2463
|
+
eventName: 'WhitelistUpdated',
|
|
2464
|
+
logIdx: 0,
|
|
2465
|
+
}),
|
|
2466
|
+
logRow({
|
|
2467
|
+
address: tokenA,
|
|
2468
|
+
args: { policyId: 9n, policyType: 1, updater: viewer },
|
|
2469
|
+
eventName: 'PolicyCreated',
|
|
2470
|
+
logIdx: 1,
|
|
2471
|
+
}),
|
|
2472
|
+
logRow({
|
|
2473
|
+
address: tokenA,
|
|
2474
|
+
args: { account, allowed: false, policyId: 9n, updater: viewer },
|
|
2475
|
+
eventName: 'WhitelistUpdated',
|
|
2476
|
+
logIdx: 2,
|
|
2477
|
+
}),
|
|
2478
|
+
])
|
|
2479
|
+
expect(items.map((item) => item.type)).toEqual(['policy-created', 'whitelist-updated'])
|
|
2480
|
+
})
|
|
2481
|
+
|
|
2482
|
+
test('keeps an unrelated validator addition beside a rotation', async () => {
|
|
2483
|
+
const validator = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2484
|
+
const feeRecipient = '0xabababababababababababababababababababab' as Address.Address
|
|
2485
|
+
const items = await classify([
|
|
2486
|
+
logRow({
|
|
2487
|
+
address: accountKeychain,
|
|
2488
|
+
args: { index: 1n, validatorAddress: viewer },
|
|
2489
|
+
eventName: 'ValidatorDeactivated',
|
|
2490
|
+
logIdx: 0,
|
|
2491
|
+
}),
|
|
2492
|
+
logRow({
|
|
2493
|
+
address: accountKeychain,
|
|
2494
|
+
args: {
|
|
2495
|
+
egress: 'https://v.example.com:2',
|
|
2496
|
+
feeRecipient,
|
|
2497
|
+
index: 2n,
|
|
2498
|
+
ingress: 'https://v.example.com:1',
|
|
2499
|
+
publicKey: `0x${'2'.repeat(64)}`,
|
|
2500
|
+
validatorAddress: validator,
|
|
2501
|
+
},
|
|
2502
|
+
eventName: 'ValidatorAdded',
|
|
2503
|
+
logIdx: 1,
|
|
2504
|
+
}),
|
|
2505
|
+
logRow({
|
|
2506
|
+
address: accountKeychain,
|
|
2507
|
+
args: {
|
|
2508
|
+
caller: viewer,
|
|
2509
|
+
deactivatedIndex: 1n,
|
|
2510
|
+
egress: 'https://v.example.com:2',
|
|
2511
|
+
index: 2n,
|
|
2512
|
+
ingress: 'https://v.example.com:1',
|
|
2513
|
+
newPublicKey: `0x${'2'.repeat(64)}`,
|
|
2514
|
+
oldPublicKey: `0x${'1'.repeat(64)}`,
|
|
2515
|
+
validatorAddress: validator,
|
|
2516
|
+
},
|
|
2517
|
+
eventName: 'ValidatorRotated',
|
|
2518
|
+
logIdx: 2,
|
|
2519
|
+
}),
|
|
2520
|
+
logRow({
|
|
2521
|
+
address: accountKeychain,
|
|
2522
|
+
args: {
|
|
2523
|
+
egress: 'https://v.example.com:4',
|
|
2524
|
+
feeRecipient,
|
|
2525
|
+
index: 3n,
|
|
2526
|
+
ingress: 'https://v.example.com:3',
|
|
2527
|
+
publicKey: `0x${'4'.repeat(64)}`,
|
|
2528
|
+
validatorAddress: feeRecipient,
|
|
2529
|
+
},
|
|
2530
|
+
eventName: 'ValidatorAdded',
|
|
2531
|
+
logIdx: 3,
|
|
2532
|
+
}),
|
|
2533
|
+
])
|
|
2534
|
+
expect(items.map((item) => item.type)).toEqual(['validator-rotated', 'validator-added'])
|
|
2535
|
+
})
|
|
2536
|
+
|
|
2537
|
+
test('keeps an access-key authorization beside a vault deposit', async () => {
|
|
2538
|
+
const publicKey = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2539
|
+
const items = await classify([
|
|
2540
|
+
logRow({
|
|
2541
|
+
address: tokenA,
|
|
2542
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2543
|
+
eventName: 'Deposited',
|
|
2544
|
+
logIdx: 0,
|
|
2545
|
+
}),
|
|
2546
|
+
logRow({
|
|
2547
|
+
address: accountKeychain,
|
|
2548
|
+
args: { account: viewer, expiry: 1_800_000_000n, publicKey, signatureType: 1 },
|
|
2549
|
+
eventName: 'KeyAuthorized',
|
|
2550
|
+
logIdx: 1,
|
|
2551
|
+
}),
|
|
2552
|
+
])
|
|
2553
|
+
expect(items.map((item) => item.type)).toEqual(['assets-deposited', 'access-key-created'])
|
|
2554
|
+
})
|
|
2555
|
+
|
|
2556
|
+
test('keeps both directions of a receipt-less transaction', async () => {
|
|
2557
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2558
|
+
const q2 = transferRow({ from: other, logIdx: 0, to: viewer, token: tokenA, value: 10n })
|
|
2559
|
+
const q3 = transferRow({ from: viewer, logIdx: 1, to: other, token: tokenA, value: 5n })
|
|
2560
|
+
const merged = Activities.mergeAndSort([], [q2], [q3])
|
|
2561
|
+
expect(merged.map((row) => row.logIdx)).toEqual([0, 1])
|
|
2562
|
+
})
|
|
2563
|
+
|
|
2564
|
+
test('keeps duplicate payments when one carries a memo', async () => {
|
|
2565
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2566
|
+
const items = await classify([
|
|
2567
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenA, value: 10n }),
|
|
2568
|
+
logRow({
|
|
2569
|
+
address: tokenA,
|
|
2570
|
+
args: { amount: 10n, from: viewer, memo: `0x${'1'.repeat(64)}`, to: recipient },
|
|
2571
|
+
eventName: 'TransferWithMemo',
|
|
2572
|
+
logIdx: 1,
|
|
2573
|
+
}),
|
|
2574
|
+
transferRow({ from: viewer, logIdx: 2, to: recipient, token: tokenA, value: 10n }),
|
|
2575
|
+
])
|
|
2576
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
2577
|
+
})
|
|
2578
|
+
|
|
2579
|
+
test('keeps a settlement payout beside a refundless session close', async () => {
|
|
2580
|
+
const payee = viewer
|
|
2581
|
+
const closeRow = {
|
|
2582
|
+
...transferRow({ from: channelReserve, logIdx: 0, to: viewer, token: tokenA, value: 0n }),
|
|
2583
|
+
data: '0x',
|
|
2584
|
+
logAddress: channelReserve,
|
|
2585
|
+
logIdx: 0,
|
|
2586
|
+
selector: '0xf5a36fc00a96cbb9cf1f8f59299165e1d8ffffe94396d82904b4da524d16bbce',
|
|
2587
|
+
topic1: null,
|
|
2588
|
+
topic2: null,
|
|
2589
|
+
}
|
|
2590
|
+
const items = await classify([
|
|
2591
|
+
closeRow,
|
|
2592
|
+
transferRow({ from: channelReserve, logIdx: 1, to: payee, token: tokenA, value: 250n }),
|
|
2593
|
+
logRow({
|
|
2594
|
+
address: channelReserve,
|
|
2595
|
+
args: {
|
|
2596
|
+
channelId: `0x${'c'.repeat(64)}`,
|
|
2597
|
+
cumulativeAmount: 250n,
|
|
2598
|
+
deltaPaid: 250n,
|
|
2599
|
+
newSettled: 250n,
|
|
2600
|
+
payee,
|
|
2601
|
+
payer: viewer,
|
|
2602
|
+
},
|
|
2603
|
+
eventName: 'Settled',
|
|
2604
|
+
logIdx: 2,
|
|
2605
|
+
}),
|
|
2606
|
+
])
|
|
2607
|
+
expect(items.map((item) => item.type)).toEqual(['session-closed', 'channel-settled'])
|
|
2608
|
+
if (items[0]?.type !== 'session-closed' || items[1]?.type !== 'channel-settled') return
|
|
2609
|
+
expect(items[0].data.refund).toBeUndefined()
|
|
2610
|
+
expect(items[1].data.sourceToken.address).toBe(tokenA)
|
|
2611
|
+
})
|
|
2612
|
+
|
|
2613
|
+
test('keeps an order placement beside a swap in one batch', async () => {
|
|
2614
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2615
|
+
const tokenC = '0x6666666666666666666666666666666666666666' as Address.Address
|
|
2616
|
+
const items = await classify([
|
|
2617
|
+
transferRow({ from: viewer, logIdx: 0, to: stablecoinDex, token: tokenA, value: 500n }),
|
|
2618
|
+
logRow({
|
|
2619
|
+
address: stablecoinDex,
|
|
2620
|
+
args: {
|
|
2621
|
+
amount: 500n,
|
|
2622
|
+
flipTick: 0,
|
|
2623
|
+
isBid: true,
|
|
2624
|
+
isFlipOrder: false,
|
|
2625
|
+
maker: viewer,
|
|
2626
|
+
orderId: 7n,
|
|
2627
|
+
tick: 3,
|
|
2628
|
+
token: tokenA,
|
|
2629
|
+
},
|
|
2630
|
+
eventName: 'OrderPlaced',
|
|
2631
|
+
logIdx: 1,
|
|
2632
|
+
}),
|
|
2633
|
+
transferRow({ from: viewer, logIdx: 2, to: stablecoinDex, token: tokenB, value: 1_000n }),
|
|
2634
|
+
transferRow({ from: stablecoinDex, logIdx: 3, to: viewer, token: tokenC, value: 990n }),
|
|
2635
|
+
])
|
|
2636
|
+
expect(items.map((item) => item.type)).toEqual(['order-placed', 'swap'])
|
|
2637
|
+
if (items[1]?.type !== 'swap') return
|
|
2638
|
+
expect(items[1].data.sourceToken).toEqual({ address: tokenB, amount: '1000' })
|
|
2639
|
+
})
|
|
2640
|
+
|
|
2641
|
+
test("keeps the viewer's own fill beside a third-party swap", async () => {
|
|
2642
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2643
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2644
|
+
const items = await classify([
|
|
2645
|
+
transferRow({ from: other, logIdx: 0, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2646
|
+
logRow({
|
|
2647
|
+
address: stablecoinDex,
|
|
2648
|
+
args: {
|
|
2649
|
+
amountFilled: 1_000n,
|
|
2650
|
+
maker: viewer,
|
|
2651
|
+
orderId: 7n,
|
|
2652
|
+
partialFill: false,
|
|
2653
|
+
taker: other,
|
|
2654
|
+
},
|
|
2655
|
+
eventName: 'OrderFilled',
|
|
2656
|
+
logIdx: 1,
|
|
2657
|
+
}),
|
|
2658
|
+
transferRow({ from: stablecoinDex, logIdx: 2, to: other, token: tokenB, value: 990n }),
|
|
2659
|
+
])
|
|
2660
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'order-filled'])
|
|
2661
|
+
})
|
|
2662
|
+
|
|
2663
|
+
test('folds the amount-matched vault event into a private Earn deposit', async () => {
|
|
2664
|
+
const actionId = `0x${'c'.repeat(64)}` as Hex.Hex
|
|
2665
|
+
const items = await classify([
|
|
2666
|
+
logRow({
|
|
2667
|
+
address: tokenA,
|
|
2668
|
+
args: { assets: 50n, caller: viewer, earnShares: 45n, receiver: viewer },
|
|
2669
|
+
eventName: 'Deposited',
|
|
2670
|
+
logIdx: 0,
|
|
2671
|
+
}),
|
|
2672
|
+
logRow({
|
|
2673
|
+
address: tokenA,
|
|
2674
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2675
|
+
eventName: 'Deposited',
|
|
2676
|
+
logIdx: 1,
|
|
2677
|
+
}),
|
|
2678
|
+
logRow({
|
|
2679
|
+
address: tokenA,
|
|
2680
|
+
args: {
|
|
2681
|
+
actionId,
|
|
2682
|
+
earnShares: 90n,
|
|
2683
|
+
earnVault: tokenA,
|
|
2684
|
+
inputAmount: 100n,
|
|
2685
|
+
inputToken: tokenA,
|
|
2686
|
+
vaultAssets: 100n,
|
|
2687
|
+
zoneDepositHash: actionId,
|
|
2688
|
+
},
|
|
2689
|
+
eventName: 'EarnDeposit',
|
|
2690
|
+
logIdx: 2,
|
|
2691
|
+
}),
|
|
2692
|
+
])
|
|
2693
|
+
expect(items.map((item) => item.type)).toEqual(['assets-deposited', 'private-assets-deposited'])
|
|
2694
|
+
expect(items[0]?.type === 'assets-deposited' && items[0].data.assets).toBe('50')
|
|
2695
|
+
})
|
|
2696
|
+
|
|
2697
|
+
test('surfaces a fold-only vault operation when nothing else classified', async () => {
|
|
2698
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2699
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2700
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2701
|
+
const items = await classify([
|
|
2702
|
+
transferRow({ from: other, logIdx: 0, to: zero, token: tokenA, value: 90n }),
|
|
2703
|
+
transferRow({ from: tokenA, logIdx: 1, to: other, token: asset, value: 100n }),
|
|
2704
|
+
logRow({
|
|
2705
|
+
address: tokenA,
|
|
2706
|
+
args: { assets: 100n, owner: other, receiver: other, sender: other, shares: 90n },
|
|
2707
|
+
eventName: 'Withdraw',
|
|
2708
|
+
logIdx: 2,
|
|
2709
|
+
}),
|
|
2710
|
+
])
|
|
2711
|
+
expect(items.map((item) => item.type)).toEqual(['assets-withdrawn'])
|
|
2712
|
+
})
|
|
2713
|
+
|
|
2714
|
+
test('releases claimed legs when an anchor is partially malformed', async () => {
|
|
2715
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2716
|
+
const corrupted = logRow({
|
|
2717
|
+
address: tokenA,
|
|
2718
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2719
|
+
eventName: 'Deposited',
|
|
2720
|
+
logIdx: 1,
|
|
2721
|
+
})
|
|
2722
|
+
const items = await classify([
|
|
2723
|
+
transferRow({ from: viewer, logIdx: 0, to: tokenA, token: asset, value: 100n }),
|
|
2724
|
+
// Valid amounts with the indexed parties stripped: the builder throws
|
|
2725
|
+
// after claiming the asset leg, which must be released.
|
|
2726
|
+
{ ...corrupted, topic1: null, topic2: null },
|
|
2727
|
+
])
|
|
2728
|
+
expect(items.map((item) => item.type)).toEqual(['transfer'])
|
|
2729
|
+
})
|
|
2730
|
+
|
|
2731
|
+
test('keeps a cross-token payment beside a same-amount memo payment', async () => {
|
|
2732
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2733
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2734
|
+
const items = await classify([
|
|
2735
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenB, value: 10n }),
|
|
2736
|
+
logRow({
|
|
2737
|
+
address: tokenA,
|
|
2738
|
+
args: { amount: 10n, from: viewer, memo: `0x${'1'.repeat(64)}`, to: recipient },
|
|
2739
|
+
eventName: 'TransferWithMemo',
|
|
2740
|
+
logIdx: 1,
|
|
2741
|
+
}),
|
|
2742
|
+
])
|
|
2743
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'transfer'])
|
|
2744
|
+
})
|
|
2745
|
+
|
|
2746
|
+
test('keeps chained swaps through two routes distinct', async () => {
|
|
2747
|
+
const routeA = '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f' as Address.Address
|
|
2748
|
+
const routeB = '0xcafe000000000000000000000000000000000001' as Address.Address
|
|
2749
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2750
|
+
const tokenC = '0x6666666666666666666666666666666666666666' as Address.Address
|
|
2751
|
+
const routeLog = (route: Address.Address, logIdx: number): Activities.Row => ({
|
|
2752
|
+
...transferRow({ from: viewer, logIdx, to: route, token: tokenA, value: 0n }),
|
|
2753
|
+
data: '0x',
|
|
2754
|
+
logAddress: route,
|
|
2755
|
+
selector: `0x${'9'.repeat(64)}`,
|
|
2756
|
+
topic1: null,
|
|
2757
|
+
topic2: null,
|
|
2758
|
+
})
|
|
2759
|
+
const items = await classify([
|
|
2760
|
+
transferRow({ from: viewer, logIdx: 0, to: routeA, token: tokenA, value: 1_000n }),
|
|
2761
|
+
routeLog(routeA, 1),
|
|
2762
|
+
transferRow({ from: routeA, logIdx: 2, to: viewer, token: tokenB, value: 990n }),
|
|
2763
|
+
transferRow({ from: viewer, logIdx: 3, to: routeB, token: tokenB, value: 990n }),
|
|
2764
|
+
routeLog(routeB, 4),
|
|
2765
|
+
transferRow({ from: routeB, logIdx: 5, to: viewer, token: tokenC, value: 980n }),
|
|
2766
|
+
])
|
|
2767
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'swap'])
|
|
2768
|
+
})
|
|
2769
|
+
|
|
2770
|
+
test('keeps an approval to an unrelated spender beside a swap', async () => {
|
|
2771
|
+
const spender = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2772
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2773
|
+
const gap: Activities.Row = {
|
|
2774
|
+
...transferRow({ from: viewer, logIdx: 1, to: spender, token: tokenA, value: 0n }),
|
|
2775
|
+
data: '0x',
|
|
2776
|
+
logAddress: accountKeychain,
|
|
2777
|
+
selector: `0x${'8'.repeat(64)}`,
|
|
2778
|
+
topic1: null,
|
|
2779
|
+
topic2: null,
|
|
2780
|
+
}
|
|
2781
|
+
const items = await classify([
|
|
2782
|
+
logRow({
|
|
2783
|
+
address: tokenA,
|
|
2784
|
+
args: { amount: 1_000n, owner: viewer, spender },
|
|
2785
|
+
eventName: 'Approval',
|
|
2786
|
+
logIdx: 0,
|
|
2787
|
+
}),
|
|
2788
|
+
gap,
|
|
2789
|
+
transferRow({ from: viewer, logIdx: 2, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2790
|
+
transferRow({ from: stablecoinDex, logIdx: 3, to: viewer, token: tokenB, value: 990n }),
|
|
2791
|
+
])
|
|
2792
|
+
expect(items.map((item) => item.type)).toEqual(['approval', 'swap'])
|
|
2793
|
+
})
|
|
2794
|
+
|
|
2795
|
+
test('keeps a settlement leg away from a zero-value channel close', async () => {
|
|
2796
|
+
const payee = viewer
|
|
2797
|
+
const channelA = `0x${'a'.repeat(64)}` as Hex.Hex
|
|
2798
|
+
const channelB = `0x${'b'.repeat(64)}` as Hex.Hex
|
|
2799
|
+
const items = await classify([
|
|
2800
|
+
logRow({
|
|
2801
|
+
address: channelReserve,
|
|
2802
|
+
args: {
|
|
2803
|
+
channelId: channelA,
|
|
2804
|
+
payee,
|
|
2805
|
+
payer: viewer,
|
|
2806
|
+
refundedToPayer: 0n,
|
|
2807
|
+
settledToPayee: 0n,
|
|
2808
|
+
},
|
|
2809
|
+
eventName: 'ChannelClosed',
|
|
2810
|
+
logIdx: 0,
|
|
2811
|
+
}),
|
|
2812
|
+
transferRow({ from: channelReserve, logIdx: 1, to: payee, token: tokenA, value: 250n }),
|
|
2813
|
+
logRow({
|
|
2814
|
+
address: channelReserve,
|
|
2815
|
+
args: {
|
|
2816
|
+
channelId: channelB,
|
|
2817
|
+
cumulativeAmount: 250n,
|
|
2818
|
+
deltaPaid: 250n,
|
|
2819
|
+
newSettled: 250n,
|
|
2820
|
+
payee,
|
|
2821
|
+
payer: viewer,
|
|
2822
|
+
},
|
|
2823
|
+
eventName: 'Settled',
|
|
2824
|
+
logIdx: 2,
|
|
2825
|
+
}),
|
|
2826
|
+
])
|
|
2827
|
+
expect(items.map((item) => item.type)).toEqual(['channel-closed', 'channel-settled'])
|
|
2828
|
+
if (items[1]?.type !== 'channel-settled') return
|
|
2829
|
+
expect(items[1].data.sourceToken.address).toBe(tokenA)
|
|
2830
|
+
})
|
|
2831
|
+
|
|
2832
|
+
test('keeps an inbound fee-manager payment beside a fee distribution', async () => {
|
|
2833
|
+
const validator = '0xcccccccc00000000000000000000000000000000' as Address.Address
|
|
2834
|
+
const items = await classify([
|
|
2835
|
+
transferRow({ from: feeManager, logIdx: 0, to: viewer, token: tokenA, value: 77n }),
|
|
2836
|
+
transferRow({ from: feeManager, logIdx: 1, to: validator, token: tokenA, value: 77n }),
|
|
2837
|
+
logRow({
|
|
2838
|
+
address: feeManager,
|
|
2839
|
+
args: { amount: 77n, token: tokenA, validator },
|
|
2840
|
+
eventName: 'FeesDistributed',
|
|
2841
|
+
logIdx: 2,
|
|
2842
|
+
}),
|
|
2843
|
+
])
|
|
2844
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'fees-distributed'])
|
|
2845
|
+
})
|
|
2846
|
+
|
|
2847
|
+
test('keeps a same-amount payment beside a reward funding', async () => {
|
|
2848
|
+
const other = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2849
|
+
const items = await classify([
|
|
2850
|
+
transferRow({ from: viewer, logIdx: 0, to: other, token: tokenA, value: 77n }),
|
|
2851
|
+
transferRow({ from: viewer, logIdx: 1, to: tokenA, token: tokenA, value: 77n }),
|
|
2852
|
+
logRow({
|
|
2853
|
+
address: tokenA,
|
|
2854
|
+
args: { amount: 77n, funder: viewer },
|
|
2855
|
+
eventName: 'RewardDistributed',
|
|
2856
|
+
logIdx: 2,
|
|
2857
|
+
}),
|
|
2858
|
+
])
|
|
2859
|
+
expect(items.map((item) => item.type)).toEqual(['transfer', 'reward-distributed'])
|
|
2860
|
+
})
|
|
2861
|
+
|
|
2862
|
+
test('pairs an order with its own escrow after a same-token swap', async () => {
|
|
2863
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2864
|
+
const items = await classify([
|
|
2865
|
+
transferRow({ from: viewer, logIdx: 0, to: stablecoinDex, token: tokenA, value: 500n }),
|
|
2866
|
+
transferRow({ from: stablecoinDex, logIdx: 1, to: viewer, token: tokenB, value: 495n }),
|
|
2867
|
+
transferRow({ from: viewer, logIdx: 2, to: stablecoinDex, token: tokenA, value: 500n }),
|
|
2868
|
+
logRow({
|
|
2869
|
+
address: stablecoinDex,
|
|
2870
|
+
args: {
|
|
2871
|
+
amount: 500n,
|
|
2872
|
+
flipTick: 0,
|
|
2873
|
+
isBid: true,
|
|
2874
|
+
isFlipOrder: false,
|
|
2875
|
+
maker: viewer,
|
|
2876
|
+
orderId: 7n,
|
|
2877
|
+
tick: 3,
|
|
2878
|
+
token: tokenA,
|
|
2879
|
+
},
|
|
2880
|
+
eventName: 'OrderPlaced',
|
|
2881
|
+
logIdx: 3,
|
|
2882
|
+
}),
|
|
2883
|
+
])
|
|
2884
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'order-placed'])
|
|
2885
|
+
})
|
|
2886
|
+
|
|
2887
|
+
test('pairs interleaved DEX swaps with their own outputs', async () => {
|
|
2888
|
+
const other = '0x9999999999999999999999999999999999999999' as Address.Address
|
|
2889
|
+
const tokenB = '0x5555555555555555555555555555555555555555' as Address.Address
|
|
2890
|
+
const items = await classify([
|
|
2891
|
+
transferRow({ from: other, logIdx: 0, to: stablecoinDex, token: tokenA, value: 1_000n }),
|
|
2892
|
+
transferRow({ from: viewer, logIdx: 1, to: stablecoinDex, token: tokenA, value: 500n }),
|
|
2893
|
+
transferRow({ from: stablecoinDex, logIdx: 2, to: viewer, token: tokenB, value: 495n }),
|
|
2894
|
+
transferRow({ from: stablecoinDex, logIdx: 3, to: other, token: tokenB, value: 990n }),
|
|
2895
|
+
])
|
|
2896
|
+
expect(items.map((item) => item.type)).toEqual(['swap', 'swap'])
|
|
2897
|
+
if (items[0]?.type !== 'swap' || items[1]?.type !== 'swap') return
|
|
2898
|
+
expect(items[0].data.destinationToken.amount).toBe('990')
|
|
2899
|
+
expect(items[1].data.destinationToken.amount).toBe('495')
|
|
2900
|
+
})
|
|
2901
|
+
|
|
2902
|
+
test('keeps an unrelated-token approval to the vault beside a deposit', async () => {
|
|
2903
|
+
const asset = '0x4444444444444444444444444444444444444444' as Address.Address
|
|
2904
|
+
const tokenC = '0x6666666666666666666666666666666666666666' as Address.Address
|
|
2905
|
+
const zero = '0x0000000000000000000000000000000000000000' as Address.Address
|
|
2906
|
+
const items = await classify([
|
|
2907
|
+
logRow({
|
|
2908
|
+
address: tokenC,
|
|
2909
|
+
args: { amount: 100n, owner: viewer, spender: tokenA },
|
|
2910
|
+
eventName: 'Approval',
|
|
2911
|
+
logIdx: 0,
|
|
2912
|
+
}),
|
|
2913
|
+
transferRow({ from: viewer, logIdx: 1, to: tokenA, token: asset, value: 100n }),
|
|
2914
|
+
transferRow({ from: zero, logIdx: 2, to: viewer, token: tokenA, value: 90n }),
|
|
2915
|
+
logRow({
|
|
2916
|
+
address: tokenA,
|
|
2917
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2918
|
+
eventName: 'Deposited',
|
|
2919
|
+
logIdx: 3,
|
|
2920
|
+
}),
|
|
2921
|
+
])
|
|
2922
|
+
expect(items.map((item) => item.type)).toEqual(['approval', 'assets-deposited'])
|
|
2923
|
+
})
|
|
2924
|
+
|
|
2925
|
+
test('folds the nearest preceding vault event into a private Earn deposit', async () => {
|
|
2926
|
+
const actionId = `0x${'c'.repeat(64)}` as Hex.Hex
|
|
2927
|
+
const items = await classify([
|
|
2928
|
+
logRow({
|
|
2929
|
+
address: tokenA,
|
|
2930
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2931
|
+
eventName: 'Deposited',
|
|
2932
|
+
logIdx: 0,
|
|
2933
|
+
}),
|
|
2934
|
+
logRow({
|
|
2935
|
+
address: tokenA,
|
|
2936
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2937
|
+
eventName: 'Deposited',
|
|
2938
|
+
logIdx: 1,
|
|
2939
|
+
}),
|
|
2940
|
+
logRow({
|
|
2941
|
+
address: tokenA,
|
|
2942
|
+
args: {
|
|
2943
|
+
actionId,
|
|
2944
|
+
earnShares: 90n,
|
|
2945
|
+
earnVault: tokenA,
|
|
2946
|
+
inputAmount: 100n,
|
|
2947
|
+
inputToken: tokenA,
|
|
2948
|
+
vaultAssets: 100n,
|
|
2949
|
+
zoneDepositHash: actionId,
|
|
2950
|
+
},
|
|
2951
|
+
eventName: 'EarnDeposit',
|
|
2952
|
+
logIdx: 2,
|
|
2953
|
+
}),
|
|
2954
|
+
])
|
|
2955
|
+
expect(items.map((item) => item.type)).toEqual(['assets-deposited', 'private-assets-deposited'])
|
|
2956
|
+
expect(items[0]?.id.endsWith('-0')).toBe(true)
|
|
2957
|
+
})
|
|
2958
|
+
|
|
2959
|
+
test('keeps earlier activities when a later event is malformed', async () => {
|
|
2960
|
+
const recipient = '0x7777777777777777777777777777777777777777' as Address.Address
|
|
2961
|
+
const corrupted = logRow({
|
|
2962
|
+
address: tokenA,
|
|
2963
|
+
args: { assets: 100n, caller: viewer, earnShares: 90n, receiver: viewer },
|
|
2964
|
+
eventName: 'Deposited',
|
|
2965
|
+
logIdx: 1,
|
|
2966
|
+
})
|
|
2967
|
+
const items = await classify([
|
|
2968
|
+
transferRow({ from: viewer, logIdx: 0, to: recipient, token: tokenA, value: 10n }),
|
|
2969
|
+
// Valid topics with truncated data: the event decodes but its builder
|
|
2970
|
+
// cannot read the amounts, which must not discard the whole transaction.
|
|
2971
|
+
{ ...corrupted, data: '0x' },
|
|
2972
|
+
])
|
|
2973
|
+
expect(items.map((item) => item.type)).toEqual(['transfer'])
|
|
2974
|
+
})
|
|
2975
|
+
})
|
|
2976
|
+
|
|
2977
|
+
/**
|
|
2978
|
+
* Replays real Tempo mainnet receipts (raw logs, topics, and data) through the
|
|
2979
|
+
* classifier, covering batch transactions whose activities the old headline
|
|
2980
|
+
* classifier collapsed or misread.
|
|
2981
|
+
*/
|
|
2982
|
+
describe('Activities.list receipt replays', () => {
|
|
2983
|
+
const viewer = '0x03ef854275a0f914a0f34db6756a59f20c821989' as Address.Address
|
|
2984
|
+
const txHash = `0x${'d'.repeat(64)}` as Hex.Hex
|
|
2985
|
+
|
|
2986
|
+
function store() {
|
|
2987
|
+
const store = Store.memory()
|
|
2988
|
+
void store.put('mpp-services:v1', JSON.stringify({}))
|
|
2989
|
+
return store
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
type ReceiptLog = {
|
|
2993
|
+
address: string
|
|
2994
|
+
data: string
|
|
2995
|
+
logIdx: number
|
|
2996
|
+
topics: readonly string[]
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
/** Maps a raw receipt log to a Q1 row, viewer as the transaction sender. */
|
|
3000
|
+
function receiptRow(log: ReceiptLog): Activities.Row {
|
|
3001
|
+
return {
|
|
3002
|
+
blockNum: 100,
|
|
3003
|
+
blockTimestamp: 1_700_000_000,
|
|
3004
|
+
data: log.data,
|
|
3005
|
+
feePayer: null,
|
|
3006
|
+
logAddress: log.address as Address.Address,
|
|
3007
|
+
logIdx: log.logIdx,
|
|
3008
|
+
selector: log.topics[0] ?? null,
|
|
3009
|
+
source: 'q1',
|
|
3010
|
+
topic1: log.topics[1] ?? null,
|
|
3011
|
+
topic2: log.topics[2] ?? null,
|
|
3012
|
+
topic3: log.topics[3] ?? null,
|
|
3013
|
+
txHash,
|
|
3014
|
+
txIdx: 0,
|
|
3015
|
+
txSender: viewer,
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
async function classify(logs: readonly ReceiptLog[]) {
|
|
3020
|
+
const provider: Activities.Provider = {
|
|
3021
|
+
cursorForGroup: (group) => `${group.blockNum}:${group.txIdx}`,
|
|
3022
|
+
fetchRows: async () => ({ rows: logs.map(receiptRow) }),
|
|
3023
|
+
}
|
|
3024
|
+
const { items } = await Activities.list(provider, { address: viewer, store: store() })
|
|
3025
|
+
return items
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
const routerSwapLogs = [
|
|
3029
|
+
{
|
|
3030
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3031
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010dc68',
|
|
3032
|
+
logIdx: 0,
|
|
3033
|
+
topics: [
|
|
3034
|
+
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',
|
|
3035
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3036
|
+
'0x000000000000000000000000ccc88a9d1b4ed6b0eaba998850414b24f1c315be',
|
|
3037
|
+
],
|
|
3038
|
+
},
|
|
3039
|
+
{
|
|
3040
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3041
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010dc68',
|
|
3042
|
+
logIdx: 1,
|
|
3043
|
+
topics: [
|
|
3044
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3045
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3046
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3047
|
+
],
|
|
3048
|
+
},
|
|
3049
|
+
{
|
|
3050
|
+
address: '0xccc88a9d1b4ed6b0eaba998850414b24f1c315be',
|
|
3051
|
+
data: '0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000020c000000000000000000000b9537d11c60e8b50000000000000000000000000000000000000000000000000000000000010dc6800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000217eb7a8dfb83149821d35b62fbd7880e2fe6e29f04133156c1adb203c95d7e2a40000000000000000000000000000000000000000000000000000000000000000',
|
|
3052
|
+
logIdx: 2,
|
|
3053
|
+
topics: ['0xafbab204e8271965231d37baed9b1abca8725b7409c70314455f68bc89142b91'],
|
|
3054
|
+
},
|
|
3055
|
+
{
|
|
3056
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3057
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010dc68',
|
|
3058
|
+
logIdx: 3,
|
|
3059
|
+
topics: [
|
|
3060
|
+
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',
|
|
3061
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3062
|
+
'0x0000000000000000000000000000000000001ff3684f28c67538d4d072c22734',
|
|
3063
|
+
],
|
|
3064
|
+
},
|
|
3065
|
+
{
|
|
3066
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3067
|
+
data: '0x00000000000000000000000020c000000000000000000000b9537d11c60e8b50000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000000000000000001ff3684f28c67538d4d072c22734000000000000000000000000000000000000000000000000000000000010dc6800000000000000000000000000000000000000000000000000000000',
|
|
3068
|
+
logIdx: 4,
|
|
3069
|
+
topics: ['0x93485dcd31a905e3ffd7b012abe3438fa8fa77f98ddc9f50e879d3fa7ccdc324'],
|
|
3070
|
+
},
|
|
3071
|
+
{
|
|
3072
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3073
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000003ab',
|
|
3074
|
+
logIdx: 5,
|
|
3075
|
+
topics: [
|
|
3076
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3077
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3078
|
+
'0x000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef',
|
|
3079
|
+
],
|
|
3080
|
+
},
|
|
3081
|
+
{
|
|
3082
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3083
|
+
data: '0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef00000000000000000000000020c000000000000000000000b9537d11c60e8b5000000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000217eb7a8dfb83149821d35b62fbd7880e2fe6e29f04133156c1adb203c95d7e2a40100000000000000000000000000000000000000000000000000000000000000',
|
|
3084
|
+
logIdx: 6,
|
|
3085
|
+
topics: ['0xafbab204e8271965231d37baed9b1abca8725b7409c70314455f68bc89142b91'],
|
|
3086
|
+
},
|
|
3087
|
+
{
|
|
3088
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3089
|
+
data: '0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a49bb43718000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000020c000000000000000000000b9537d11c60e8b500000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000000217eb7a8dfb83149821d35b62fbd7880e2fe6e29f04133156c1adb203c95d7e2a4010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
3090
|
+
logIdx: 7,
|
|
3091
|
+
topics: ['0x93485dcd31a905e3ffd7b012abe3438fa8fa77f98ddc9f50e879d3fa7ccdc324'],
|
|
3092
|
+
},
|
|
3093
|
+
{
|
|
3094
|
+
address: '0x33620f62c5b9b2086dd6b62f4a297a9f30347029',
|
|
3095
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010d8dcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef27430000000000000000000000000000000000000000fffbc94d34a6b108b32b149a00000000000000000000000000000000000000000000000000001d36428c070cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000000000000000000000000000000064',
|
|
3096
|
+
logIdx: 8,
|
|
3097
|
+
topics: [
|
|
3098
|
+
'0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f',
|
|
3099
|
+
'0xbd170b4ccb7f5da2565e00d5bbfa4777187b20ffb824412766463c162a47d78a',
|
|
3100
|
+
'0x000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce441',
|
|
3101
|
+
],
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3105
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010d8dc',
|
|
3106
|
+
logIdx: 9,
|
|
3107
|
+
topics: [
|
|
3108
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3109
|
+
'0x00000000000000000000000033620f62c5b9b2086dd6b62f4a297a9f30347029',
|
|
3110
|
+
'0x000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce441',
|
|
3111
|
+
],
|
|
3112
|
+
},
|
|
3113
|
+
{
|
|
3114
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3115
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010d8bd',
|
|
3116
|
+
logIdx: 10,
|
|
3117
|
+
topics: [
|
|
3118
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3119
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3120
|
+
'0x00000000000000000000000033620f62c5b9b2086dd6b62f4a297a9f30347029',
|
|
3121
|
+
],
|
|
3122
|
+
},
|
|
3123
|
+
{
|
|
3124
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3125
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010d8dc',
|
|
3126
|
+
logIdx: 11,
|
|
3127
|
+
topics: [
|
|
3128
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3129
|
+
'0x000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce441',
|
|
3130
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3131
|
+
],
|
|
3132
|
+
},
|
|
3133
|
+
{
|
|
3134
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3135
|
+
data: '0x0000000000000000000000000000000000001ff3684f28c67538d4d072c227340000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e42213bc0b000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce44100000000000000000000000020c000000000000000000000b9537d11c60e8b50000000000000000000000000000000000000000000000000000000000010d8bd000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce44100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000004041fff991f000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000020c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010c34a00000000000000000000000000000000000000000000000000000000000000a0d70e27de7eb4ff3a242a1d170af43c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000204931997d3000000000000000000000000e59f2f6ace8b9f985900f14462fbaa40385ce44100000000000000000000000020c000000000000000000000b9537d11c60e8b50000000000000000000000000000000000000000000000000000000000010d8bd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a862ef800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000ffffffffffffffc5000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000482710fffd8963efd1fc6a506488495d951d5263988d250120c00000000000000000000000000000000000000000640000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008434ee90ca000000000000000000000000f5c4f3dc02c3fb9279495a8fef7b0741da95615700000000000000000000000020c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010d98b0000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
3136
|
+
logIdx: 12,
|
|
3137
|
+
topics: ['0x93485dcd31a905e3ffd7b012abe3438fa8fa77f98ddc9f50e879d3fa7ccdc324'],
|
|
3138
|
+
},
|
|
3139
|
+
{
|
|
3140
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3141
|
+
data: '0x000000000000000000000000000000000000000000000000000000000010d8dc',
|
|
3142
|
+
logIdx: 13,
|
|
3143
|
+
topics: [
|
|
3144
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3145
|
+
'0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3146
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3147
|
+
],
|
|
3148
|
+
},
|
|
3149
|
+
{
|
|
3150
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3151
|
+
data: '0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f00000000000000000000000003ef854275a0f914a0f34db6756a59f20c82198900000000000000000000000020c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010d8dc00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000217eb7a8dfb83149821d35b62fbd7880e2fe6e29f04133156c1adb203c95d7e2a40000000000000000000000000000000000000000000000000000000000000000',
|
|
3152
|
+
logIdx: 14,
|
|
3153
|
+
topics: ['0xafbab204e8271965231d37baed9b1abca8725b7409c70314455f68bc89142b91'],
|
|
3154
|
+
},
|
|
3155
|
+
{
|
|
3156
|
+
address: '0xb92fe925dc43a0ecde6c8b1a2709c170ec4fff4f',
|
|
3157
|
+
data: '0x000000000000000000000000b92fe925dc43a0ecde6c8b1a2709c170ec4fff4f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a49bb43718000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000020c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000003ef854275a0f914a0f34db6756a59f20c8219890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000217eb7a8dfb83149821d35b62fbd7880e2fe6e29f04133156c1adb203c95d7e2a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
3158
|
+
logIdx: 15,
|
|
3159
|
+
topics: ['0x93485dcd31a905e3ffd7b012abe3438fa8fa77f98ddc9f50e879d3fa7ccdc324'],
|
|
3160
|
+
},
|
|
3161
|
+
{
|
|
3162
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3163
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000000ab',
|
|
3164
|
+
logIdx: 16,
|
|
3165
|
+
topics: [
|
|
3166
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3167
|
+
'0x00000000000000000000000058aa7ce42e1d13b2919e2ac7e006c4fbc171442c',
|
|
3168
|
+
'0x000000000000000000000000feec000000000000000000000000000000000000',
|
|
3169
|
+
],
|
|
3170
|
+
},
|
|
3171
|
+
]
|
|
3172
|
+
|
|
3173
|
+
const vaultWithdrawLogs = [
|
|
3174
|
+
{
|
|
3175
|
+
address: '0x0ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3176
|
+
data: '0x00000000000000000000000000000000000000000000000000000000156963bb00000000000000000000000000000000000000000000000000000000156963bb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
3177
|
+
logIdx: 0,
|
|
3178
|
+
topics: ['0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04'],
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
address: '0x9a044ae05e5e6290dcf56afd69548565e957a626',
|
|
3182
|
+
data: '0x000000000000000000000000000000000000000000000000000014bc099ff380000000000000000000000000000000000000000000000000000014bc09a0344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
3183
|
+
logIdx: 1,
|
|
3184
|
+
topics: ['0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04'],
|
|
3185
|
+
},
|
|
3186
|
+
{
|
|
3187
|
+
address: '0x9a044ae05e5e6290dcf56afd69548565e957a626',
|
|
3188
|
+
data: '0x00000000000000000000000000000000000000000000000000b1bcfa85011524',
|
|
3189
|
+
logIdx: 2,
|
|
3190
|
+
topics: [
|
|
3191
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3192
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3193
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
3194
|
+
],
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3198
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a0',
|
|
3199
|
+
logIdx: 3,
|
|
3200
|
+
topics: [
|
|
3201
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3202
|
+
'0x0000000000000000000000009a044ae05e5e6290dcf56afd69548565e957a626',
|
|
3203
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3204
|
+
],
|
|
3205
|
+
},
|
|
3206
|
+
{
|
|
3207
|
+
address: '0x9a044ae05e5e6290dcf56afd69548565e957a626',
|
|
3208
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000b1bcfa85011524',
|
|
3209
|
+
logIdx: 4,
|
|
3210
|
+
topics: [
|
|
3211
|
+
'0xfbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db',
|
|
3212
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3213
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3214
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3215
|
+
],
|
|
3216
|
+
},
|
|
3217
|
+
{
|
|
3218
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3219
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a0',
|
|
3220
|
+
logIdx: 5,
|
|
3221
|
+
topics: [
|
|
3222
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3223
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3224
|
+
'0x0000000000000000000000000ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3225
|
+
],
|
|
3226
|
+
},
|
|
3227
|
+
{
|
|
3228
|
+
address: '0x0ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3229
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000060fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe79600000000000000000000000000000000000000000000000000000000000000001cad5f244418b485269ca822a1e213ac83a6fed12d0c16e0741eb94e4545b05e8',
|
|
3230
|
+
logIdx: 6,
|
|
3231
|
+
topics: [
|
|
3232
|
+
'0xd602b36fb24934aef1bc2a658de029b486fa4c664a6e45de1f48e3fd1be25dd9',
|
|
3233
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3234
|
+
'0x000000000000000000000000004ad9220bce494c1f1892ee4965972fde188d51',
|
|
3235
|
+
],
|
|
3236
|
+
},
|
|
3237
|
+
{
|
|
3238
|
+
address: '0x0ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3239
|
+
data: '0x00000000000000000000000000000000000000000000000001634373939b29b4',
|
|
3240
|
+
logIdx: 7,
|
|
3241
|
+
topics: [
|
|
3242
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3243
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3244
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
3245
|
+
],
|
|
3246
|
+
},
|
|
3247
|
+
{
|
|
3248
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3249
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a0',
|
|
3250
|
+
logIdx: 8,
|
|
3251
|
+
topics: [
|
|
3252
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3253
|
+
'0x0000000000000000000000000ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3254
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3255
|
+
],
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
address: '0x0ec811f25e8fc247fe98faf32b90a48683d62f05',
|
|
3259
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000001634373939b29b4',
|
|
3260
|
+
logIdx: 9,
|
|
3261
|
+
topics: [
|
|
3262
|
+
'0xfbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db',
|
|
3263
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3264
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3265
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3266
|
+
],
|
|
3267
|
+
},
|
|
3268
|
+
{
|
|
3269
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3270
|
+
data: '0x00000000000000000000000000000000000000000000000000000000000186a0',
|
|
3271
|
+
logIdx: 10,
|
|
3272
|
+
topics: [
|
|
3273
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3274
|
+
'0x00000000000000000000000003ef854275a0f914a0f34db6756a59f20c821989',
|
|
3275
|
+
'0x00000000000000000000000076a21038ea44d8942c0765175f05d34fcfb838ab',
|
|
3276
|
+
],
|
|
3277
|
+
},
|
|
3278
|
+
{
|
|
3279
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3280
|
+
data: '0x0000000000000000000000000000000000000000000000000000000000000096',
|
|
3281
|
+
logIdx: 11,
|
|
3282
|
+
topics: [
|
|
3283
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
3284
|
+
'0x00000000000000000000000058aa7ce42e1d13b2919e2ac7e006c4fbc171442c',
|
|
3285
|
+
'0x000000000000000000000000feec000000000000000000000000000000000000',
|
|
3286
|
+
],
|
|
3287
|
+
},
|
|
3288
|
+
]
|
|
3289
|
+
|
|
3290
|
+
test('classifies a router swap instead of a lonely approval', async () => {
|
|
3291
|
+
// 0xd8be4bfb370d4395c45fb587686b0bd83b01228871896926e92d66d15f25838b
|
|
3292
|
+
// (chain 4217): 1.105 USDC.e swapped to pathUSD through a route account.
|
|
3293
|
+
const items = await classify(routerSwapLogs)
|
|
3294
|
+
expect(items.map((item) => item.type)).toEqual(['swap'])
|
|
3295
|
+
const swap = items[0]
|
|
3296
|
+
if (swap?.type !== 'swap') return
|
|
3297
|
+
expect(swap.data.sourceToken).toEqual({
|
|
3298
|
+
address: '0x20c000000000000000000000b9537d11c60e8b50',
|
|
3299
|
+
amount: '1105000',
|
|
3300
|
+
})
|
|
3301
|
+
expect(swap.data.destinationToken).toEqual({
|
|
3302
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3303
|
+
amount: '1104092',
|
|
3304
|
+
})
|
|
3305
|
+
})
|
|
3306
|
+
|
|
3307
|
+
test('classifies a batched vault withdrawal and payment separately', async () => {
|
|
3308
|
+
// 0xf438783016504c10273a74631730fef8bfd5209a655c3e1ea7a87848d6406a7c
|
|
3309
|
+
// (chain 4217): ERC-4626 withdraw of 0.10 pathUSD, then a 0.10 pathUSD
|
|
3310
|
+
// payment. The old classifier reported a single `burn`.
|
|
3311
|
+
const items = await classify(vaultWithdrawLogs)
|
|
3312
|
+
expect(items.map((item) => item.type)).toEqual(['assets-withdrawn', 'transfer'])
|
|
3313
|
+
const withdrawal = items[0]
|
|
3314
|
+
if (withdrawal?.type !== 'assets-withdrawn') return
|
|
3315
|
+
expect(withdrawal.data.vault).toBe('0x0ec811f25e8fc247fe98faf32b90a48683d62f05')
|
|
3316
|
+
expect(withdrawal.data.assets).toBe('100000')
|
|
3317
|
+
expect(withdrawal.data.receiver).toBe(viewer)
|
|
3318
|
+
const payment = items[1]
|
|
3319
|
+
if (payment?.type !== 'transfer') return
|
|
3320
|
+
expect(payment.data.direction).toBe('out')
|
|
3321
|
+
expect(payment.data.recipient).toBe('0x76a21038ea44d8942c0765175f05d34fcfb838ab')
|
|
3322
|
+
expect(payment.data.sourceToken).toEqual({
|
|
3323
|
+
address: '0x20c0000000000000000000000000000000000000',
|
|
3324
|
+
amount: '100000',
|
|
3325
|
+
})
|
|
3326
|
+
})
|
|
1486
3327
|
})
|