tapimo 0.12.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 +5561 -5026
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +38 -3
- package/dist/App.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 +123 -74
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +80 -0
- 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 +15 -15
- package/dist/admin/apps/earn-vaults.js +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +81 -81
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +151 -151
- 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.js +18 -18
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +8 -8
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
- package/dist/apps/data/ZoneSelection.js +1 -1
- package/dist/apps/data/ZoneSelection.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +588 -584
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +1561 -1239
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +26 -26
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +43 -16
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +44 -44
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +21 -11
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +3 -3
- package/dist/apps/data/routes/earn.d.ts +18 -18
- package/dist/apps/data/routes/earn.js +13 -13
- package/dist/apps/data/routes/exchanges.d.ts +145 -145
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/receipts.d.ts +155 -55
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +28 -10
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +13 -13
- package/dist/apps/data/routes/tokens.d.ts +98 -98
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +51 -20
- 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 +37 -17
- 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 +18 -12
- 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 +11 -11
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/data/routes/zones.js +1 -1
- package/dist/apps/funding/App.d.ts +11 -0
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +16 -11
- package/dist/apps/funding/routes/deposit-addresses.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 +4487 -3963
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +710 -159
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +236 -4
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +6 -6
- package/dist/apps/management/routes/invite-links.d.ts +42 -42
- package/dist/apps/management/routes/members.d.ts +30 -30
- package/dist/apps/management/routes/projects.d.ts +40 -40
- package/dist/apps/management/routes/usage.d.ts +24 -24
- package/dist/apps/management/routes/verified-token-requests.d.ts +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +55 -55
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +12 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +3 -2
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +5 -2
- package/dist/db/Schema.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/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 +3 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +6 -3
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +3 -3
- package/dist/internal/EarnVaults.js +3 -3
- 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 +5 -3
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +13 -3
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Schema.d.ts +6 -3
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +8 -2
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +16 -3
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +82 -16
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +7 -2
- 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/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/Provider.d.ts +68 -13
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +29 -8
- 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 +28 -9
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
- package/dist/internal/funding/providers/stargate.js +251 -117
- package/dist/internal/funding/providers/stargate.js.map +1 -1
- 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/package.json +2 -2
- package/src/App.test.ts +100 -0
- package/src/App.ts +42 -3
- package/src/Client.test-d.ts +46 -0
- 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 +1 -1
- package/src/admin/apps/earn-vaults.ts +1 -1
- package/src/admin/ui/src/lib/api-keys.ts +19 -0
- package/src/admin/ui/src/routes/api-keys.tsx +89 -2
- package/src/admin/ui.generated.ts +189 -151
- 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 +41 -0
- package/src/apps/data/App.ts +18 -18
- package/src/apps/data/ZoneSelection.test.ts +2 -2
- package/src/apps/data/ZoneSelection.ts +8 -8
- package/src/apps/data/routes/activities.test.ts +1526 -12
- package/src/apps/data/routes/activities.ts +1774 -1356
- package/src/apps/data/routes/balances.ts +133 -97
- package/src/apps/data/routes/blocks.test.ts +533 -0
- package/src/apps/data/routes/blocks.ts +35 -16
- package/src/apps/data/routes/earn.test.ts +4 -2
- package/src/apps/data/routes/earn.ts +13 -13
- package/src/apps/data/routes/earn.zone-access.test.ts +6 -6
- package/src/apps/data/routes/receipts.ts +47 -16
- package/src/apps/data/routes/tokens.test.ts +578 -0
- package/src/apps/data/routes/tokens.ts +283 -225
- package/src/apps/data/routes/transactions.ts +68 -29
- package/src/apps/data/routes/transfers.test.ts +421 -0
- package/src/apps/data/routes/transfers.ts +45 -27
- package/src/apps/data/routes/valuation.ts +94 -78
- package/src/apps/data/routes/zones.test.ts +2 -2
- package/src/apps/data/routes/zones.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.test.ts +25 -3
- package/src/apps/funding/routes/deposit-addresses.ts +17 -12
- package/src/apps/funding/routes/quotes.test.ts +5 -9
- package/src/apps/funding/routes/transfers.test.ts +395 -8
- package/src/apps/funding/routes/transfers.ts +76 -59
- package/src/apps/management/routes/api-keys.test.ts +220 -0
- package/src/apps/management/routes/api-keys.ts +280 -3
- package/src/db/Db.ts +13 -1
- package/src/db/Schema.ts +5 -2
- package/src/db/tables/earnVaults.test.ts +9 -9
- package/src/db/tables/fundingCatalog.test.ts +16 -2
- package/src/db/tables/fundingCatalog.ts +9 -3
- package/src/handlers/internal/utils.test.ts +53 -0
- package/src/handlers/internal/utils.ts +15 -0
- package/src/internal/Auth.ts +12 -4
- package/src/internal/EarnVaults.capabilities.test.ts +2 -2
- package/src/internal/EarnVaults.test.ts +1 -1
- package/src/internal/EarnVaults.ts +3 -3
- 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 +14 -4
- package/src/internal/Response.ts +1 -1
- package/src/internal/Schema.test.ts +2 -0
- package/src/internal/Schema.ts +12 -2
- package/src/internal/Store.test.ts +178 -0
- package/src/internal/Store.ts +104 -18
- package/src/internal/Tidx.test.ts +2 -1
- package/src/internal/Tidx.ts +7 -2
- 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/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/Provider.test.ts +73 -0
- package/src/internal/funding/Provider.ts +82 -28
- package/src/internal/funding/Transfer.ts +133 -110
- package/src/internal/funding/providers/index.test.ts +4 -167
- 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 +359 -134
- package/src/internal/index.test.ts +1 -0
- package/src/internal/index.ts +1 -0
|
@@ -46,47 +46,49 @@ export namespace schema {
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
/** A token with required RPC metadata and optional curated fields. */
|
|
49
|
-
export const Token =
|
|
50
|
-
Tokens.schema.TokenReference,
|
|
51
|
-
'
|
|
49
|
+
export const Token = OpenApi.component(
|
|
50
|
+
Schema.describe(Tokens.schema.TokenReference, 'A token referenced by an activity item.'),
|
|
51
|
+
'ActivityToken',
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
const ValuedAmount = Schema.describe(
|
|
55
|
+
z.extend(Schema.TokenAmount, {
|
|
56
|
+
valuation: z
|
|
57
|
+
.optional(z.nullable(Valuation.schema.Value))
|
|
58
|
+
.check(
|
|
59
|
+
z.describe(
|
|
60
|
+
'The amount’s nominal value in the requested `valuation.currency`. `null` when the token is ' +
|
|
61
|
+
'unverified, its display currency has no rate, or rates were unavailable.',
|
|
62
|
+
),
|
|
62
63
|
),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/** Self-contained amount for a destination token. */
|
|
67
|
-
const DestinationAmount = ValuedAmount.check(
|
|
68
|
-
z.describe('Amount associated with `destinationToken`.'),
|
|
64
|
+
}),
|
|
65
|
+
'A token amount carrying its nominal value in the requested denomination.',
|
|
69
66
|
)
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const valuedAmount = OpenApi.component(ValuedAmount, 'ActivityValuedAmount')
|
|
69
|
+
|
|
70
|
+
const DestinationAmount = valuedAmount
|
|
71
|
+
|
|
72
|
+
const RefundAmount = valuedAmount
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
const SourceAmount = ValuedAmount.check(z.describe('Amount associated with `sourceToken`.'))
|
|
74
|
+
const SourceAmount = valuedAmount
|
|
76
75
|
|
|
77
76
|
/** Page-level resources: valuation rate provenance. */
|
|
78
|
-
export const Meta =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'
|
|
77
|
+
export const Meta = OpenApi.component(
|
|
78
|
+
z
|
|
79
|
+
.object({
|
|
80
|
+
valuation: z
|
|
81
|
+
.optional(Valuation.schema.Pricing)
|
|
82
|
+
.check(
|
|
83
|
+
z.describe(
|
|
84
|
+
'Rate provenance for amount valuations. Present when conversion rates were ' +
|
|
85
|
+
'consulted; absent when every value was identity-valued or rates were unavailable.',
|
|
86
|
+
),
|
|
86
87
|
),
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
})
|
|
89
|
+
.check(z.describe('Page-level resources attached to this response.')),
|
|
90
|
+
'ActivityMeta',
|
|
91
|
+
)
|
|
90
92
|
|
|
91
93
|
/** A token transfer with subject-relative direction and signer fields. */
|
|
92
94
|
export const Transfer = z
|
|
@@ -160,6 +162,8 @@ export namespace schema {
|
|
|
160
162
|
z.describe('One token transfer involving the requested address.'),
|
|
161
163
|
)
|
|
162
164
|
|
|
165
|
+
const activityTransfer = OpenApi.component(Transfer, 'ActivityTransfer')
|
|
166
|
+
|
|
163
167
|
/**
|
|
164
168
|
* One transaction log, decoded into a known Tempo event when possible. The
|
|
165
169
|
* raw fields (`address`/`data`/`logIndex`/`topics`) are always present so no
|
|
@@ -196,6 +200,8 @@ export namespace schema {
|
|
|
196
200
|
),
|
|
197
201
|
)
|
|
198
202
|
|
|
203
|
+
const activityEvent = OpenApi.component(event, 'ActivityEvent')
|
|
204
|
+
|
|
199
205
|
/**
|
|
200
206
|
* Fields shared by every activity item. `transactionHash`/`timestamp` identify and sort
|
|
201
207
|
* the entry; `data` carries the type-specific payload — shaped like the
|
|
@@ -206,7 +212,7 @@ export namespace schema {
|
|
|
206
212
|
chainId: z
|
|
207
213
|
.optional(Schema.ChainId)
|
|
208
214
|
.check(
|
|
209
|
-
z.describe('Chain
|
|
215
|
+
z.describe('Chain ID containing this activity. Returned when `include=zones`.'),
|
|
210
216
|
z.meta({ examples: [421700001] }),
|
|
211
217
|
),
|
|
212
218
|
perspective: z
|
|
@@ -217,7 +223,7 @@ export namespace schema {
|
|
|
217
223
|
),
|
|
218
224
|
),
|
|
219
225
|
events: z
|
|
220
|
-
.optional(z.array(
|
|
226
|
+
.optional(z.array(activityEvent))
|
|
221
227
|
.check(
|
|
222
228
|
z.describe(
|
|
223
229
|
'Every log in the transaction, decoded when possible, so nothing is hidden. Returned only when the request sets `logs=true`, except on `unknown` items where it is always present.',
|
|
@@ -253,8 +259,7 @@ export namespace schema {
|
|
|
253
259
|
transactionHash: Schema.Hash.check(z.describe('Transaction hash for this activity.')),
|
|
254
260
|
}
|
|
255
261
|
|
|
256
|
-
|
|
257
|
-
export const items = [
|
|
262
|
+
const itemSchemas = [
|
|
258
263
|
z
|
|
259
264
|
.object({
|
|
260
265
|
...base,
|
|
@@ -398,7 +403,7 @@ export namespace schema {
|
|
|
398
403
|
z
|
|
399
404
|
.object({
|
|
400
405
|
...base,
|
|
401
|
-
data:
|
|
406
|
+
data: activityTransfer.check(
|
|
402
407
|
z.describe('Transfer details with feed-relative direction and signer fields.'),
|
|
403
408
|
),
|
|
404
409
|
type: z
|
|
@@ -1479,10 +1484,21 @@ export namespace schema {
|
|
|
1479
1484
|
.check(z.meta({ title: 'Unknown' })),
|
|
1480
1485
|
] as const
|
|
1481
1486
|
|
|
1487
|
+
/** The classified activity item variants (discriminated by `type`). */
|
|
1488
|
+
export const items = itemSchemas.map((item) => {
|
|
1489
|
+
const [type] = item.shape.type.def.values
|
|
1490
|
+
if (typeof type !== 'string') throw new Error('Activity item schemas require a string type.')
|
|
1491
|
+
const name = `ActivityItem${type
|
|
1492
|
+
.replace(/[^A-Za-z0-9]+(.)/g, (_, character: string) => character.toUpperCase())
|
|
1493
|
+
.replace(/^(.)/, (character) => character.toUpperCase())}`
|
|
1494
|
+
return OpenApi.component(item, name)
|
|
1495
|
+
}) as unknown as typeof itemSchemas
|
|
1496
|
+
|
|
1482
1497
|
/** A single classified activity item. */
|
|
1483
|
-
export const Item =
|
|
1484
|
-
.union(items)
|
|
1485
|
-
|
|
1498
|
+
export const Item = OpenApi.component(
|
|
1499
|
+
z.union(items).check(z.describe('One classified activity item in the address feed.')),
|
|
1500
|
+
'ActivityItem',
|
|
1501
|
+
)
|
|
1486
1502
|
|
|
1487
1503
|
/** A folded access-key group: consecutive same-signer items on one UTC day. */
|
|
1488
1504
|
export const Group = z
|
|
@@ -1490,7 +1506,7 @@ export namespace schema {
|
|
|
1490
1506
|
chainId: z
|
|
1491
1507
|
.optional(Schema.ChainId)
|
|
1492
1508
|
.check(
|
|
1493
|
-
z.describe('Chain
|
|
1509
|
+
z.describe('Chain ID containing this activity group. Returned when `include=zones`.'),
|
|
1494
1510
|
z.meta({ examples: [421700001] }),
|
|
1495
1511
|
),
|
|
1496
1512
|
data: z
|
|
@@ -1529,10 +1545,17 @@ export namespace schema {
|
|
|
1529
1545
|
z.describe('A group of consecutive activity items signed by the same access key.'),
|
|
1530
1546
|
)
|
|
1531
1547
|
|
|
1548
|
+
const activityGroup = OpenApi.component(Group, 'ActivityGroup')
|
|
1549
|
+
|
|
1532
1550
|
/** A single activity item, or a folded access-key group. */
|
|
1533
|
-
export const Entry =
|
|
1534
|
-
|
|
1535
|
-
|
|
1551
|
+
export const Entry = OpenApi.component(
|
|
1552
|
+
z
|
|
1553
|
+
.union([Item, activityGroup])
|
|
1554
|
+
.check(
|
|
1555
|
+
z.describe('Either one activity item, or a grouped set of access-key activity items.'),
|
|
1556
|
+
),
|
|
1557
|
+
'ActivityEntry',
|
|
1558
|
+
)
|
|
1536
1559
|
|
|
1537
1560
|
/** Optional activity resources and selection modes. */
|
|
1538
1561
|
export const Include = z
|
|
@@ -1542,7 +1565,7 @@ export namespace schema {
|
|
|
1542
1565
|
/** Parses comma-separated optional activity resources and selection modes. */
|
|
1543
1566
|
export const includeQuery = Schema.includeQuery(
|
|
1544
1567
|
Include,
|
|
1545
|
-
'Comma-separated activity options
|
|
1568
|
+
'Comma-separated activity options. `zones` combines the selected parent chain with every readable Zone.',
|
|
1546
1569
|
)
|
|
1547
1570
|
|
|
1548
1571
|
/** Schemas for the getAddressActivities operation. */
|
|
@@ -1596,15 +1619,18 @@ export namespace schema {
|
|
|
1596
1619
|
* `group=true`) plus the root `nextCursor` pagination field shared with
|
|
1597
1620
|
* other list endpoints.
|
|
1598
1621
|
*/
|
|
1599
|
-
export const Response =
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1622
|
+
export const Response = OpenApi.component(
|
|
1623
|
+
z
|
|
1624
|
+
.object({
|
|
1625
|
+
data: z.array(Entry).check(z.describe('Activity feed entries for the address.')),
|
|
1626
|
+
meta: z
|
|
1627
|
+
.optional(schema.Meta)
|
|
1628
|
+
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1629
|
+
nextCursor: Schema.NextCursor,
|
|
1630
|
+
})
|
|
1631
|
+
.check(z.describe('A page of human-readable activity for the address.')),
|
|
1632
|
+
'AddressActivityList',
|
|
1633
|
+
)
|
|
1608
1634
|
}
|
|
1609
1635
|
|
|
1610
1636
|
/** Schemas for the getTransactionActivities operation. */
|
|
@@ -1640,23 +1666,26 @@ export namespace schema {
|
|
|
1640
1666
|
.check(z.describe('Query parameters for the transaction activity request.'))
|
|
1641
1667
|
|
|
1642
1668
|
/**
|
|
1643
|
-
* The classified activity for one transaction: a list of `data` items
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
1669
|
+
* The classified activity for one transaction: a list of `data` items,
|
|
1670
|
+
* one per independent activity, describing what the transaction did
|
|
1671
|
+
* onchain. Unpaginated (a single transaction is finite), so there is no
|
|
1672
|
+
* `nextCursor`.
|
|
1647
1673
|
*/
|
|
1648
|
-
export const Response =
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1674
|
+
export const Response = OpenApi.component(
|
|
1675
|
+
z
|
|
1676
|
+
.object({
|
|
1677
|
+
chainId: Schema.ChainId.check(
|
|
1678
|
+
z.describe('Chain ID containing the transaction.'),
|
|
1679
|
+
z.meta({ examples: [4217] }),
|
|
1680
|
+
),
|
|
1681
|
+
data: z.array(Item).check(z.describe('Activity items classified from the transaction.')),
|
|
1682
|
+
meta: z
|
|
1683
|
+
.optional(schema.Meta)
|
|
1684
|
+
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1685
|
+
})
|
|
1686
|
+
.check(z.describe('The human-readable activity that happened on a transaction.')),
|
|
1687
|
+
'TransactionActivityList',
|
|
1688
|
+
)
|
|
1660
1689
|
}
|
|
1661
1690
|
}
|
|
1662
1691
|
|
|
@@ -1788,6 +1817,7 @@ const titleByEventName: Record<string, string> = {
|
|
|
1788
1817
|
BlacklistUpdated: 'Blacklist updated',
|
|
1789
1818
|
Burn: 'Token burned',
|
|
1790
1819
|
BurnBlocked: 'Burn blocked',
|
|
1820
|
+
Deposit: 'Assets deposited',
|
|
1791
1821
|
Deposited: 'Assets deposited',
|
|
1792
1822
|
EarnDeposit: 'Private assets deposited',
|
|
1793
1823
|
EarnRedeem: 'Private shares redeemed',
|
|
@@ -1796,6 +1826,7 @@ const titleByEventName: Record<string, string> = {
|
|
|
1796
1826
|
RedeemRequested: 'Share redemption requested',
|
|
1797
1827
|
Redeemed: 'Shares redeemed',
|
|
1798
1828
|
VenueSharesDeposited: 'Shares deposited',
|
|
1829
|
+
Withdraw: 'Assets withdrawn',
|
|
1799
1830
|
WithdrewExact: 'Assets withdrawn',
|
|
1800
1831
|
ChannelClosed: 'Channel closed',
|
|
1801
1832
|
ChannelOpened: 'Channel opened',
|
|
@@ -1863,6 +1894,33 @@ const accessKeySpendSelector = '0xe0815e3aaadddf4dd75bde97fc060f0c38afe18e87a169
|
|
|
1863
1894
|
/** TIP-20 / ERC-20 `Transfer` event signature for the decoded CTE. */
|
|
1864
1895
|
const transferSignature = 'event Transfer(address indexed from, address indexed to, uint256 value)'
|
|
1865
1896
|
|
|
1897
|
+
// Standard ERC-4626 vault events, absent from the merged `viem/tempo` ABI.
|
|
1898
|
+
// Decoding them lets external vault deposits/withdrawals classify instead of
|
|
1899
|
+
// falling through to the share-token mint/burn legs.
|
|
1900
|
+
const erc4626Events = [
|
|
1901
|
+
{
|
|
1902
|
+
inputs: [
|
|
1903
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1904
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1905
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1906
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1907
|
+
],
|
|
1908
|
+
name: 'Deposit',
|
|
1909
|
+
type: 'event',
|
|
1910
|
+
},
|
|
1911
|
+
{
|
|
1912
|
+
inputs: [
|
|
1913
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1914
|
+
{ indexed: true, name: 'receiver', type: 'address' },
|
|
1915
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1916
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1917
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1918
|
+
],
|
|
1919
|
+
name: 'Withdraw',
|
|
1920
|
+
type: 'event',
|
|
1921
|
+
},
|
|
1922
|
+
] as const
|
|
1923
|
+
|
|
1866
1924
|
// TIP-20 payment-channel reserve precompile. Not exported by `viem/tempo`
|
|
1867
1925
|
// `Addresses`, so the known address is pinned here: channel events
|
|
1868
1926
|
// (`ChannelOpened`/`Settled`/`TopUp`/`ChannelClosed`) are emitted here, and
|
|
@@ -1870,6 +1928,14 @@ const transferSignature = 'event Transfer(address indexed from, address indexed
|
|
|
1870
1928
|
// is how `channel-funded`/`channel-settled`/`channel-closed` recover the token.
|
|
1871
1929
|
const tip20ChannelReserve = '0x4d50500000000000000000000000000000000000' as Address.Address
|
|
1872
1930
|
|
|
1931
|
+
// Tempo system addresses other than the DEX are never swap-route
|
|
1932
|
+
// counterparties; a newly shipped precompile joins the exclusion automatically.
|
|
1933
|
+
const nonRouteSystemAddresses = new Set(
|
|
1934
|
+
[...Object.values(Addresses), tip20ChannelReserve]
|
|
1935
|
+
.filter((value) => value !== Addresses.stablecoinDex)
|
|
1936
|
+
.map((value) => value.toLowerCase()),
|
|
1937
|
+
)
|
|
1938
|
+
|
|
1873
1939
|
/** Number of grouped entries to target before stopping the page-fetch loop. */
|
|
1874
1940
|
const minGroupEntries = 5
|
|
1875
1941
|
|
|
@@ -1996,7 +2062,12 @@ export function addresses(options: addresses.Options = {}) {
|
|
|
1996
2062
|
'Get a human-readable feed of what an address has been doing onchain, including payments sent and received, swaps, approvals, and more.',
|
|
1997
2063
|
operationId: 'getAddressActivities',
|
|
1998
2064
|
responses: OpenApi.responses({
|
|
1999
|
-
errors: {
|
|
2065
|
+
errors: {
|
|
2066
|
+
400: {
|
|
2067
|
+
codes: ['address_invalid', 'chain_id_invalid', 'chain_id_unsupported', 'query_invalid'],
|
|
2068
|
+
},
|
|
2069
|
+
502: 'Tempo could not read activity or token data from an upstream service.',
|
|
2070
|
+
},
|
|
2000
2071
|
success: {
|
|
2001
2072
|
description: 'A page of address activity entries.',
|
|
2002
2073
|
schema: schema.getAddressActivities.Response,
|
|
@@ -2037,8 +2108,8 @@ export function addresses(options: addresses.Options = {}) {
|
|
|
2037
2108
|
const listed = await Timing.time(c, 'address_activities', async () => {
|
|
2038
2109
|
if (!query.include.includes('zones')) {
|
|
2039
2110
|
const result = await Store.memoize(
|
|
2040
|
-
() =>
|
|
2041
|
-
list(tidx({ chainId, tidx: c.get('getTidx')(chainId) }), {
|
|
2111
|
+
(signal) =>
|
|
2112
|
+
list(tidx({ chainId, signal, tidx: c.get('getTidx')(chainId) }), {
|
|
2042
2113
|
address: address as Hex.Hex,
|
|
2043
2114
|
cursor: query.cursor,
|
|
2044
2115
|
group: query.group,
|
|
@@ -2057,25 +2128,27 @@ export function addresses(options: addresses.Options = {}) {
|
|
|
2057
2128
|
|
|
2058
2129
|
const zoneChainIds = c.get('zoneChainIds')
|
|
2059
2130
|
if (!zoneChainIds) throw new Error('Inferred Zone selection was not resolved.')
|
|
2131
|
+
const chainIds = [chainId, ...zoneChainIds]
|
|
2060
2132
|
const result = await Store.memoize(
|
|
2061
|
-
() =>
|
|
2062
|
-
|
|
2133
|
+
(signal) =>
|
|
2134
|
+
listChains({
|
|
2063
2135
|
address: address as Hex.Hex,
|
|
2064
2136
|
cursor: query.cursor,
|
|
2065
2137
|
group: query.group,
|
|
2066
2138
|
limit: query.limit,
|
|
2067
2139
|
logs: query.logs,
|
|
2068
|
-
providers:
|
|
2069
|
-
chainId
|
|
2140
|
+
providers: chainIds.map((chainId) => ({
|
|
2141
|
+
chainId,
|
|
2070
2142
|
provider: tidx({
|
|
2071
|
-
chainId
|
|
2072
|
-
|
|
2143
|
+
chainId,
|
|
2144
|
+
signal,
|
|
2145
|
+
tidx: c.get('getTidx')(chainId),
|
|
2073
2146
|
}),
|
|
2074
2147
|
})),
|
|
2075
2148
|
store: c.get('store'),
|
|
2076
2149
|
}),
|
|
2077
2150
|
{
|
|
2078
|
-
key: `activities:zones:
|
|
2151
|
+
key: `activities:zones:v2:${chainIds.join(',')}:${address.toLowerCase()}:${query.group ?? false}:${query.logs ?? false}:${query.cursor ?? 'head'}:${query.limit}`,
|
|
2079
2152
|
store: c.get('store'),
|
|
2080
2153
|
ttl: Ttl.seconds(15),
|
|
2081
2154
|
},
|
|
@@ -2085,7 +2158,7 @@ export function addresses(options: addresses.Options = {}) {
|
|
|
2085
2158
|
|
|
2086
2159
|
const { items, rates } =
|
|
2087
2160
|
listed.kind === 'zones'
|
|
2088
|
-
? await
|
|
2161
|
+
? await enrichChainTokens(c, {
|
|
2089
2162
|
denomination: query['valuation.currency'],
|
|
2090
2163
|
include: query.include,
|
|
2091
2164
|
items: listed.items,
|
|
@@ -2135,7 +2208,7 @@ export declare namespace addresses {
|
|
|
2135
2208
|
* Runs the same `groupByTx` → `classifyGroup` pipeline the address feed uses
|
|
2136
2209
|
* over a single transaction's logs, anchored on the transaction sender as the
|
|
2137
2210
|
* viewer (a transaction-scoped feed has no subject account). Returns the
|
|
2138
|
-
* classified
|
|
2211
|
+
* classified items (one per independent activity) with the full per-tx
|
|
2139
2212
|
* event set available via `logs=true`.
|
|
2140
2213
|
*/
|
|
2141
2214
|
export function transactions(options: transactions.Options = {}) {
|
|
@@ -2157,12 +2230,20 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2157
2230
|
operationId: 'getTransactionActivities',
|
|
2158
2231
|
responses: OpenApi.responses({
|
|
2159
2232
|
errors: {
|
|
2233
|
+
400: {
|
|
2234
|
+
codes: [
|
|
2235
|
+
'chain_id_invalid',
|
|
2236
|
+
'chain_id_unsupported',
|
|
2237
|
+
'query_invalid',
|
|
2238
|
+
'transaction_invalid',
|
|
2239
|
+
],
|
|
2240
|
+
},
|
|
2160
2241
|
404: {
|
|
2161
2242
|
description: 'No mined transaction was found for that hash.',
|
|
2162
2243
|
codes: ['transaction_not_found'],
|
|
2163
2244
|
},
|
|
2164
2245
|
409: {
|
|
2165
|
-
description: 'More than one readable
|
|
2246
|
+
description: 'More than one readable chain contains that transaction hash.',
|
|
2166
2247
|
codes: ['transaction_ambiguous'],
|
|
2167
2248
|
},
|
|
2168
2249
|
502: 'Tempo could not read activity or token data from an upstream service.',
|
|
@@ -2210,13 +2291,14 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2210
2291
|
|
|
2211
2292
|
const zoneChainIds = c.get('zoneChainIds')
|
|
2212
2293
|
if (!zoneChainIds) throw new Error('Inferred Zone selection was not resolved.')
|
|
2294
|
+
const chainIds = [query.chainId ?? c.get('chainId'), ...zoneChainIds]
|
|
2213
2295
|
// Cache only positive resolution. Missing transactions must remain
|
|
2214
|
-
// visible as soon as
|
|
2296
|
+
// visible as soon as any selected indexer catches up.
|
|
2215
2297
|
const match = await Store.memoize(
|
|
2216
2298
|
async () => {
|
|
2217
2299
|
const probes: readonly ZoneSelection.uniqueMatch.Probe<TransactionReceipt>[] =
|
|
2218
2300
|
await Promise.all(
|
|
2219
|
-
|
|
2301
|
+
chainIds.map(async (chainId) => {
|
|
2220
2302
|
try {
|
|
2221
2303
|
return {
|
|
2222
2304
|
chainId,
|
|
@@ -2234,7 +2316,7 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2234
2316
|
return ZoneSelection.uniqueMatch<TransactionReceipt>(probes)
|
|
2235
2317
|
},
|
|
2236
2318
|
{
|
|
2237
|
-
key: `activities:zones:
|
|
2319
|
+
key: `activities:zones:v2:${chainIds.join(',')}:tx:${transactionHash.toLowerCase()}`,
|
|
2238
2320
|
shouldCache: (match) => match.kind === 'selected',
|
|
2239
2321
|
store: c.get('store'),
|
|
2240
2322
|
ttl: Ttl.seconds(15),
|
|
@@ -2259,11 +2341,12 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2259
2341
|
const unenrichedItems = await Store.memoize(
|
|
2260
2342
|
() =>
|
|
2261
2343
|
selected.receipt === null
|
|
2262
|
-
?
|
|
2344
|
+
? getTransactionActivities_inner({
|
|
2263
2345
|
chainId,
|
|
2264
2346
|
logs: query.logs,
|
|
2265
2347
|
store: c.get('store'),
|
|
2266
2348
|
tidx: c.get('getTidx')(chainId),
|
|
2349
|
+
time: (name, fn) => Timing.time(c, name, fn),
|
|
2267
2350
|
transactionHash,
|
|
2268
2351
|
})
|
|
2269
2352
|
: classifyTransactionActivities({
|
|
@@ -2272,6 +2355,7 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2272
2355
|
receipt: selected.receipt,
|
|
2273
2356
|
store: c.get('store'),
|
|
2274
2357
|
tidx: c.get('getTidx')(chainId),
|
|
2358
|
+
time: (name, fn) => Timing.time(c, name, fn),
|
|
2275
2359
|
transactionHash,
|
|
2276
2360
|
}),
|
|
2277
2361
|
{
|
|
@@ -2294,7 +2378,7 @@ export function transactions(options: transactions.Options = {}) {
|
|
|
2294
2378
|
if (result.kind === 'ambiguous')
|
|
2295
2379
|
return Response.error(c, {
|
|
2296
2380
|
code: 'transaction_ambiguous',
|
|
2297
|
-
message: 'Transaction hash matched more than one readable
|
|
2381
|
+
message: 'Transaction hash matched more than one readable chain',
|
|
2298
2382
|
status: 409,
|
|
2299
2383
|
})
|
|
2300
2384
|
|
|
@@ -2349,7 +2433,18 @@ export declare namespace transactions {
|
|
|
2349
2433
|
export async function getTransactionActivities(
|
|
2350
2434
|
options: getTransactionActivities.Options,
|
|
2351
2435
|
): Promise<UnenrichedItem[] | null> {
|
|
2352
|
-
|
|
2436
|
+
return getTransactionActivities_inner({
|
|
2437
|
+
...options,
|
|
2438
|
+
time: async (_name, fn) => fn(),
|
|
2439
|
+
})
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
async function getTransactionActivities_inner(
|
|
2443
|
+
options: TransactionActivityOptions,
|
|
2444
|
+
): Promise<UnenrichedItem[] | null> {
|
|
2445
|
+
const receipt = await options.time('transaction_activity_receipt', () =>
|
|
2446
|
+
getTransactionReceipt(options),
|
|
2447
|
+
)
|
|
2353
2448
|
if (!receipt) return null
|
|
2354
2449
|
return classifyTransactionActivities({ ...options, receipt })
|
|
2355
2450
|
}
|
|
@@ -2398,15 +2493,19 @@ async function classifyTransactionActivities(
|
|
|
2398
2493
|
const { chainId, logs = false, receipt, tidx: tidxClient } = options
|
|
2399
2494
|
const hash = options.transactionHash.toLowerCase()
|
|
2400
2495
|
const { blockNum, feePayer, txSender } = receipt
|
|
2401
|
-
const fingerprintMap_promise =
|
|
2496
|
+
const fingerprintMap_promise = options
|
|
2497
|
+
.time('transaction_activity_mpp', () => Mpp.fingerprintMap({ store: options.store }))
|
|
2498
|
+
.catch(() => ({}))
|
|
2402
2499
|
|
|
2403
2500
|
// `logs` is sorted by block position, so a bare `tx_hash =` scans to genesis;
|
|
2404
2501
|
// pair it with the receipt's block number (see `embedReceipts` in receipts.ts).
|
|
2405
|
-
const logsResult = await
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2502
|
+
const logsResult = await options.time('transaction_activity_logs', () =>
|
|
2503
|
+
tidxClient.fetch({
|
|
2504
|
+
chainId,
|
|
2505
|
+
query:
|
|
2506
|
+
`SELECT tx_hash, block_num, block_timestamp, tx_idx, log_idx, address, selector, topic1, topic2, topic3, data FROM logs WHERE block_num = ${blockNum} AND tx_hash = '${hash}' ORDER BY log_idx ASC` as string,
|
|
2507
|
+
}),
|
|
2508
|
+
)
|
|
2410
2509
|
|
|
2411
2510
|
const rows: Row[] = logsResult.rows.map((r) => ({
|
|
2412
2511
|
blockNum: Number(Value.toNumber(r['block_num'])),
|
|
@@ -2442,7 +2541,7 @@ async function classifyTransactionActivities(
|
|
|
2442
2541
|
}
|
|
2443
2542
|
|
|
2444
2543
|
declare namespace classifyTransactionActivities {
|
|
2445
|
-
type Options =
|
|
2544
|
+
type Options = TransactionActivityOptions & {
|
|
2446
2545
|
/** Indexed receipt fields used to bound the log lookup. */
|
|
2447
2546
|
receipt: TransactionReceipt
|
|
2448
2547
|
}
|
|
@@ -2464,6 +2563,13 @@ export declare namespace getTransactionActivities {
|
|
|
2464
2563
|
}
|
|
2465
2564
|
}
|
|
2466
2565
|
|
|
2566
|
+
type TransactionActivityOptions = getTransactionActivities.Options & {
|
|
2567
|
+
time: <value>(
|
|
2568
|
+
name: 'transaction_activity_logs' | 'transaction_activity_mpp' | 'transaction_activity_receipt',
|
|
2569
|
+
fn: () => Promise<value> | value,
|
|
2570
|
+
) => Promise<value>
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2467
2573
|
/**
|
|
2468
2574
|
* Lists classified activity for an address by running the shared pipeline
|
|
2469
2575
|
* (`groupByTx` → `classifyGroup` → `enrichItem`) over rows from a
|
|
@@ -2544,38 +2650,38 @@ export declare namespace list {
|
|
|
2544
2650
|
}
|
|
2545
2651
|
}
|
|
2546
2652
|
|
|
2547
|
-
const
|
|
2653
|
+
const chainCursorVersion = 2
|
|
2548
2654
|
|
|
2549
|
-
type
|
|
2550
|
-
/**
|
|
2655
|
+
type ChainPageState = {
|
|
2656
|
+
/** Chain ID backing this page state. */
|
|
2551
2657
|
chainId: number
|
|
2552
|
-
/** Whether the current buffer reaches the end of this
|
|
2658
|
+
/** Whether the current buffer reaches the end of this chain feed. */
|
|
2553
2659
|
exhausted: boolean
|
|
2554
|
-
/** Unconsumed transaction groups already fetched from this
|
|
2660
|
+
/** Unconsumed transaction groups already fetched from this chain. */
|
|
2555
2661
|
groups: TxGroup[]
|
|
2556
|
-
/** Activity data source for this
|
|
2662
|
+
/** Activity data source for this chain. */
|
|
2557
2663
|
provider: Provider
|
|
2558
2664
|
}
|
|
2559
2665
|
|
|
2560
2666
|
/**
|
|
2561
|
-
* Lists address activity across multiple
|
|
2562
|
-
* feed. Its opaque cursor retains each
|
|
2667
|
+
* Lists address activity across multiple chains in one stable timestamp-ordered
|
|
2668
|
+
* feed. Its opaque cursor retains each provider's native continuation.
|
|
2563
2669
|
*/
|
|
2564
|
-
export async function
|
|
2670
|
+
export async function listChains(options: listChains.Options): Promise<listChains.ReturnType> {
|
|
2565
2671
|
const { address, cursor, limit = 20, logs = false, providers, store } = options
|
|
2566
2672
|
const group = options.group ?? false
|
|
2567
2673
|
const orderedProviders = [...providers].sort((a, b) => a.chainId - b.chainId)
|
|
2568
2674
|
const chainIds = orderedProviders.map(({ chainId }) => chainId)
|
|
2569
|
-
const cursors =
|
|
2675
|
+
const cursors = decodeChainCursor(cursor, chainIds)
|
|
2570
2676
|
const viewer = address.toLowerCase() as Address.Address
|
|
2571
2677
|
const fingerprintMap_promise = Mpp.fingerprintMap({ store }).catch(() => ({}))
|
|
2572
|
-
const states:
|
|
2678
|
+
const states: ChainPageState[] = orderedProviders.map(({ chainId, provider }) => ({
|
|
2573
2679
|
chainId,
|
|
2574
2680
|
exhausted: false,
|
|
2575
2681
|
groups: [],
|
|
2576
2682
|
provider,
|
|
2577
2683
|
}))
|
|
2578
|
-
const allItems:
|
|
2684
|
+
const allItems: listChains.Item[] = []
|
|
2579
2685
|
let hasMore = false
|
|
2580
2686
|
|
|
2581
2687
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
@@ -2643,30 +2749,30 @@ export async function listZones(options: listZones.Options): Promise<listZones.R
|
|
|
2643
2749
|
|
|
2644
2750
|
return {
|
|
2645
2751
|
items: allItems,
|
|
2646
|
-
nextCursor: hasMore ?
|
|
2752
|
+
nextCursor: hasMore ? encodeChainCursor(cursors, chainIds) : null,
|
|
2647
2753
|
}
|
|
2648
2754
|
}
|
|
2649
2755
|
|
|
2650
|
-
export declare namespace
|
|
2651
|
-
/** One
|
|
2756
|
+
export declare namespace listChains {
|
|
2757
|
+
/** One chain activity provider. */
|
|
2652
2758
|
type ChainProvider = {
|
|
2653
|
-
/**
|
|
2759
|
+
/** Chain ID. */
|
|
2654
2760
|
chainId: number
|
|
2655
|
-
/** Activity data source for the
|
|
2761
|
+
/** Activity data source for the chain. */
|
|
2656
2762
|
provider: Provider
|
|
2657
2763
|
}
|
|
2658
2764
|
|
|
2659
|
-
/** A classified item tagged with its
|
|
2765
|
+
/** A classified item tagged with its chain ID. */
|
|
2660
2766
|
type Item = UnenrichedItem & {
|
|
2661
|
-
/**
|
|
2767
|
+
/** Chain ID containing the activity. */
|
|
2662
2768
|
chainId: number
|
|
2663
2769
|
}
|
|
2664
2770
|
|
|
2665
|
-
/** Options for {@link
|
|
2771
|
+
/** Options for {@link listChains}. */
|
|
2666
2772
|
type Options = {
|
|
2667
2773
|
/** Subject account address. */
|
|
2668
2774
|
address: Address.Address
|
|
2669
|
-
/** Opaque multi-
|
|
2775
|
+
/** Opaque multi-chain cursor from a previous page. */
|
|
2670
2776
|
cursor?: string | undefined
|
|
2671
2777
|
/** Whether the caller will fold access-key items. */
|
|
2672
2778
|
group?: boolean | undefined
|
|
@@ -2674,27 +2780,27 @@ export declare namespace listZones {
|
|
|
2674
2780
|
limit?: number | undefined
|
|
2675
2781
|
/** Attach each item's full transaction event capture. */
|
|
2676
2782
|
logs?: boolean | undefined
|
|
2677
|
-
/**
|
|
2783
|
+
/** Chain providers participating in the merged feed. */
|
|
2678
2784
|
providers: readonly ChainProvider[]
|
|
2679
2785
|
/** Cache store for the MPP fingerprint-directory lookup. */
|
|
2680
2786
|
store: Store.Store
|
|
2681
2787
|
}
|
|
2682
2788
|
|
|
2683
|
-
/** Multi-
|
|
2789
|
+
/** Multi-chain classified page before token enrichment. */
|
|
2684
2790
|
type ReturnType = {
|
|
2685
2791
|
/** Timestamp-ordered, chain-tagged activity items. */
|
|
2686
2792
|
items: Item[]
|
|
2687
|
-
/** Opaque continuation for every
|
|
2793
|
+
/** Opaque continuation for every chain, or null at the end. */
|
|
2688
2794
|
nextCursor: string | null
|
|
2689
2795
|
}
|
|
2690
2796
|
}
|
|
2691
2797
|
|
|
2692
|
-
function
|
|
2798
|
+
function decodeChainCursor(cursor: string | undefined, chainIds: readonly number[]) {
|
|
2693
2799
|
const fields: Cursor.Field[] = ['int']
|
|
2694
2800
|
for (const _chainId of chainIds) fields.push('int', 'id')
|
|
2695
2801
|
const decoded = cursor ? Cursor.decode(cursor, fields) : undefined
|
|
2696
2802
|
const cursors = new Map<number, string>()
|
|
2697
|
-
if (!decoded || decoded[0] !==
|
|
2803
|
+
if (!decoded || decoded[0] !== chainCursorVersion) return cursors
|
|
2698
2804
|
|
|
2699
2805
|
for (let index = 0; index < chainIds.length; index++) {
|
|
2700
2806
|
const chainId = chainIds[index]!
|
|
@@ -2706,9 +2812,9 @@ function decodeZoneCursor(cursor: string | undefined, chainIds: readonly number[
|
|
|
2706
2812
|
return cursors
|
|
2707
2813
|
}
|
|
2708
2814
|
|
|
2709
|
-
function
|
|
2815
|
+
function encodeChainCursor(cursors: ReadonlyMap<number, string>, chainIds: readonly number[]) {
|
|
2710
2816
|
return Cursor.encode([
|
|
2711
|
-
|
|
2817
|
+
chainCursorVersion,
|
|
2712
2818
|
...chainIds.flatMap((chainId) => [chainId, cursors.get(chainId) ?? 'head']),
|
|
2713
2819
|
])
|
|
2714
2820
|
}
|
|
@@ -2723,7 +2829,18 @@ function encodeZoneCursor(cursors: ReadonlyMap<number, string>, chainIds: readon
|
|
|
2723
2829
|
* (catches access-key-signed sends where `tx.from` differs from the address).
|
|
2724
2830
|
*/
|
|
2725
2831
|
export function tidx(deps: tidx.Deps): Provider {
|
|
2726
|
-
const { chainId, tidx: tidxClient } = deps
|
|
2832
|
+
const { chainId, signal, tidx: tidxClient } = deps
|
|
2833
|
+
|
|
2834
|
+
const fetch: Tidx.Client['fetch'] = (options) => {
|
|
2835
|
+
const requestSignal =
|
|
2836
|
+
signal && options.signal
|
|
2837
|
+
? AbortSignal.any([signal, options.signal])
|
|
2838
|
+
: (signal ?? options.signal)
|
|
2839
|
+
return tidxClient.fetch({
|
|
2840
|
+
...options,
|
|
2841
|
+
...(requestSignal ? { signal: requestSignal } : {}),
|
|
2842
|
+
})
|
|
2843
|
+
}
|
|
2727
2844
|
|
|
2728
2845
|
function parseCursor(cursor: string): { blockNum: number; txIdx: number } | undefined {
|
|
2729
2846
|
// Opaque `[blockNum, txIdx]` keyset cursor, the same encoding every other
|
|
@@ -2749,7 +2866,7 @@ export function tidx(deps: tidx.Deps): Provider {
|
|
|
2749
2866
|
: ''
|
|
2750
2867
|
const queryLimit = limit + 1
|
|
2751
2868
|
const fetchReceipts = (side: 'fee_payer' | 'from') =>
|
|
2752
|
-
|
|
2869
|
+
fetch({
|
|
2753
2870
|
chainId,
|
|
2754
2871
|
// Raw-table query (no signature-decoded CTE), so the inline SQL is cast
|
|
2755
2872
|
// to `string`.
|
|
@@ -2793,7 +2910,7 @@ export function tidx(deps: tidx.Deps): Provider {
|
|
|
2793
2910
|
|
|
2794
2911
|
// `logs` is not keyed by transaction hash, so pair the bounded hash list
|
|
2795
2912
|
// with the selected receipts' block range to avoid a full archive scan.
|
|
2796
|
-
const logs = await
|
|
2913
|
+
const logs = await fetch({
|
|
2797
2914
|
chainId,
|
|
2798
2915
|
// Raw-table query (no signature-decoded CTE), so the inline SQL is cast
|
|
2799
2916
|
// to `string`.
|
|
@@ -2853,7 +2970,7 @@ export function tidx(deps: tidx.Deps): Provider {
|
|
|
2853
2970
|
`
|
|
2854
2971
|
const result = await (async () => {
|
|
2855
2972
|
try {
|
|
2856
|
-
return await
|
|
2973
|
+
return await fetch({
|
|
2857
2974
|
chainId,
|
|
2858
2975
|
query: query('block_num'),
|
|
2859
2976
|
signatures: [transferSignature],
|
|
@@ -2862,14 +2979,14 @@ export function tidx(deps: tidx.Deps): Provider {
|
|
|
2862
2979
|
if (!Tidx.isDeterministicError(error)) throw error
|
|
2863
2980
|
try {
|
|
2864
2981
|
// The expression blocks a mis-selected block-ordered top-N walk.
|
|
2865
|
-
return await
|
|
2982
|
+
return await fetch({
|
|
2866
2983
|
chainId,
|
|
2867
2984
|
query: query('block_num + 0'),
|
|
2868
2985
|
signatures: [transferSignature],
|
|
2869
2986
|
})
|
|
2870
2987
|
} catch (fallbackError) {
|
|
2871
2988
|
if (!Tidx.isDeterministicError(fallbackError)) throw fallbackError
|
|
2872
|
-
const result = await
|
|
2989
|
+
const result = await fetch({
|
|
2873
2990
|
chainId,
|
|
2874
2991
|
query: query(),
|
|
2875
2992
|
signatures: [transferSignature],
|
|
@@ -2924,6 +3041,8 @@ export declare namespace tidx {
|
|
|
2924
3041
|
type Deps = {
|
|
2925
3042
|
/** Source chain id. */
|
|
2926
3043
|
chainId: number
|
|
3044
|
+
/** Optional deadline signal shared by this provider's TIDX reads. */
|
|
3045
|
+
signal?: AbortSignal | undefined
|
|
2927
3046
|
/** TIDX query client. */
|
|
2928
3047
|
tidx: Tidx.Client
|
|
2929
3048
|
}
|
|
@@ -2938,8 +3057,10 @@ export function mergeAndSort(q1: readonly Row[], q2: readonly Row[], q3: readonl
|
|
|
2938
3057
|
const q1Hashes = new Set(q1.map((r) => r.txHash))
|
|
2939
3058
|
const filteredQ2 = q2.filter((r) => !q1Hashes.has(r.txHash))
|
|
2940
3059
|
const filteredQ3 = q3.filter((r) => !q1Hashes.has(r.txHash))
|
|
2941
|
-
|
|
2942
|
-
|
|
3060
|
+
// Dedup by row, not transaction: a tx that both credits and debits the
|
|
3061
|
+
// viewer keeps its rows from both sides, one item each.
|
|
3062
|
+
const q2Keys = new Set(filteredQ2.map((r) => `${r.txHash}:${r.logIdx}`))
|
|
3063
|
+
const dedupedQ3 = filteredQ3.filter((r) => !q2Keys.has(`${r.txHash}:${r.logIdx}`))
|
|
2943
3064
|
return [...q1, ...filteredQ2, ...dedupedQ3].sort((a, b) => {
|
|
2944
3065
|
if (a.blockNum !== b.blockNum) return b.blockNum - a.blockNum
|
|
2945
3066
|
return b.txIdx - a.txIdx
|
|
@@ -3016,34 +3137,38 @@ function classifyGroup(
|
|
|
3016
3137
|
viewer,
|
|
3017
3138
|
})
|
|
3018
3139
|
|
|
3019
|
-
// Q2-only
|
|
3020
|
-
//
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3140
|
+
// Q2-only: transfer rows without a joined receipt, one item per row so a
|
|
3141
|
+
// batch paying the viewer several times surfaces every payment. Classify by
|
|
3142
|
+
// comparing `from`/`to` against the viewer (zero-address sides mint/burn).
|
|
3143
|
+
if (q2Rows.length > 0)
|
|
3144
|
+
return [...q2Rows]
|
|
3145
|
+
.sort((a, b) => a.logIdx - b.logIdx)
|
|
3146
|
+
.map((r) => {
|
|
3147
|
+
const base = itemBase(r.logIdx)
|
|
3148
|
+
const from = r.transferFrom!
|
|
3149
|
+
const to = r.transferTo!
|
|
3150
|
+
const sourceToken = token(r.transferAmount, r.transferToken!)
|
|
3151
|
+
if (Address.isEqual(from, zeroAddress as Address.Address))
|
|
3152
|
+
return { ...base, data: { recipient: viewer, sourceToken }, type: 'mint' }
|
|
3153
|
+
if (Address.isEqual(to, zeroAddress as Address.Address))
|
|
3154
|
+
return { ...base, data: { sender: from, signer, sourceToken }, type: 'burn' }
|
|
3155
|
+
// Q2-only rows carry no memo, so MPP service attribution is unavailable
|
|
3156
|
+
// here; `attribution` is resolved only on the log-bearing (Q1) path.
|
|
3157
|
+
if (Address.isEqual(from, viewer))
|
|
3158
|
+
return transfer(base, blockNum, {
|
|
3159
|
+
direction: 'out',
|
|
3160
|
+
recipient: to,
|
|
3161
|
+
sender: viewer,
|
|
3162
|
+
signer,
|
|
3163
|
+
sourceToken,
|
|
3164
|
+
})
|
|
3165
|
+
return transfer(base, blockNum, {
|
|
3166
|
+
direction: 'in',
|
|
3167
|
+
recipient: viewer,
|
|
3168
|
+
sender: from,
|
|
3040
3169
|
sourceToken,
|
|
3041
|
-
})
|
|
3042
|
-
|
|
3043
|
-
return [
|
|
3044
|
-
transfer(base, blockNum, { direction: 'in', recipient: viewer, sender: from, sourceToken }),
|
|
3045
|
-
]
|
|
3046
|
-
}
|
|
3170
|
+
})
|
|
3171
|
+
})
|
|
3047
3172
|
|
|
3048
3173
|
// Fallback — should not happen (a group with neither q1 nor q2 rows).
|
|
3049
3174
|
return [
|
|
@@ -3082,7 +3207,7 @@ function classifyFromLogs(
|
|
|
3082
3207
|
})()
|
|
3083
3208
|
// When the caller requested raw logs, attach the full per-transaction log set
|
|
3084
3209
|
// (decoded when possible) to every classified item so no co-located log is
|
|
3085
|
-
// hidden
|
|
3210
|
+
// hidden. `unknown` items already carry it.
|
|
3086
3211
|
if (!ctx.includeLogs) return items
|
|
3087
3212
|
const events = buildTxEvents(rows, decoded)
|
|
3088
3213
|
return items.map((item) => (item.type === 'unknown' ? item : { ...item, events }))
|
|
@@ -3106,7 +3231,11 @@ function signerForRows(
|
|
|
3106
3231
|
: options.fallback
|
|
3107
3232
|
}
|
|
3108
3233
|
|
|
3109
|
-
/**
|
|
3234
|
+
/**
|
|
3235
|
+
* Classifies every activity in a Q1 (log-bearing) transaction. Recognizers run
|
|
3236
|
+
* in priority order over a pool of decoded events, each emitting one item per
|
|
3237
|
+
* matched anchor and claiming the exact logs backing it.
|
|
3238
|
+
*/
|
|
3110
3239
|
function classifyFromLogsCore(
|
|
3111
3240
|
rows: Row[],
|
|
3112
3241
|
ctx: {
|
|
@@ -3124,97 +3253,196 @@ function classifyFromLogsCore(
|
|
|
3124
3253
|
|
|
3125
3254
|
const minLogIndex = Math.min(...rows.map((r) => r.logIdx))
|
|
3126
3255
|
|
|
3127
|
-
//
|
|
3128
|
-
//
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
const refundTransfer = events.find(
|
|
3134
|
-
(e) =>
|
|
3135
|
-
(e.eventName === 'Transfer' || e.eventName === 'TransferWithMemo') &&
|
|
3136
|
-
'to' in e.args &&
|
|
3137
|
-
Address.isEqual((e.args as { to: Address.Address }).to, viewer),
|
|
3138
|
-
)
|
|
3139
|
-
const refund = refundTransfer
|
|
3140
|
-
? token(
|
|
3141
|
-
(refundTransfer.args as { amount: bigint }).amount,
|
|
3142
|
-
refundTransfer.address as Address.Address,
|
|
3143
|
-
)
|
|
3144
|
-
: undefined
|
|
3145
|
-
return [
|
|
3146
|
-
{
|
|
3147
|
-
...itemBase(closeRequestedLogIndex),
|
|
3148
|
-
data: { ...(refund ? { refund } : {}), signer },
|
|
3149
|
-
type: 'session-closed',
|
|
3150
|
-
},
|
|
3151
|
-
]
|
|
3152
|
-
}
|
|
3153
|
-
|
|
3154
|
-
// Dedup preference: TransferWithMemo beats Transfer; Mint/Burn beat
|
|
3155
|
-
// zero-address Transfer.
|
|
3156
|
-
const preferenceMap = new Map<string, string>()
|
|
3256
|
+
// Pair folds are counted: each TransferWithMemo folds one matching plain
|
|
3257
|
+
// Transfer and each Mint/Burn event folds one zero-address transfer, so an
|
|
3258
|
+
// extra identical payment survives as its own item. Keys require the full
|
|
3259
|
+
// event shape; a malformed event must not fold away a classifiable leg.
|
|
3260
|
+
const foldCounts = new Map<string, number>()
|
|
3261
|
+
const addFold = (key: string) => foldCounts.set(key, (foldCounts.get(key) ?? 0) + 1)
|
|
3157
3262
|
for (const event of events) {
|
|
3158
3263
|
if (event.eventName === 'TransferWithMemo' && 'from' in event.args && 'to' in event.args) {
|
|
3159
3264
|
const args = event.args as { amount: bigint; from: Address.Address; to: Address.Address }
|
|
3160
|
-
|
|
3265
|
+
addFold(`${event.address}:${args.from}:${args.to}:${args.amount}`)
|
|
3161
3266
|
}
|
|
3162
|
-
if (event.eventName === 'Mint' && 'amount' in event.args)
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3267
|
+
if (event.eventName === 'Mint' && 'amount' in event.args && 'to' in event.args)
|
|
3268
|
+
addFold(`mint:${event.address}:${(event.args as { amount: bigint }).amount}`)
|
|
3269
|
+
if (event.eventName === 'Burn' && 'amount' in event.args && 'from' in event.args)
|
|
3270
|
+
addFold(`burn:${event.address}:${(event.args as { amount: bigint }).amount}`)
|
|
3271
|
+
}
|
|
3272
|
+
const takeFold = (key: string) => {
|
|
3273
|
+
const count = foldCounts.get(key) ?? 0
|
|
3274
|
+
if (count === 0) return false
|
|
3275
|
+
foldCounts.set(key, count - 1)
|
|
3276
|
+
return true
|
|
3172
3277
|
}
|
|
3173
3278
|
|
|
3174
|
-
const
|
|
3175
|
-
if (event.eventName !== 'Transfer') return true
|
|
3279
|
+
const finalEvents = events.filter((event) => {
|
|
3280
|
+
if (event.eventName !== 'Transfer' && event.eventName !== 'TransferWithMemo') return true
|
|
3281
|
+
if (!('from' in event.args) || !('to' in event.args)) return true
|
|
3176
3282
|
const args = event.args as { amount: bigint; from: Address.Address; to: Address.Address }
|
|
3177
|
-
if (preferenceMap.has(`${args.from}:${args.to}:${args.amount}`)) return false
|
|
3178
|
-
if (
|
|
3179
|
-
Address.isEqual(args.from, zeroAddress as Address.Address) &&
|
|
3180
|
-
preferenceMap.has(`mint:${event.address}:${args.amount}`)
|
|
3181
|
-
)
|
|
3182
|
-
return false
|
|
3183
3283
|
if (
|
|
3184
|
-
|
|
3185
|
-
|
|
3284
|
+
event.eventName === 'Transfer' &&
|
|
3285
|
+
takeFold(`${event.address}:${args.from}:${args.to}:${args.amount}`)
|
|
3186
3286
|
)
|
|
3187
3287
|
return false
|
|
3188
|
-
return true
|
|
3189
|
-
})
|
|
3190
|
-
|
|
3191
|
-
const finalEvents = dedupedEvents.filter((event) => {
|
|
3192
|
-
if (event.eventName !== 'TransferWithMemo') return true
|
|
3193
|
-
const args = event.args as { amount: bigint; from: Address.Address; to: Address.Address }
|
|
3194
3288
|
if (
|
|
3195
3289
|
Address.isEqual(args.from, zeroAddress as Address.Address) &&
|
|
3196
|
-
|
|
3290
|
+
takeFold(`mint:${event.address}:${args.amount}`)
|
|
3197
3291
|
)
|
|
3198
3292
|
return false
|
|
3199
3293
|
if (
|
|
3200
3294
|
Address.isEqual(args.to, zeroAddress as Address.Address) &&
|
|
3201
|
-
|
|
3295
|
+
takeFold(`burn:${event.address}:${args.amount}`)
|
|
3202
3296
|
)
|
|
3203
3297
|
return false
|
|
3204
3298
|
return true
|
|
3205
3299
|
})
|
|
3206
3300
|
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3301
|
+
type Decoded = (typeof finalEvents)[number]
|
|
3302
|
+
|
|
3303
|
+
const pool = [...finalEvents].sort((a, b) => a.logIndex - b.logIndex)
|
|
3304
|
+
const claimed = new Set<number>()
|
|
3305
|
+
const items: RawItem[] = []
|
|
3306
|
+
// Claims made while building an item are journaled, so a build that throws
|
|
3307
|
+
// on a malformed event releases its legs and consumes only the anchor.
|
|
3308
|
+
let journal: number[] | undefined
|
|
3309
|
+
const claim = (event: Decoded) => {
|
|
3310
|
+
claimed.add(event.logIndex)
|
|
3311
|
+
journal?.push(event.logIndex)
|
|
3312
|
+
}
|
|
3313
|
+
// A predicate throwing on a malformed decode must not discard the whole
|
|
3314
|
+
// transaction; treat that event as a non-match instead.
|
|
3315
|
+
const unclaimed = (predicate?: (event: Decoded) => boolean) =>
|
|
3316
|
+
pool.filter((event) => {
|
|
3317
|
+
if (claimed.has(event.logIndex)) return false
|
|
3318
|
+
if (!predicate) return true
|
|
3319
|
+
try {
|
|
3320
|
+
return predicate(event)
|
|
3321
|
+
} catch {
|
|
3322
|
+
return false
|
|
3323
|
+
}
|
|
3324
|
+
})
|
|
3325
|
+
const lc = (value: Address.Address) => value.toLowerCase() as Address.Address
|
|
3326
|
+
const hx = (value: Hex.Hex) => value.toLowerCase() as Hex.Hex
|
|
3327
|
+
|
|
3328
|
+
// Non-strict decoding can surface Transfer-shaped events with malformed
|
|
3329
|
+
// args; validating (once per event) keeps predicate comparisons throw-free.
|
|
3330
|
+
const transferIndexes = new Set(
|
|
3331
|
+
pool
|
|
3332
|
+
.filter((event) => {
|
|
3333
|
+
if (event.eventName !== 'Transfer' && event.eventName !== 'TransferWithMemo') return false
|
|
3334
|
+
const args = event.args as { amount?: unknown; from?: unknown; to?: unknown }
|
|
3335
|
+
return (
|
|
3336
|
+
typeof args.amount === 'bigint' &&
|
|
3337
|
+
typeof args.from === 'string' &&
|
|
3338
|
+
Address.validate(args.from) &&
|
|
3339
|
+
typeof args.to === 'string' &&
|
|
3340
|
+
Address.validate(args.to)
|
|
3341
|
+
)
|
|
3342
|
+
})
|
|
3343
|
+
.map((event) => event.logIndex),
|
|
3213
3344
|
)
|
|
3345
|
+
const isTransfer = (event: Decoded) => transferIndexes.has(event.logIndex)
|
|
3346
|
+
const transferArgs = (event: Decoded) =>
|
|
3347
|
+
event.args as {
|
|
3348
|
+
amount: bigint
|
|
3349
|
+
from: Address.Address
|
|
3350
|
+
memo?: Hex.Hex | undefined
|
|
3351
|
+
to: Address.Address
|
|
3352
|
+
}
|
|
3353
|
+
// Finds and claims the first unclaimed event matching `predicate`, so a
|
|
3354
|
+
// later identical anchor pairs with the next matching leg instead of
|
|
3355
|
+
// reusing this one.
|
|
3356
|
+
const takeEvent = (predicate: (event: Decoded) => boolean) => {
|
|
3357
|
+
for (const event of pool) {
|
|
3358
|
+
if (claimed.has(event.logIndex)) continue
|
|
3359
|
+
const match = (() => {
|
|
3360
|
+
try {
|
|
3361
|
+
return predicate(event)
|
|
3362
|
+
} catch {
|
|
3363
|
+
return false
|
|
3364
|
+
}
|
|
3365
|
+
})()
|
|
3366
|
+
if (!match) continue
|
|
3367
|
+
claim(event)
|
|
3368
|
+
return event
|
|
3369
|
+
}
|
|
3370
|
+
return undefined
|
|
3371
|
+
}
|
|
3372
|
+
const takeTransfer = (predicate: (event: Decoded) => boolean) =>
|
|
3373
|
+
takeEvent((event) => isTransfer(event) && predicate(event))
|
|
3374
|
+
|
|
3375
|
+
// Runs `build` once per unclaimed matching event, in log order; `build`
|
|
3376
|
+
// claims its legs and returns the item(s) to emit, or nothing for
|
|
3377
|
+
// fold-only recognitions. A throwing build consumes only its anchor.
|
|
3378
|
+
const recognize = (
|
|
3379
|
+
predicate: (event: Decoded) => boolean,
|
|
3380
|
+
build: (event: Decoded) => RawItem | readonly RawItem[] | undefined,
|
|
3381
|
+
) => {
|
|
3382
|
+
for (const anchor of unclaimed(predicate)) {
|
|
3383
|
+
// A build may claim a later anchor of the same pass (or a log decoded
|
|
3384
|
+
// into duplicate ABI copies); never process a claimed anchor again.
|
|
3385
|
+
if (claimed.has(anchor.logIndex)) continue
|
|
3386
|
+
claim(anchor)
|
|
3387
|
+
journal = []
|
|
3388
|
+
try {
|
|
3389
|
+
const built = build(anchor)
|
|
3390
|
+
if (built) items.push(...(Array.isArray(built) ? built : [built as RawItem]))
|
|
3391
|
+
} catch {
|
|
3392
|
+
// Indexed logs can match a known signature without satisfying that
|
|
3393
|
+
// event's expected shape; release the legs and skip the anchor.
|
|
3394
|
+
for (const index of journal ?? []) claimed.delete(index)
|
|
3395
|
+
} finally {
|
|
3396
|
+
journal = undefined
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
const named = (name: string) => (event: Decoded) => event.eventName === name
|
|
3401
|
+
|
|
3402
|
+
// Claims and returns a vault operation's token legs, matched by role and
|
|
3403
|
+
// amount: asset legs in or out of the vault, share mint/burn legs on it.
|
|
3404
|
+
const claimVaultLegs = (vault: Address.Address, amounts: readonly bigint[]) => {
|
|
3405
|
+
const legs: Decoded[] = []
|
|
3406
|
+
for (const event of unclaimed()) {
|
|
3407
|
+
const isVaultMintBurn =
|
|
3408
|
+
(event.eventName === 'Mint' || event.eventName === 'Burn') &&
|
|
3409
|
+
Address.isEqual(event.address, vault)
|
|
3410
|
+
const isVaultLeg = (() => {
|
|
3411
|
+
if (!isTransfer(event)) return false
|
|
3412
|
+
const args = transferArgs(event)
|
|
3413
|
+
if (Address.isEqual(args.from, vault) || Address.isEqual(args.to, vault)) return true
|
|
3414
|
+
return (
|
|
3415
|
+
Address.isEqual(event.address, vault) &&
|
|
3416
|
+
(Address.isEqual(args.from, zeroAddress as Address.Address) ||
|
|
3417
|
+
Address.isEqual(args.to, zeroAddress as Address.Address))
|
|
3418
|
+
)
|
|
3419
|
+
})()
|
|
3420
|
+
if (!isVaultMintBurn && !isVaultLeg) continue
|
|
3421
|
+
const amount = (event.args as { amount?: bigint }).amount
|
|
3422
|
+
if (amount === undefined || !amounts.includes(amount)) continue
|
|
3423
|
+
claim(event)
|
|
3424
|
+
legs.push(event)
|
|
3425
|
+
}
|
|
3426
|
+
// The allowance granted for this operation (approve+deposit multicall) is
|
|
3427
|
+
// internal: exact amount, spender is the vault, on a token a leg moved.
|
|
3428
|
+
const legTokens = new Set(legs.map((leg) => leg.address.toLowerCase()))
|
|
3429
|
+
for (const event of unclaimed()) {
|
|
3430
|
+
if (event.eventName !== 'Approval' || !('spender' in event.args)) continue
|
|
3431
|
+
if (!legTokens.has(event.address.toLowerCase())) continue
|
|
3432
|
+
if (!Address.isEqual((event.args as { spender: Address.Address }).spender, vault)) continue
|
|
3433
|
+
const amount = (event.args as { amount?: bigint }).amount
|
|
3434
|
+
if (amount === undefined || !amounts.includes(amount)) continue
|
|
3435
|
+
claim(event)
|
|
3436
|
+
legs.push(event)
|
|
3437
|
+
}
|
|
3438
|
+
return legs
|
|
3439
|
+
}
|
|
3214
3440
|
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3441
|
+
// Private Earn router operations. Each wraps a public vault operation on the
|
|
3442
|
+
// vault it names; fold that vault's own event and token legs, leaving vault
|
|
3443
|
+
// activity elsewhere in the batch unclaimed.
|
|
3444
|
+
recognize(named('EarnDeposit'), (event) => {
|
|
3445
|
+
const args = event.args as {
|
|
3218
3446
|
actionId: Hex.Hex
|
|
3219
3447
|
earnShares: bigint
|
|
3220
3448
|
earnVault: Address.Address
|
|
@@ -3223,28 +3451,38 @@ function classifyFromLogsCore(
|
|
|
3223
3451
|
vaultAssets: bigint
|
|
3224
3452
|
zoneDepositHash: Hex.Hex
|
|
3225
3453
|
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3454
|
+
const vault = lc(args.earnVault)
|
|
3455
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
3456
|
+
const data = {
|
|
3457
|
+
actionId: args.actionId,
|
|
3458
|
+
assets: args.vaultAssets.toString(),
|
|
3459
|
+
inputAmount: args.inputAmount.toString(),
|
|
3460
|
+
inputToken: lc(args.inputToken),
|
|
3461
|
+
shares: args.earnShares.toString(),
|
|
3462
|
+
signer,
|
|
3463
|
+
status: 'completed' as const,
|
|
3464
|
+
vault,
|
|
3465
|
+
zoneDepositHash: args.zoneDepositHash,
|
|
3466
|
+
}
|
|
3467
|
+
// Fold the vault event this router call produced: the nearest preceding
|
|
3468
|
+
// amount match, so a separate same-vault deposit keeps its own.
|
|
3469
|
+
const nested = unclaimed(
|
|
3470
|
+
(e) =>
|
|
3471
|
+
e.eventName === 'Deposited' &&
|
|
3472
|
+
e.logIndex < event.logIndex &&
|
|
3473
|
+
Address.isEqual(e.address, vault) &&
|
|
3474
|
+
(e.args as { assets?: bigint }).assets === args.vaultAssets,
|
|
3475
|
+
).at(-1)
|
|
3476
|
+
if (nested) claim(nested)
|
|
3477
|
+
else takeEvent((e) => e.eventName === 'Deposited' && Address.isEqual(e.address, vault))
|
|
3478
|
+
claimVaultLegs(vault, [args.inputAmount, args.vaultAssets, args.earnShares])
|
|
3479
|
+
// The funding leg pays the router (the emitting contract), not the vault.
|
|
3480
|
+
claimVaultLegs(event.address as Address.Address, [args.inputAmount])
|
|
3481
|
+
return { ...itemBase(event.logIndex), data, type: 'private-assets-deposited' }
|
|
3482
|
+
})
|
|
3244
3483
|
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
const args = privateRedeem.args as {
|
|
3484
|
+
recognize(named('EarnRedeem'), (event) => {
|
|
3485
|
+
const args = event.args as {
|
|
3248
3486
|
actionId: Hex.Hex
|
|
3249
3487
|
earnShares: bigint
|
|
3250
3488
|
earnVault: Address.Address
|
|
@@ -3253,782 +3491,1025 @@ function classifyFromLogsCore(
|
|
|
3253
3491
|
vaultAssets: bigint
|
|
3254
3492
|
zoneDepositHash: Hex.Hex
|
|
3255
3493
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3494
|
+
const vault = lc(args.earnVault)
|
|
3495
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
3496
|
+
const data = {
|
|
3497
|
+
actionId: args.actionId,
|
|
3498
|
+
assets: args.vaultAssets.toString(),
|
|
3499
|
+
outputAmount: args.outputAmount.toString(),
|
|
3500
|
+
outputToken: lc(args.outputToken),
|
|
3501
|
+
shares: args.earnShares.toString(),
|
|
3502
|
+
signer,
|
|
3503
|
+
status: 'completed' as const,
|
|
3504
|
+
vault,
|
|
3505
|
+
zoneDepositHash: args.zoneDepositHash,
|
|
3506
|
+
}
|
|
3507
|
+
// Fold the vault event this router call produced: the nearest preceding
|
|
3508
|
+
// amount match, so a separate same-vault redemption keeps its own.
|
|
3509
|
+
const nested = unclaimed(
|
|
3510
|
+
(e) =>
|
|
3511
|
+
(e.eventName === 'Redeemed' || e.eventName === 'WithdrewExact') &&
|
|
3512
|
+
e.logIndex < event.logIndex &&
|
|
3513
|
+
Address.isEqual(e.address, vault) &&
|
|
3514
|
+
(e.args as { assets?: bigint }).assets === args.vaultAssets,
|
|
3515
|
+
).at(-1)
|
|
3516
|
+
if (nested) claim(nested)
|
|
3517
|
+
else
|
|
3518
|
+
takeEvent(
|
|
3519
|
+
(e) =>
|
|
3520
|
+
(e.eventName === 'Redeemed' || e.eventName === 'WithdrewExact') &&
|
|
3521
|
+
Address.isEqual(e.address, vault),
|
|
3522
|
+
)
|
|
3523
|
+
claimVaultLegs(vault, [args.outputAmount, args.vaultAssets, args.earnShares])
|
|
3524
|
+
// The payout leg leaves through the router (the emitting contract).
|
|
3525
|
+
claimVaultLegs(event.address as Address.Address, [args.outputAmount])
|
|
3526
|
+
return { ...itemBase(event.logIndex), data, type: 'private-shares-redeemed' }
|
|
3527
|
+
})
|
|
3274
3528
|
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
const args =
|
|
3529
|
+
// Earn vault operations, each claiming its own token legs.
|
|
3530
|
+
recognize(named('Deposited'), (event) => {
|
|
3531
|
+
const args = event.args as {
|
|
3278
3532
|
assets: bigint
|
|
3279
3533
|
caller: Address.Address
|
|
3280
3534
|
earnShares: bigint
|
|
3281
3535
|
receiver: Address.Address
|
|
3282
3536
|
}
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
},
|
|
3295
|
-
type: 'assets-deposited',
|
|
3537
|
+
claimVaultLegs(event.address as Address.Address, [args.assets, args.earnShares])
|
|
3538
|
+
return {
|
|
3539
|
+
...itemBase(event.logIndex),
|
|
3540
|
+
data: {
|
|
3541
|
+
assets: args.assets.toString(),
|
|
3542
|
+
caller: lc(args.caller),
|
|
3543
|
+
receiver: lc(args.receiver),
|
|
3544
|
+
shares: args.earnShares.toString(),
|
|
3545
|
+
signer,
|
|
3546
|
+
status: 'completed',
|
|
3547
|
+
vault: lc(event.address),
|
|
3296
3548
|
},
|
|
3297
|
-
|
|
3298
|
-
|
|
3549
|
+
type: 'assets-deposited',
|
|
3550
|
+
}
|
|
3551
|
+
})
|
|
3299
3552
|
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
const args = venueDeposit.args as {
|
|
3553
|
+
recognize(named('VenueSharesDeposited'), (event) => {
|
|
3554
|
+
const args = event.args as {
|
|
3303
3555
|
caller: Address.Address
|
|
3304
3556
|
earnShares: bigint
|
|
3305
3557
|
receivedEngineShares: bigint
|
|
3306
3558
|
receiver: Address.Address
|
|
3307
3559
|
requestedVenueShares: bigint
|
|
3308
3560
|
}
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3561
|
+
claimVaultLegs(event.address as Address.Address, [
|
|
3562
|
+
args.earnShares,
|
|
3563
|
+
args.receivedEngineShares,
|
|
3564
|
+
args.requestedVenueShares,
|
|
3565
|
+
])
|
|
3566
|
+
return {
|
|
3567
|
+
...itemBase(event.logIndex),
|
|
3568
|
+
data: {
|
|
3569
|
+
caller: lc(args.caller),
|
|
3570
|
+
receivedEngineShares: args.receivedEngineShares.toString(),
|
|
3571
|
+
receiver: lc(args.receiver),
|
|
3572
|
+
requestedVenueShares: args.requestedVenueShares.toString(),
|
|
3573
|
+
shares: args.earnShares.toString(),
|
|
3574
|
+
signer,
|
|
3575
|
+
status: 'completed',
|
|
3576
|
+
vault: lc(event.address),
|
|
3323
3577
|
},
|
|
3324
|
-
|
|
3325
|
-
|
|
3578
|
+
type: 'shares-deposited',
|
|
3579
|
+
}
|
|
3580
|
+
})
|
|
3326
3581
|
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
const args = redeemed.args as {
|
|
3582
|
+
recognize(named('Redeemed'), (event) => {
|
|
3583
|
+
const args = event.args as {
|
|
3330
3584
|
assets: bigint
|
|
3331
3585
|
caller: Address.Address
|
|
3332
3586
|
earnShares: bigint
|
|
3333
3587
|
receiver: Address.Address
|
|
3334
3588
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
},
|
|
3347
|
-
type: 'shares-redeemed',
|
|
3589
|
+
claimVaultLegs(event.address as Address.Address, [args.assets, args.earnShares])
|
|
3590
|
+
return {
|
|
3591
|
+
...itemBase(event.logIndex),
|
|
3592
|
+
data: {
|
|
3593
|
+
assets: args.assets.toString(),
|
|
3594
|
+
caller: lc(args.caller),
|
|
3595
|
+
receiver: lc(args.receiver),
|
|
3596
|
+
shares: args.earnShares.toString(),
|
|
3597
|
+
signer,
|
|
3598
|
+
status: 'completed',
|
|
3599
|
+
vault: lc(event.address),
|
|
3348
3600
|
},
|
|
3349
|
-
|
|
3350
|
-
|
|
3601
|
+
type: 'shares-redeemed',
|
|
3602
|
+
}
|
|
3603
|
+
})
|
|
3351
3604
|
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
const args = withdrew.args as {
|
|
3605
|
+
recognize(named('WithdrewExact'), (event) => {
|
|
3606
|
+
const args = event.args as {
|
|
3355
3607
|
assets: bigint
|
|
3356
3608
|
caller: Address.Address
|
|
3357
3609
|
earnSharesBurned: bigint
|
|
3358
3610
|
receiver: Address.Address
|
|
3359
3611
|
}
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
},
|
|
3372
|
-
type: 'assets-withdrawn',
|
|
3612
|
+
claimVaultLegs(event.address as Address.Address, [args.assets, args.earnSharesBurned])
|
|
3613
|
+
return {
|
|
3614
|
+
...itemBase(event.logIndex),
|
|
3615
|
+
data: {
|
|
3616
|
+
assets: args.assets.toString(),
|
|
3617
|
+
caller: lc(args.caller),
|
|
3618
|
+
receiver: lc(args.receiver),
|
|
3619
|
+
sharesBurned: args.earnSharesBurned.toString(),
|
|
3620
|
+
signer,
|
|
3621
|
+
status: 'completed',
|
|
3622
|
+
vault: lc(event.address),
|
|
3373
3623
|
},
|
|
3374
|
-
|
|
3375
|
-
|
|
3624
|
+
type: 'assets-withdrawn',
|
|
3625
|
+
}
|
|
3626
|
+
})
|
|
3376
3627
|
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
const args = requested.args as {
|
|
3628
|
+
recognize(named('RedeemRequested'), (event) => {
|
|
3629
|
+
const args = event.args as {
|
|
3380
3630
|
earnShares: bigint
|
|
3381
3631
|
receiver: Address.Address
|
|
3382
3632
|
requestId: Hex.Hex
|
|
3383
3633
|
requester: Address.Address
|
|
3384
3634
|
}
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
},
|
|
3397
|
-
type: 'shares-redemption-requested',
|
|
3635
|
+
claimVaultLegs(event.address as Address.Address, [args.earnShares])
|
|
3636
|
+
return {
|
|
3637
|
+
...itemBase(event.logIndex),
|
|
3638
|
+
data: {
|
|
3639
|
+
receiver: lc(args.receiver),
|
|
3640
|
+
requestId: args.requestId,
|
|
3641
|
+
requester: lc(args.requester),
|
|
3642
|
+
shares: args.earnShares.toString(),
|
|
3643
|
+
signer,
|
|
3644
|
+
status: 'pending',
|
|
3645
|
+
vault: lc(event.address),
|
|
3398
3646
|
},
|
|
3399
|
-
|
|
3400
|
-
|
|
3647
|
+
type: 'shares-redemption-requested',
|
|
3648
|
+
}
|
|
3649
|
+
})
|
|
3401
3650
|
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
const args = finalized.args as {
|
|
3651
|
+
recognize(named('RedeemFinalized'), (event) => {
|
|
3652
|
+
const args = event.args as {
|
|
3405
3653
|
asset: Address.Address
|
|
3406
3654
|
assets: bigint
|
|
3407
3655
|
earnShares: bigint
|
|
3408
3656
|
receiver: Address.Address
|
|
3409
3657
|
requestId: Hex.Hex
|
|
3410
3658
|
}
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
},
|
|
3424
|
-
type: 'shares-redemption-finalized',
|
|
3659
|
+
claimVaultLegs(event.address as Address.Address, [args.assets, args.earnShares])
|
|
3660
|
+
return {
|
|
3661
|
+
...itemBase(event.logIndex),
|
|
3662
|
+
data: {
|
|
3663
|
+
asset: lc(args.asset),
|
|
3664
|
+
assets: args.assets.toString(),
|
|
3665
|
+
receiver: lc(args.receiver),
|
|
3666
|
+
requestId: args.requestId,
|
|
3667
|
+
shares: args.earnShares.toString(),
|
|
3668
|
+
signer,
|
|
3669
|
+
status: 'completed',
|
|
3670
|
+
vault: lc(event.address),
|
|
3425
3671
|
},
|
|
3426
|
-
|
|
3427
|
-
|
|
3672
|
+
type: 'shares-redemption-finalized',
|
|
3673
|
+
}
|
|
3674
|
+
})
|
|
3428
3675
|
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
const args = cancelled.args as {
|
|
3676
|
+
recognize(named('RedeemCancelled'), (event) => {
|
|
3677
|
+
const args = event.args as {
|
|
3432
3678
|
earnShares: bigint
|
|
3433
3679
|
receiver: Address.Address
|
|
3434
3680
|
requestId: Hex.Hex
|
|
3435
3681
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
},
|
|
3447
|
-
type: 'shares-redemption-cancelled',
|
|
3682
|
+
claimVaultLegs(event.address as Address.Address, [args.earnShares])
|
|
3683
|
+
return {
|
|
3684
|
+
...itemBase(event.logIndex),
|
|
3685
|
+
data: {
|
|
3686
|
+
receiver: lc(args.receiver),
|
|
3687
|
+
requestId: args.requestId,
|
|
3688
|
+
shares: args.earnShares.toString(),
|
|
3689
|
+
signer,
|
|
3690
|
+
status: 'cancelled',
|
|
3691
|
+
vault: lc(event.address),
|
|
3448
3692
|
},
|
|
3449
|
-
|
|
3450
|
-
|
|
3693
|
+
type: 'shares-redemption-cancelled',
|
|
3694
|
+
}
|
|
3695
|
+
})
|
|
3451
3696
|
|
|
3452
|
-
//
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
const
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
toDex.address as Address.Address,
|
|
3697
|
+
// External ERC-4626 vaults. Claim the share and asset legs unconditionally
|
|
3698
|
+
// so nested vault plumbing never resurfaces as burns or transfers, but emit
|
|
3699
|
+
// only operations the viewer is a party to. Fold-only recognitions are
|
|
3700
|
+
// buffered and surface only when nothing else classified.
|
|
3701
|
+
const suppressedVaultItems: RawItem[] = []
|
|
3702
|
+
const viewerInvolved = (parties: readonly Address.Address[], legs: readonly Decoded[]) =>
|
|
3703
|
+
parties.some((party) => Address.isEqual(party, viewer)) ||
|
|
3704
|
+
legs.some(
|
|
3705
|
+
(leg) =>
|
|
3706
|
+
isTransfer(leg) &&
|
|
3707
|
+
(Address.isEqual(transferArgs(leg).from, viewer) ||
|
|
3708
|
+
Address.isEqual(transferArgs(leg).to, viewer)),
|
|
3465
3709
|
)
|
|
3710
|
+
recognize(named('Withdraw'), (event) => {
|
|
3711
|
+
const args = event.args as {
|
|
3712
|
+
assets: bigint
|
|
3713
|
+
owner: Address.Address
|
|
3714
|
+
receiver: Address.Address
|
|
3715
|
+
sender: Address.Address
|
|
3716
|
+
shares: bigint
|
|
3717
|
+
}
|
|
3718
|
+
const vault = event.address as Address.Address
|
|
3719
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
3720
|
+
const data = {
|
|
3721
|
+
assets: args.assets.toString(),
|
|
3722
|
+
caller: lc(args.sender),
|
|
3723
|
+
receiver: lc(args.receiver),
|
|
3724
|
+
sharesBurned: args.shares.toString(),
|
|
3725
|
+
signer,
|
|
3726
|
+
status: 'completed' as const,
|
|
3727
|
+
vault: lc(vault),
|
|
3728
|
+
}
|
|
3729
|
+
const legs = claimVaultLegs(vault, [args.assets, args.shares])
|
|
3730
|
+
const item = { ...itemBase(event.logIndex), data, type: 'assets-withdrawn' as const }
|
|
3731
|
+
if (viewerInvolved([args.sender, args.receiver, args.owner], legs)) return item
|
|
3732
|
+
suppressedVaultItems.push(item)
|
|
3733
|
+
return undefined
|
|
3734
|
+
})
|
|
3466
3735
|
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
const delivery = transfers.find(
|
|
3474
|
-
(e) =>
|
|
3475
|
-
Address.isEqual(e.address, destTokenAddress) &&
|
|
3476
|
-
Address.isEqual((e.args as { from: Address.Address }).from, swapper) &&
|
|
3477
|
-
!Address.isEqual((e.args as { to: Address.Address }).to, Addresses.stablecoinDex) &&
|
|
3478
|
-
!Address.isEqual((e.args as { to: Address.Address }).to, swapper),
|
|
3479
|
-
)
|
|
3480
|
-
if (delivery) {
|
|
3481
|
-
const args = delivery.args as {
|
|
3482
|
-
amount: bigint
|
|
3483
|
-
from: Address.Address
|
|
3484
|
-
memo?: Hex.Hex | undefined
|
|
3485
|
-
to: Address.Address
|
|
3486
|
-
}
|
|
3487
|
-
const rawMemo = delivery.eventName === 'TransferWithMemo' ? args.memo : undefined
|
|
3488
|
-
const memo = rawMemo ? Transfers.memoToString(rawMemo) : undefined
|
|
3489
|
-
const attribution = Mpp.resolve({ fingerprintMap, memo: rawMemo })
|
|
3490
|
-
return [
|
|
3491
|
-
transfer(itemBase(logIndexOfEvent(delivery)), blockNum, {
|
|
3492
|
-
attribution,
|
|
3493
|
-
destinationToken: token(args.amount, delivery.address as Address.Address),
|
|
3494
|
-
direction: Address.isEqual(args.to, viewer) ? 'in' : 'out',
|
|
3495
|
-
memo,
|
|
3496
|
-
recipient: args.to.toLowerCase() as Address.Address,
|
|
3497
|
-
sender: swapper.toLowerCase() as Address.Address,
|
|
3498
|
-
signer,
|
|
3499
|
-
sourceToken,
|
|
3500
|
-
}),
|
|
3501
|
-
]
|
|
3736
|
+
recognize(named('Deposit'), (event) => {
|
|
3737
|
+
const args = event.args as {
|
|
3738
|
+
assets: bigint
|
|
3739
|
+
owner: Address.Address
|
|
3740
|
+
sender: Address.Address
|
|
3741
|
+
shares: bigint
|
|
3502
3742
|
}
|
|
3743
|
+
const vault = event.address as Address.Address
|
|
3744
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
3745
|
+
const data = {
|
|
3746
|
+
assets: args.assets.toString(),
|
|
3747
|
+
caller: lc(args.sender),
|
|
3748
|
+
receiver: lc(args.owner),
|
|
3749
|
+
shares: args.shares.toString(),
|
|
3750
|
+
signer,
|
|
3751
|
+
status: 'completed' as const,
|
|
3752
|
+
vault: lc(vault),
|
|
3753
|
+
}
|
|
3754
|
+
const legs = claimVaultLegs(vault, [args.assets, args.shares])
|
|
3755
|
+
const item = { ...itemBase(event.logIndex), data, type: 'assets-deposited' as const }
|
|
3756
|
+
if (viewerInvolved([args.sender, args.owner], legs)) return item
|
|
3757
|
+
suppressedVaultItems.push(item)
|
|
3758
|
+
return undefined
|
|
3759
|
+
})
|
|
3503
3760
|
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3761
|
+
// DEX order placed: `OrderPlaced` carries the escrow token/amount and side;
|
|
3762
|
+
// claim the escrow transfer that funded it.
|
|
3763
|
+
const orderPlacedItem = (event: Decoded): RawItem => {
|
|
3764
|
+
const args = event.args as {
|
|
3765
|
+
amount: bigint
|
|
3766
|
+
isBid: boolean
|
|
3767
|
+
orderId: bigint
|
|
3768
|
+
tick: number
|
|
3769
|
+
token: Address.Address
|
|
3770
|
+
}
|
|
3771
|
+
const escrow = (leg: Decoded) =>
|
|
3772
|
+
isTransfer(leg) &&
|
|
3773
|
+
Address.isEqual(leg.address, args.token) &&
|
|
3774
|
+
Address.isEqual(transferArgs(leg).to, event.address as Address.Address)
|
|
3775
|
+
// The escrow funds the placement, so it is the nearest preceding match; a
|
|
3776
|
+
// same-token earlier swap input must not be taken instead.
|
|
3777
|
+
const escrowLeg =
|
|
3778
|
+
unclaimed(
|
|
3779
|
+
(leg) =>
|
|
3780
|
+
escrow(leg) && leg.logIndex < event.logIndex && transferArgs(leg).amount === args.amount,
|
|
3781
|
+
).at(-1) ?? unclaimed((leg) => escrow(leg) && leg.logIndex < event.logIndex).at(-1)
|
|
3782
|
+
if (escrowLeg) claim(escrowLeg)
|
|
3783
|
+
return {
|
|
3784
|
+
...itemBase(event.logIndex),
|
|
3785
|
+
data: {
|
|
3786
|
+
orderId: args.orderId.toString(),
|
|
3787
|
+
side: args.isBid ? 'bid' : 'ask',
|
|
3788
|
+
signer,
|
|
3789
|
+
sourceToken: token(args.amount, args.token),
|
|
3790
|
+
tick: Number(args.tick),
|
|
3513
3791
|
},
|
|
3514
|
-
|
|
3792
|
+
type: 'order-placed',
|
|
3793
|
+
}
|
|
3515
3794
|
}
|
|
3795
|
+
const isFlipOrder = (event: Decoded) =>
|
|
3796
|
+
'isFlipOrder' in event.args && (event.args as { isFlipOrder: boolean }).isFlipOrder === true
|
|
3797
|
+
// Non-flip order escrow legs claim before swap pairing so they cannot
|
|
3798
|
+
// mispair with an unrelated swap's output leg.
|
|
3799
|
+
recognize((event) => event.eventName === 'OrderPlaced' && !isFlipOrder(event), orderPlacedItem)
|
|
3800
|
+
|
|
3801
|
+
// Swap: a transfer into a route paired with a different token coming back
|
|
3802
|
+
// out. The DEX matches any swapper; other routes must be viewer-bounded
|
|
3803
|
+
// with route-emitted logs between the legs.
|
|
3804
|
+
{
|
|
3805
|
+
// Outgoing transfers indexed by lowercase sender, for O(candidates)
|
|
3806
|
+
// destination lookups. Claimed entries are skipped at scan time.
|
|
3807
|
+
const transfersByFrom = new Map<string, Decoded[]>()
|
|
3808
|
+
for (const event of pool) {
|
|
3809
|
+
if (!isTransfer(event)) continue
|
|
3810
|
+
const key = transferArgs(event).from.toLowerCase()
|
|
3811
|
+
const list = transfersByFrom.get(key)
|
|
3812
|
+
if (list) list.push(event)
|
|
3813
|
+
else transfersByFrom.set(key, [event])
|
|
3814
|
+
}
|
|
3815
|
+
// One forward pass: claims only shrink the pool, so a source that finds
|
|
3816
|
+
// no destination now never finds one later, and scanning each source once
|
|
3817
|
+
// keeps the cost linear in sources instead of restarting per pair.
|
|
3818
|
+
for (const source of unclaimed(isTransfer)) {
|
|
3819
|
+
if (claimed.has(source.logIndex)) continue
|
|
3820
|
+
const sourceArgs = transferArgs(source)
|
|
3821
|
+
const route = sourceArgs.to
|
|
3822
|
+
const routeKey = route.toLowerCase()
|
|
3823
|
+
// Tempo system addresses (the reserve, fee manager, outbox, and every
|
|
3824
|
+
// other non-DEX precompile) are never swap routes; new precompiles join
|
|
3825
|
+
// the exclusion automatically.
|
|
3826
|
+
if (nonRouteSystemAddresses.has(routeKey)) continue
|
|
3827
|
+
if (Address.isEqual(route, zeroAddress as Address.Address)) continue
|
|
3828
|
+
if (Address.isEqual(route, viewer)) continue
|
|
3829
|
+
const isDex = Address.isEqual(route, Addresses.stablecoinDex)
|
|
3830
|
+
if (!isDex && !Address.isEqual(sourceArgs.from, viewer)) continue
|
|
3831
|
+
const candidates = (transfersByFrom.get(routeKey) ?? []).filter((event) => {
|
|
3832
|
+
if (claimed.has(event.logIndex) || event.logIndex <= source.logIndex) return false
|
|
3833
|
+
if (Address.isEqual(event.address, source.address)) return false
|
|
3834
|
+
return !Address.isEqual(transferArgs(event).to, Addresses.feeManager)
|
|
3835
|
+
})
|
|
3836
|
+
// Prefer the output paying the swapper back (so interleaved swaps by
|
|
3837
|
+
// different accounts keep their own outputs); otherwise a DEX pairs
|
|
3838
|
+
// first-in-first-out, and a route's last leg is a direct delivery
|
|
3839
|
+
// (earlier different-token legs are intermediate hops).
|
|
3840
|
+
const destination =
|
|
3841
|
+
candidates.find((event) => Address.isEqual(transferArgs(event).to, sourceArgs.from)) ??
|
|
3842
|
+
(isDex ? candidates[0] : candidates.at(-1))
|
|
3843
|
+
if (!destination) continue
|
|
3844
|
+
// Route evidence for non-precompile routes: the route itself emitted a
|
|
3845
|
+
// log between the legs (raw rows, so undecoded router events count). A
|
|
3846
|
+
// counterparty that merely received and later paid back is not a route.
|
|
3847
|
+
if (!isDex) {
|
|
3848
|
+
const evidence = rows.some(
|
|
3849
|
+
(r) =>
|
|
3850
|
+
r.logIdx > source.logIndex &&
|
|
3851
|
+
r.logIdx < destination.logIndex &&
|
|
3852
|
+
r.logAddress.toLowerCase() === routeKey,
|
|
3853
|
+
)
|
|
3854
|
+
if (!evidence) continue
|
|
3855
|
+
}
|
|
3856
|
+
journal = []
|
|
3857
|
+
try {
|
|
3858
|
+
claim(source)
|
|
3859
|
+
claim(destination)
|
|
3860
|
+
const destinationArgs = transferArgs(destination)
|
|
3861
|
+
const swapper = isDex ? destinationArgs.to : sourceArgs.from
|
|
3862
|
+
const destTokenAddress = destination.address as Address.Address
|
|
3863
|
+
const sourceToken = token(sourceArgs.amount, source.address as Address.Address)
|
|
3864
|
+
|
|
3865
|
+
// Router plumbing between the legs: route-touching or zero-address
|
|
3866
|
+
// token moves and router-side approvals, never viewer-party transfers.
|
|
3867
|
+
// DEX-precompile pairs claim nothing here, so an interleaved swap by
|
|
3868
|
+
// another account keeps its own legs.
|
|
3869
|
+
if (!isDex)
|
|
3870
|
+
for (const event of unclaimed()) {
|
|
3871
|
+
if (event.logIndex <= source.logIndex || event.logIndex >= destination.logIndex)
|
|
3872
|
+
continue
|
|
3873
|
+
if (isTransfer(event)) {
|
|
3874
|
+
const args = transferArgs(event)
|
|
3875
|
+
if (Address.isEqual(args.from, viewer) || Address.isEqual(args.to, viewer)) continue
|
|
3876
|
+
if (
|
|
3877
|
+
Address.isEqual(args.from, route) ||
|
|
3878
|
+
Address.isEqual(args.to, route) ||
|
|
3879
|
+
Address.isEqual(args.from, zeroAddress as Address.Address) ||
|
|
3880
|
+
Address.isEqual(args.to, zeroAddress as Address.Address)
|
|
3881
|
+
)
|
|
3882
|
+
claim(event)
|
|
3883
|
+
} else if (
|
|
3884
|
+
event.eventName === 'Approval' &&
|
|
3885
|
+
'owner' in event.args &&
|
|
3886
|
+
!Address.isEqual((event.args as { owner: Address.Address }).owner, viewer)
|
|
3887
|
+
)
|
|
3888
|
+
claim(event)
|
|
3889
|
+
}
|
|
3516
3890
|
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3891
|
+
// The allowance granted for this swap: same token, owner, and amount,
|
|
3892
|
+
// and causally tied to the input leg (spender is the route, or the
|
|
3893
|
+
// approval immediately precedes it). Unrelated approvals stay.
|
|
3894
|
+
unclaimed(
|
|
3895
|
+
(event) =>
|
|
3896
|
+
event.eventName === 'Approval' &&
|
|
3897
|
+
'amount' in event.args &&
|
|
3898
|
+
'owner' in event.args &&
|
|
3899
|
+
'spender' in event.args &&
|
|
3900
|
+
Address.isEqual(event.address, source.address) &&
|
|
3901
|
+
Address.isEqual((event.args as { owner: Address.Address }).owner, sourceArgs.from) &&
|
|
3902
|
+
(event.args as { amount: bigint }).amount === sourceArgs.amount &&
|
|
3903
|
+
event.logIndex < source.logIndex &&
|
|
3904
|
+
(Address.isEqual((event.args as { spender: Address.Address }).spender, route) ||
|
|
3905
|
+
event.logIndex === source.logIndex - 1),
|
|
3906
|
+
)
|
|
3907
|
+
.slice(-1)
|
|
3908
|
+
.forEach(claim)
|
|
3909
|
+
|
|
3910
|
+
// DEX bookkeeping this swap produced: fills against resting orders and
|
|
3911
|
+
// the auto-flip re-placement, acted by the swapper or its route account.
|
|
3912
|
+
// A fill of the viewer's own resting order stays its own item.
|
|
3913
|
+
const isBookkeepingActor = (value: Address.Address) =>
|
|
3914
|
+
Address.isEqual(value, swapper) || Address.isEqual(value, route)
|
|
3915
|
+
unclaimed(
|
|
3916
|
+
(event) =>
|
|
3917
|
+
(Address.isEqual(event.address, route) ||
|
|
3918
|
+
Address.isEqual(event.address, Addresses.stablecoinDex)) &&
|
|
3919
|
+
((event.eventName === 'OrderFilled' &&
|
|
3920
|
+
'taker' in event.args &&
|
|
3921
|
+
isBookkeepingActor((event.args as { taker: Address.Address }).taker) &&
|
|
3922
|
+
!(
|
|
3923
|
+
'maker' in event.args &&
|
|
3924
|
+
Address.isEqual((event.args as { maker: Address.Address }).maker, viewer)
|
|
3925
|
+
)) ||
|
|
3926
|
+
(event.eventName === 'OrderPlaced' &&
|
|
3927
|
+
isFlipOrder(event) &&
|
|
3928
|
+
'maker' in event.args &&
|
|
3929
|
+
isBookkeepingActor((event.args as { maker: Address.Address }).maker))),
|
|
3930
|
+
).forEach(claim)
|
|
3931
|
+
|
|
3932
|
+
// Cross-token transfer: the swapped-out token is forwarded whole from
|
|
3933
|
+
// the swapper to a new recipient after the swap (the account server's
|
|
3934
|
+
// approve+swap+transfer multicall), or the route delivered it to the
|
|
3935
|
+
// recipient directly; fold both shapes into one `transfer`. A leg
|
|
3936
|
+
// whose recipient later forwards a different token is the input of a
|
|
3937
|
+
// chained swap, not a delivery.
|
|
3938
|
+
const forwardsOn = (candidate: Decoded) => {
|
|
3939
|
+
const args = transferArgs(candidate)
|
|
3940
|
+
return (transfersByFrom.get(args.to.toLowerCase()) ?? []).some(
|
|
3941
|
+
(onward) =>
|
|
3942
|
+
!claimed.has(onward.logIndex) &&
|
|
3943
|
+
onward.logIndex > candidate.logIndex &&
|
|
3944
|
+
!Address.isEqual(onward.address, candidate.address),
|
|
3945
|
+
)
|
|
3946
|
+
}
|
|
3947
|
+
const delivery =
|
|
3948
|
+
!isDex && !Address.isEqual(destinationArgs.to, viewer)
|
|
3949
|
+
? destination
|
|
3950
|
+
: takeTransfer((event) => {
|
|
3951
|
+
const args = transferArgs(event)
|
|
3952
|
+
return (
|
|
3953
|
+
event.logIndex > destination.logIndex &&
|
|
3954
|
+
Address.isEqual(event.address, destTokenAddress) &&
|
|
3955
|
+
Address.isEqual(args.from, swapper) &&
|
|
3956
|
+
args.amount === destinationArgs.amount &&
|
|
3957
|
+
!Address.isEqual(args.to, route) &&
|
|
3958
|
+
!Address.isEqual(args.to, swapper) &&
|
|
3959
|
+
!forwardsOn(event)
|
|
3960
|
+
)
|
|
3961
|
+
})
|
|
3962
|
+
// A malformed delivery memo degrades to a plain swap item; the claimed
|
|
3963
|
+
// legs must still produce an activity.
|
|
3964
|
+
const item = (() => {
|
|
3965
|
+
if (!delivery) return undefined
|
|
3966
|
+
try {
|
|
3967
|
+
const args = transferArgs(delivery)
|
|
3968
|
+
const rawMemo = delivery.eventName === 'TransferWithMemo' ? args.memo : undefined
|
|
3969
|
+
const memo = rawMemo ? Transfers.memoToString(rawMemo) : undefined
|
|
3970
|
+
const attribution = Mpp.resolve({ fingerprintMap, memo: rawMemo })
|
|
3971
|
+
return transfer(itemBase(delivery.logIndex), blockNum, {
|
|
3972
|
+
attribution,
|
|
3973
|
+
destinationToken: token(args.amount, delivery.address as Address.Address),
|
|
3974
|
+
direction: Address.isEqual(args.to, viewer) ? ('in' as const) : ('out' as const),
|
|
3975
|
+
memo,
|
|
3976
|
+
recipient: lc(args.to),
|
|
3977
|
+
sender: lc(swapper),
|
|
3978
|
+
signer,
|
|
3979
|
+
sourceToken,
|
|
3980
|
+
})
|
|
3981
|
+
} catch {
|
|
3982
|
+
return undefined
|
|
3983
|
+
}
|
|
3984
|
+
})()
|
|
3985
|
+
items.push(
|
|
3986
|
+
item ?? {
|
|
3987
|
+
...itemBase(source.logIndex),
|
|
3988
|
+
data: {
|
|
3989
|
+
destinationToken: token(destinationArgs.amount, destTokenAddress),
|
|
3990
|
+
signer,
|
|
3991
|
+
sourceToken,
|
|
3992
|
+
},
|
|
3993
|
+
type: 'swap',
|
|
3994
|
+
},
|
|
3995
|
+
)
|
|
3996
|
+
} catch {
|
|
3997
|
+
// A malformed leg costs only this pair; release its claims so the
|
|
3998
|
+
// legs fall through to the later recognizers.
|
|
3999
|
+
for (const index of journal ?? []) claimed.delete(index)
|
|
4000
|
+
} finally {
|
|
4001
|
+
journal = undefined
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
3552
4004
|
}
|
|
3553
4005
|
|
|
3554
|
-
//
|
|
3555
|
-
//
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
4006
|
+
// Mint: explicit `Mint` events (not the fee manager) and remaining transfers
|
|
4007
|
+
// from the zero address (the dedup above already folded event/transfer pairs).
|
|
4008
|
+
const mintItem = (event: Decoded, args: { amount: bigint; to: Address.Address }): RawItem => ({
|
|
4009
|
+
...itemBase(event.logIndex),
|
|
4010
|
+
data: {
|
|
4011
|
+
recipient: lc(args.to),
|
|
4012
|
+
sourceToken: token(args.amount, event.address as Address.Address),
|
|
4013
|
+
},
|
|
4014
|
+
type: 'mint',
|
|
4015
|
+
})
|
|
4016
|
+
recognize(
|
|
4017
|
+
(event) =>
|
|
4018
|
+
event.eventName === 'Mint' &&
|
|
4019
|
+
'amount' in event.args &&
|
|
4020
|
+
'to' in event.args &&
|
|
4021
|
+
!Address.isEqual(event.address, Addresses.feeManager),
|
|
4022
|
+
(event) => mintItem(event, event.args as { amount: bigint; to: Address.Address }),
|
|
4023
|
+
)
|
|
4024
|
+
recognize(
|
|
4025
|
+
(event) =>
|
|
4026
|
+
isTransfer(event) &&
|
|
4027
|
+
Address.isEqual(transferArgs(event).from, zeroAddress as Address.Address),
|
|
4028
|
+
(event) => mintItem(event, transferArgs(event)),
|
|
4029
|
+
)
|
|
3572
4030
|
|
|
3573
|
-
// Burn:
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
4031
|
+
// Burn blocked: a compliance burn of receive-policy-blocked funds. It also
|
|
4032
|
+
// emits a zero-address `Transfer`; claim that paired leg so it cannot
|
|
4033
|
+
// classify as a plain `burn`.
|
|
4034
|
+
recognize(named('BurnBlocked'), (event) => {
|
|
4035
|
+
const args = event.args as { amount: bigint; from: Address.Address }
|
|
4036
|
+
takeTransfer(
|
|
4037
|
+
(leg) =>
|
|
4038
|
+
Address.isEqual(leg.address, event.address) &&
|
|
4039
|
+
Address.isEqual(transferArgs(leg).to, zeroAddress as Address.Address) &&
|
|
4040
|
+
transferArgs(leg).amount === args.amount,
|
|
4041
|
+
)
|
|
4042
|
+
return {
|
|
4043
|
+
...itemBase(event.logIndex),
|
|
4044
|
+
data: {
|
|
4045
|
+
sender: lc(args.from),
|
|
4046
|
+
signer,
|
|
4047
|
+
sourceToken: token(args.amount, event.address as Address.Address),
|
|
3586
4048
|
},
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
4049
|
+
type: 'burn-blocked',
|
|
4050
|
+
}
|
|
4051
|
+
})
|
|
4052
|
+
|
|
4053
|
+
// Burn: explicit `Burn` events and remaining transfers to the zero address.
|
|
4054
|
+
const burnItem = (event: Decoded, args: { amount: bigint; from: Address.Address }): RawItem => ({
|
|
4055
|
+
...itemBase(event.logIndex),
|
|
4056
|
+
data: {
|
|
4057
|
+
sender: lc(args.from),
|
|
4058
|
+
signer,
|
|
4059
|
+
sourceToken: token(args.amount, event.address as Address.Address),
|
|
4060
|
+
},
|
|
4061
|
+
type: 'burn',
|
|
4062
|
+
})
|
|
4063
|
+
recognize(
|
|
4064
|
+
(event) => event.eventName === 'Burn' && 'amount' in event.args && 'from' in event.args,
|
|
4065
|
+
(event) => burnItem(event, event.args as { amount: bigint; from: Address.Address }),
|
|
4066
|
+
)
|
|
4067
|
+
recognize(
|
|
4068
|
+
(event) =>
|
|
4069
|
+
isTransfer(event) && Address.isEqual(transferArgs(event).to, zeroAddress as Address.Address),
|
|
4070
|
+
(event) => burnItem(event, transferArgs(event)),
|
|
3591
4071
|
)
|
|
3592
|
-
if (zeroTransferOut) {
|
|
3593
|
-
const args = zeroTransferOut.args as { amount: bigint; from: Address.Address }
|
|
3594
|
-
return [
|
|
3595
|
-
{
|
|
3596
|
-
...itemBase(logIndexOfEvent(zeroTransferOut)),
|
|
3597
|
-
data: {
|
|
3598
|
-
sender: (args.from as string).toLowerCase() as Address.Address,
|
|
3599
|
-
signer,
|
|
3600
|
-
sourceToken: token(args.amount, zeroTransferOut.address as Address.Address),
|
|
3601
|
-
},
|
|
3602
|
-
type: 'burn',
|
|
3603
|
-
},
|
|
3604
|
-
]
|
|
3605
|
-
}
|
|
3606
4072
|
|
|
3607
|
-
// Approval (excluding zone-outbox spenders)
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
4073
|
+
// Approval (excluding zone-outbox spenders): only viewer-party approvals
|
|
4074
|
+
// surface here; the rest stay for the nothing-else-classified fallback.
|
|
4075
|
+
const isErc20Approval = (event: Decoded) => {
|
|
4076
|
+
if (event.eventName !== 'Approval') return false
|
|
4077
|
+
const args = event.args as { spender?: unknown }
|
|
4078
|
+
if (typeof args.spender !== 'string' || !Address.validate(args.spender)) return false
|
|
4079
|
+
// ERC-721 uses the same selector but indexes its token ID as topic3. In
|
|
4080
|
+
// non-strict mode viem otherwise decodes the missing ERC-20 amount as zero.
|
|
4081
|
+
return (
|
|
3613
4082
|
rows.some(
|
|
3614
4083
|
(row) =>
|
|
3615
|
-
row.logIdx ===
|
|
4084
|
+
row.logIdx === event.logIndex &&
|
|
3616
4085
|
row.topic3 === null &&
|
|
3617
4086
|
/^0x[0-9a-fA-F]{64}$/.test(row.data ?? ''),
|
|
3618
|
-
) &&
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
const args =
|
|
3623
|
-
return
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
spender: args.spender.toLowerCase() as Address.Address,
|
|
3630
|
-
},
|
|
3631
|
-
type: 'approval',
|
|
4087
|
+
) && !Address.isEqual(args.spender as Address.Address, Addresses.zoneOutbox)
|
|
4088
|
+
)
|
|
4089
|
+
}
|
|
4090
|
+
const approvalItem = (event: Decoded): RawItem => {
|
|
4091
|
+
const args = event.args as { amount: bigint; spender: Address.Address }
|
|
4092
|
+
return {
|
|
4093
|
+
...itemBase(event.logIndex),
|
|
4094
|
+
data: {
|
|
4095
|
+
signer,
|
|
4096
|
+
sourceToken: token(args.amount, event.address as Address.Address),
|
|
4097
|
+
spender: lc(args.spender),
|
|
3632
4098
|
},
|
|
3633
|
-
|
|
4099
|
+
type: 'approval',
|
|
4100
|
+
}
|
|
3634
4101
|
}
|
|
4102
|
+
recognize(
|
|
4103
|
+
(event) =>
|
|
4104
|
+
isErc20Approval(event) &&
|
|
4105
|
+
'owner' in event.args &&
|
|
4106
|
+
(Address.isEqual((event.args as { owner: Address.Address }).owner, viewer) ||
|
|
4107
|
+
Address.isEqual((event.args as { spender: Address.Address }).spender, viewer)),
|
|
4108
|
+
approvalItem,
|
|
4109
|
+
)
|
|
3635
4110
|
|
|
3636
|
-
//
|
|
3637
|
-
//
|
|
3638
|
-
//
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
// by log adjacency.
|
|
3642
|
-
|
|
3643
|
-
// Payment channel opened: `ChannelOpened` carries the deposit token/amount
|
|
3644
|
-
// directly, so no paired-transfer lookup is needed.
|
|
3645
|
-
const channelOpened = finalEvents.find((e) => e.eventName === 'ChannelOpened')
|
|
3646
|
-
if (channelOpened) {
|
|
3647
|
-
const args = channelOpened.args as {
|
|
4111
|
+
// Payment channels. The reserve doesn't put the token on `TopUp`/`Settled`/
|
|
4112
|
+
// `ChannelClosed`, so each anchor claims its own reserve leg to recover it;
|
|
4113
|
+
// batched channel operations pair with their own legs one at a time.
|
|
4114
|
+
recognize(named('ChannelOpened'), (event) => {
|
|
4115
|
+
const args = event.args as {
|
|
3648
4116
|
channelId: Hex.Hex
|
|
3649
4117
|
deposit: bigint
|
|
3650
4118
|
payee: Address.Address
|
|
3651
4119
|
payer: Address.Address
|
|
3652
4120
|
token: Address.Address
|
|
3653
4121
|
}
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
4122
|
+
takeTransfer(
|
|
4123
|
+
(leg) =>
|
|
4124
|
+
Address.isEqual(leg.address, args.token) &&
|
|
4125
|
+
Address.isEqual(transferArgs(leg).to, tip20ChannelReserve) &&
|
|
4126
|
+
transferArgs(leg).amount === args.deposit,
|
|
4127
|
+
)
|
|
4128
|
+
return {
|
|
4129
|
+
...itemBase(event.logIndex),
|
|
4130
|
+
data: {
|
|
4131
|
+
channelId: hx(args.channelId),
|
|
4132
|
+
payee: lc(args.payee),
|
|
4133
|
+
payer: lc(args.payer),
|
|
4134
|
+
signer,
|
|
4135
|
+
sourceToken: token(args.deposit, args.token),
|
|
3665
4136
|
},
|
|
3666
|
-
|
|
3667
|
-
|
|
4137
|
+
type: 'channel-opened',
|
|
4138
|
+
}
|
|
4139
|
+
})
|
|
3668
4140
|
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
4141
|
+
recognize(named('TopUp'), (event) => {
|
|
4142
|
+
const args = event.args as { additionalDeposit: bigint; channelId: Hex.Hex }
|
|
4143
|
+
const inflow =
|
|
4144
|
+
takeTransfer(
|
|
4145
|
+
(leg) =>
|
|
4146
|
+
Address.isEqual(transferArgs(leg).to, tip20ChannelReserve) &&
|
|
4147
|
+
transferArgs(leg).amount === args.additionalDeposit,
|
|
4148
|
+
) ?? takeTransfer((leg) => Address.isEqual(transferArgs(leg).to, tip20ChannelReserve))
|
|
4149
|
+
// A top-up cancels any pending close request on the channel; fold the
|
|
4150
|
+
// cancellation event it bundles.
|
|
4151
|
+
unclaimed(
|
|
3676
4152
|
(e) =>
|
|
3677
|
-
|
|
3678
|
-
|
|
4153
|
+
e.eventName === 'CloseRequestCancelled' &&
|
|
4154
|
+
'channelId' in e.args &&
|
|
4155
|
+
(e.args as { channelId: Hex.Hex }).channelId === args.channelId,
|
|
3679
4156
|
)
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
if (topUp) {
|
|
3685
|
-
const args = topUp.args as { additionalDeposit: bigint; channelId: Hex.Hex }
|
|
3686
|
-
const tokenAddress = reserveInflow
|
|
3687
|
-
? (reserveInflow.address as Address.Address)
|
|
4157
|
+
.slice(0, 1)
|
|
4158
|
+
.forEach(claim)
|
|
4159
|
+
const tokenAddress = inflow
|
|
4160
|
+
? (inflow.address as Address.Address)
|
|
3688
4161
|
: (zeroAddress as Address.Address)
|
|
3689
|
-
return
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
sourceToken: token(args.additionalDeposit, tokenAddress),
|
|
3696
|
-
},
|
|
3697
|
-
type: 'channel-funded',
|
|
4162
|
+
return {
|
|
4163
|
+
...itemBase(event.logIndex),
|
|
4164
|
+
data: {
|
|
4165
|
+
channelId: hx(args.channelId),
|
|
4166
|
+
signer,
|
|
4167
|
+
sourceToken: token(args.additionalDeposit, tokenAddress),
|
|
3698
4168
|
},
|
|
3699
|
-
|
|
3700
|
-
|
|
4169
|
+
type: 'channel-funded',
|
|
4170
|
+
}
|
|
4171
|
+
})
|
|
3701
4172
|
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
const settled = finalEvents.find((e) => e.eventName === 'Settled')
|
|
3705
|
-
if (settled) {
|
|
3706
|
-
const args = settled.args as {
|
|
4173
|
+
recognize(named('Settled'), (event) => {
|
|
4174
|
+
const args = event.args as {
|
|
3707
4175
|
channelId: Hex.Hex
|
|
3708
4176
|
deltaPaid: bigint
|
|
3709
4177
|
payee: Address.Address
|
|
3710
4178
|
payer: Address.Address
|
|
3711
4179
|
}
|
|
3712
|
-
const
|
|
4180
|
+
const payout = (leg: Decoded) =>
|
|
4181
|
+
Address.isEqual(transferArgs(leg).from, tip20ChannelReserve) &&
|
|
4182
|
+
Address.isEqual(transferArgs(leg).to, args.payee)
|
|
4183
|
+
// Prefer the amount-matched payout so batched channel operations sharing
|
|
4184
|
+
// a payee keep their own legs.
|
|
4185
|
+
const payeeLeg =
|
|
4186
|
+
takeTransfer((leg) => payout(leg) && transferArgs(leg).amount === args.deltaPaid) ??
|
|
4187
|
+
takeTransfer(payout)
|
|
3713
4188
|
const tokenAddress = payeeLeg
|
|
3714
4189
|
? (payeeLeg.address as Address.Address)
|
|
3715
4190
|
: (zeroAddress as Address.Address)
|
|
3716
|
-
return
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
sourceToken: token(args.deltaPaid, tokenAddress),
|
|
3725
|
-
},
|
|
3726
|
-
type: 'channel-settled',
|
|
4191
|
+
return {
|
|
4192
|
+
...itemBase(event.logIndex),
|
|
4193
|
+
data: {
|
|
4194
|
+
channelId: hx(args.channelId),
|
|
4195
|
+
payee: lc(args.payee),
|
|
4196
|
+
payer: lc(args.payer),
|
|
4197
|
+
signer,
|
|
4198
|
+
sourceToken: token(args.deltaPaid, tokenAddress),
|
|
3727
4199
|
},
|
|
3728
|
-
|
|
3729
|
-
|
|
4200
|
+
type: 'channel-settled',
|
|
4201
|
+
}
|
|
4202
|
+
})
|
|
3730
4203
|
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
const channelClosed = finalEvents.find((e) => e.eventName === 'ChannelClosed')
|
|
3734
|
-
if (channelClosed) {
|
|
3735
|
-
const args = channelClosed.args as {
|
|
4204
|
+
recognize(named('ChannelClosed'), (event) => {
|
|
4205
|
+
const args = event.args as {
|
|
3736
4206
|
channelId: Hex.Hex
|
|
3737
4207
|
payee: Address.Address
|
|
3738
4208
|
payer: Address.Address
|
|
3739
4209
|
refundedToPayer: bigint
|
|
3740
4210
|
settledToPayee: bigint
|
|
3741
4211
|
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
4212
|
+
// A zero settlement or refund has no leg; claiming one would steal it
|
|
4213
|
+
// from a batched sibling operation.
|
|
4214
|
+
const reserveLegTo = (to: Address.Address, amount: bigint) => {
|
|
4215
|
+
if (amount === 0n) return undefined
|
|
4216
|
+
const payout = (leg: Decoded) =>
|
|
4217
|
+
Address.isEqual(transferArgs(leg).from, tip20ChannelReserve) &&
|
|
4218
|
+
Address.isEqual(transferArgs(leg).to, to)
|
|
4219
|
+
return (
|
|
4220
|
+
takeTransfer((leg) => payout(leg) && transferArgs(leg).amount === amount) ??
|
|
4221
|
+
takeTransfer(payout)
|
|
4222
|
+
)
|
|
4223
|
+
}
|
|
4224
|
+
const payeeLeg = reserveLegTo(args.payee, args.settledToPayee)
|
|
4225
|
+
const payerLeg = reserveLegTo(args.payer, args.refundedToPayer)
|
|
4226
|
+
return {
|
|
4227
|
+
...itemBase(event.logIndex),
|
|
4228
|
+
data: {
|
|
4229
|
+
channelId: hx(args.channelId),
|
|
4230
|
+
payee: lc(args.payee),
|
|
4231
|
+
payer: lc(args.payer),
|
|
4232
|
+
...(payerLeg && args.refundedToPayer > 0n
|
|
4233
|
+
? { refund: token(args.refundedToPayer, payerLeg.address as Address.Address) }
|
|
4234
|
+
: {}),
|
|
4235
|
+
signer,
|
|
4236
|
+
...(payeeLeg
|
|
4237
|
+
? { sourceToken: token(args.settledToPayee, payeeLeg.address as Address.Address) }
|
|
4238
|
+
: {}),
|
|
3760
4239
|
},
|
|
3761
|
-
|
|
3762
|
-
|
|
4240
|
+
type: 'channel-closed',
|
|
4241
|
+
}
|
|
4242
|
+
})
|
|
3763
4243
|
|
|
3764
|
-
// Payment channel close cancelled:
|
|
3765
|
-
//
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
if (closeCancelled) {
|
|
3769
|
-
const args = closeCancelled.args as {
|
|
4244
|
+
// Payment channel close cancelled: carries no transfer, so the event itself
|
|
4245
|
+
// is the signal. Cancellations bundled into a top-up were claimed above.
|
|
4246
|
+
recognize(named('CloseRequestCancelled'), (event) => {
|
|
4247
|
+
const args = event.args as {
|
|
3770
4248
|
channelId: Hex.Hex
|
|
3771
4249
|
payee: Address.Address
|
|
3772
4250
|
payer: Address.Address
|
|
3773
4251
|
}
|
|
3774
|
-
return
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
signer,
|
|
3782
|
-
},
|
|
3783
|
-
type: 'channel-close-cancelled',
|
|
4252
|
+
return {
|
|
4253
|
+
...itemBase(event.logIndex),
|
|
4254
|
+
data: {
|
|
4255
|
+
channelId: hx(args.channelId),
|
|
4256
|
+
payee: lc(args.payee),
|
|
4257
|
+
payer: lc(args.payer),
|
|
4258
|
+
signer,
|
|
3784
4259
|
},
|
|
3785
|
-
|
|
3786
|
-
}
|
|
3787
|
-
|
|
3788
|
-
// DEX order placed: `OrderPlaced` carries the escrow token/amount and side.
|
|
3789
|
-
// (Auto-flip re-places are inside swaps, which already returned above.)
|
|
3790
|
-
const orderPlaced = finalEvents.find((e) => e.eventName === 'OrderPlaced')
|
|
3791
|
-
if (orderPlaced) {
|
|
3792
|
-
const args = orderPlaced.args as {
|
|
3793
|
-
amount: bigint
|
|
3794
|
-
isBid: boolean
|
|
3795
|
-
orderId: bigint
|
|
3796
|
-
tick: number
|
|
3797
|
-
token: Address.Address
|
|
4260
|
+
type: 'channel-close-cancelled',
|
|
3798
4261
|
}
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
4262
|
+
})
|
|
4263
|
+
|
|
4264
|
+
// Payment-channel close requested: matched on the raw selector (the event
|
|
4265
|
+
// is not in the merged ABI). Runs after the channel recognizers claimed
|
|
4266
|
+
// their own reserve legs, then takes the leftover refund back to the viewer.
|
|
4267
|
+
for (const row of rows.filter((r) => r.selector === closeRequestedSelector)) {
|
|
4268
|
+
const refundLeg = takeTransfer((event) => {
|
|
4269
|
+
const args = transferArgs(event)
|
|
4270
|
+
if (!Address.isEqual(args.to, viewer)) return false
|
|
4271
|
+
return (
|
|
4272
|
+
Address.isEqual(args.from, tip20ChannelReserve) ||
|
|
4273
|
+
Address.isEqual(args.from, row.logAddress)
|
|
4274
|
+
)
|
|
4275
|
+
})
|
|
4276
|
+
const refund = refundLeg
|
|
4277
|
+
? token(transferArgs(refundLeg).amount, refundLeg.address as Address.Address)
|
|
4278
|
+
: undefined
|
|
4279
|
+
items.push({
|
|
4280
|
+
...itemBase(row.logIdx),
|
|
4281
|
+
data: { ...(refund ? { refund } : {}), signer },
|
|
4282
|
+
type: 'session-closed',
|
|
4283
|
+
})
|
|
3812
4284
|
}
|
|
3813
4285
|
|
|
4286
|
+
// Auto-flip order placements not folded into a swap above.
|
|
4287
|
+
recognize(named('OrderPlaced'), orderPlacedItem)
|
|
4288
|
+
|
|
3814
4289
|
// DEX order cancelled: refund is credited to the internal DEX balance, so
|
|
3815
|
-
// there is no co-located transfer
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
{
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
},
|
|
3825
|
-
]
|
|
3826
|
-
}
|
|
4290
|
+
// there is no co-located transfer; the event itself is the signal.
|
|
4291
|
+
recognize(named('OrderCancelled'), (event) => {
|
|
4292
|
+
const args = event.args as { orderId: bigint }
|
|
4293
|
+
return {
|
|
4294
|
+
...itemBase(event.logIndex),
|
|
4295
|
+
data: { orderId: args.orderId.toString(), signer },
|
|
4296
|
+
type: 'order-cancelled',
|
|
4297
|
+
}
|
|
4298
|
+
})
|
|
3827
4299
|
|
|
3828
|
-
// Fee-AMM rebalance swap: `validatorToken` in, `userToken` out
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
const args =
|
|
4300
|
+
// Fee-AMM rebalance swap: `validatorToken` in, `userToken` out; claim both
|
|
4301
|
+
// fee-manager legs so neither resurfaces as a plain transfer.
|
|
4302
|
+
recognize(named('RebalanceSwap'), (event) => {
|
|
4303
|
+
const args = event.args as {
|
|
3832
4304
|
amountIn: bigint
|
|
3833
4305
|
amountOut: bigint
|
|
3834
4306
|
swapper: Address.Address
|
|
3835
4307
|
userToken: Address.Address
|
|
3836
4308
|
validatorToken: Address.Address
|
|
3837
4309
|
}
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
4310
|
+
takeTransfer(
|
|
4311
|
+
(leg) =>
|
|
4312
|
+
Address.isEqual(leg.address, args.validatorToken) &&
|
|
4313
|
+
Address.isEqual(transferArgs(leg).from, args.swapper) &&
|
|
4314
|
+
Address.isEqual(transferArgs(leg).to, Addresses.feeManager) &&
|
|
4315
|
+
transferArgs(leg).amount === args.amountIn,
|
|
4316
|
+
)
|
|
4317
|
+
takeTransfer(
|
|
4318
|
+
(leg) =>
|
|
4319
|
+
Address.isEqual(leg.address, args.userToken) &&
|
|
4320
|
+
Address.isEqual(transferArgs(leg).from, Addresses.feeManager) &&
|
|
4321
|
+
Address.isEqual(transferArgs(leg).to, args.swapper) &&
|
|
4322
|
+
transferArgs(leg).amount === args.amountOut,
|
|
4323
|
+
)
|
|
4324
|
+
return {
|
|
4325
|
+
...itemBase(event.logIndex),
|
|
4326
|
+
data: {
|
|
4327
|
+
destinationToken: token(args.amountOut, args.userToken),
|
|
4328
|
+
signer,
|
|
4329
|
+
sourceToken: token(args.amountIn, args.validatorToken),
|
|
4330
|
+
swapper: lc(args.swapper),
|
|
3848
4331
|
},
|
|
3849
|
-
|
|
3850
|
-
|
|
4332
|
+
type: 'fee-rebalance-swap',
|
|
4333
|
+
}
|
|
4334
|
+
})
|
|
3851
4335
|
|
|
3852
|
-
// Validator fees distributed.
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
const args = feesDistributed.args as {
|
|
4336
|
+
// Validator fees distributed; claim the fee-manager payout leg.
|
|
4337
|
+
recognize(named('FeesDistributed'), (event) => {
|
|
4338
|
+
const args = event.args as {
|
|
3856
4339
|
amount: bigint
|
|
3857
4340
|
token: Address.Address
|
|
3858
4341
|
validator: Address.Address
|
|
3859
4342
|
}
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
4343
|
+
takeTransfer(
|
|
4344
|
+
(leg) =>
|
|
4345
|
+
Address.isEqual(leg.address, args.token) &&
|
|
4346
|
+
Address.isEqual(transferArgs(leg).from, Addresses.feeManager) &&
|
|
4347
|
+
Address.isEqual(transferArgs(leg).to, args.validator) &&
|
|
4348
|
+
transferArgs(leg).amount === args.amount,
|
|
4349
|
+
)
|
|
4350
|
+
return {
|
|
4351
|
+
...itemBase(event.logIndex),
|
|
4352
|
+
data: {
|
|
4353
|
+
signer,
|
|
4354
|
+
sourceToken: token(args.amount, args.token),
|
|
4355
|
+
validator: lc(args.validator),
|
|
3869
4356
|
},
|
|
3870
|
-
|
|
3871
|
-
|
|
4357
|
+
type: 'fees-distributed',
|
|
4358
|
+
}
|
|
4359
|
+
})
|
|
3872
4360
|
|
|
3873
|
-
// Reward pool funded: the funded token is the emitting
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
const args =
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
4361
|
+
// Reward pool funded: the funded token is the emitting contract itself;
|
|
4362
|
+
// claim the funding transfer.
|
|
4363
|
+
recognize(named('RewardDistributed'), (event) => {
|
|
4364
|
+
const args = event.args as { amount: bigint; funder: Address.Address }
|
|
4365
|
+
takeTransfer(
|
|
4366
|
+
(leg) =>
|
|
4367
|
+
Address.isEqual(leg.address, event.address as Address.Address) &&
|
|
4368
|
+
Address.isEqual(transferArgs(leg).from, args.funder) &&
|
|
4369
|
+
Address.isEqual(transferArgs(leg).to, event.address as Address.Address) &&
|
|
4370
|
+
transferArgs(leg).amount === args.amount,
|
|
4371
|
+
)
|
|
4372
|
+
return {
|
|
4373
|
+
...itemBase(event.logIndex),
|
|
4374
|
+
data: {
|
|
4375
|
+
funder: lc(args.funder),
|
|
4376
|
+
signer,
|
|
4377
|
+
sourceToken: token(args.amount, event.address as Address.Address),
|
|
3886
4378
|
},
|
|
3887
|
-
|
|
3888
|
-
|
|
4379
|
+
type: 'reward-distributed',
|
|
4380
|
+
}
|
|
4381
|
+
})
|
|
3889
4382
|
|
|
3890
4383
|
// Reward recipient redirected.
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
{
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
recipient: args.recipient.toLowerCase() as Address.Address,
|
|
3900
|
-
signer,
|
|
3901
|
-
},
|
|
3902
|
-
type: 'reward-recipient-set',
|
|
4384
|
+
recognize(named('RewardRecipientSet'), (event) => {
|
|
4385
|
+
const args = event.args as { holder: Address.Address; recipient: Address.Address }
|
|
4386
|
+
return {
|
|
4387
|
+
...itemBase(event.logIndex),
|
|
4388
|
+
data: {
|
|
4389
|
+
holder: lc(args.holder),
|
|
4390
|
+
recipient: lc(args.recipient),
|
|
4391
|
+
signer,
|
|
3903
4392
|
},
|
|
3904
|
-
|
|
3905
|
-
|
|
4393
|
+
type: 'reward-recipient-set',
|
|
4394
|
+
}
|
|
4395
|
+
})
|
|
3906
4396
|
|
|
3907
4397
|
// Access-key spending limit updated: `newLimit` is carried as the token
|
|
3908
4398
|
// amount so it enriches with the token's metadata.
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
const args = spendingLimitUpdated.args as {
|
|
4399
|
+
recognize(named('SpendingLimitUpdated'), (event) => {
|
|
4400
|
+
const args = event.args as {
|
|
3912
4401
|
account: Address.Address
|
|
3913
4402
|
newLimit: bigint
|
|
3914
4403
|
publicKey: Address.Address
|
|
3915
4404
|
token: Address.Address
|
|
3916
4405
|
}
|
|
3917
|
-
return
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
sourceToken: token(args.newLimit, args.token),
|
|
3925
|
-
},
|
|
3926
|
-
type: 'spending-limit-updated',
|
|
4406
|
+
return {
|
|
4407
|
+
...itemBase(event.logIndex),
|
|
4408
|
+
data: {
|
|
4409
|
+
account: lc(args.account),
|
|
4410
|
+
publicKey: lc(args.publicKey),
|
|
4411
|
+
signer,
|
|
4412
|
+
sourceToken: token(args.newLimit, args.token),
|
|
3927
4413
|
},
|
|
3928
|
-
|
|
3929
|
-
|
|
4414
|
+
type: 'spending-limit-updated',
|
|
4415
|
+
}
|
|
4416
|
+
})
|
|
3930
4417
|
|
|
3931
|
-
// Access-key lifecycle events
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
if (
|
|
4418
|
+
// Access-key lifecycle events.
|
|
4419
|
+
recognize(
|
|
4420
|
+
(event) =>
|
|
3935
4421
|
event.eventName === 'KeyAuthorized' &&
|
|
3936
|
-
Address.isEqual(event.address, Addresses.accountKeychain)
|
|
3937
|
-
) {
|
|
4422
|
+
Address.isEqual(event.address, Addresses.accountKeychain),
|
|
4423
|
+
(event) => {
|
|
3938
4424
|
const args = event.args as {
|
|
3939
4425
|
account: Address.Address
|
|
3940
4426
|
expiry: bigint
|
|
3941
4427
|
publicKey: Address.Address
|
|
3942
4428
|
signatureType: number
|
|
3943
4429
|
}
|
|
3944
|
-
|
|
3945
|
-
...itemBase(
|
|
4430
|
+
return {
|
|
4431
|
+
...itemBase(event.logIndex),
|
|
3946
4432
|
data: {
|
|
3947
|
-
account: args.account
|
|
4433
|
+
account: lc(args.account),
|
|
3948
4434
|
expiry: Number(args.expiry),
|
|
3949
|
-
publicKey: args.publicKey
|
|
4435
|
+
publicKey: lc(args.publicKey),
|
|
3950
4436
|
signatureType: Number(args.signatureType),
|
|
3951
4437
|
},
|
|
3952
4438
|
type: 'access-key-created',
|
|
3953
|
-
}
|
|
3954
|
-
}
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
)
|
|
4439
|
+
}
|
|
4440
|
+
},
|
|
4441
|
+
)
|
|
4442
|
+
recognize(
|
|
4443
|
+
(event) =>
|
|
4444
|
+
event.eventName === 'KeyRevoked' && Address.isEqual(event.address, Addresses.accountKeychain),
|
|
4445
|
+
(event) => {
|
|
3958
4446
|
const args = event.args as { account: Address.Address; publicKey: Address.Address }
|
|
3959
|
-
|
|
3960
|
-
...itemBase(
|
|
4447
|
+
return {
|
|
4448
|
+
...itemBase(event.logIndex),
|
|
3961
4449
|
data: {
|
|
3962
|
-
account: args.account
|
|
3963
|
-
publicKey: args.publicKey
|
|
4450
|
+
account: lc(args.account),
|
|
4451
|
+
publicKey: lc(args.publicKey),
|
|
3964
4452
|
},
|
|
3965
4453
|
type: 'access-key-revoked',
|
|
3966
|
-
}
|
|
3967
|
-
}
|
|
3968
|
-
}
|
|
3969
|
-
|
|
3970
|
-
// Token creation.
|
|
3971
|
-
const tokenCreated = finalEvents.find(
|
|
3972
|
-
(e) => e.eventName === 'TokenCreated' && Address.isEqual(e.address, Addresses.tip20Factory),
|
|
4454
|
+
}
|
|
4455
|
+
},
|
|
3973
4456
|
)
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
{
|
|
3983
|
-
|
|
4457
|
+
|
|
4458
|
+
// Token creation. Factory initialization (role grants, config) precedes the
|
|
4459
|
+
// creation event; claim those, leaving later admin actions on the token as
|
|
4460
|
+
// independent activities.
|
|
4461
|
+
recognize(
|
|
4462
|
+
(event) =>
|
|
4463
|
+
event.eventName === 'TokenCreated' && Address.isEqual(event.address, Addresses.tip20Factory),
|
|
4464
|
+
(event) => {
|
|
4465
|
+
const args = event.args as {
|
|
4466
|
+
currency: string
|
|
4467
|
+
name: string
|
|
4468
|
+
symbol: string
|
|
4469
|
+
token: Address.Address
|
|
4470
|
+
}
|
|
4471
|
+
unclaimed(
|
|
4472
|
+
(e) => e.logIndex < event.logIndex && Address.isEqual(e.address, args.token),
|
|
4473
|
+
).forEach(claim)
|
|
4474
|
+
return {
|
|
4475
|
+
...itemBase(event.logIndex),
|
|
3984
4476
|
data: {
|
|
3985
|
-
address: args.token
|
|
4477
|
+
address: lc(args.token),
|
|
3986
4478
|
currency: args.currency,
|
|
3987
4479
|
name: args.name,
|
|
3988
4480
|
symbol: args.symbol,
|
|
3989
4481
|
},
|
|
3990
4482
|
type: 'token-created',
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
}
|
|
4483
|
+
}
|
|
4484
|
+
},
|
|
4485
|
+
)
|
|
3995
4486
|
|
|
3996
4487
|
// Governance / admin / DEX-bookkeeping events. Each maps a single decoded
|
|
3997
|
-
// event to a typed item
|
|
3998
|
-
//
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
const hx = (value: Hex.Hex) => value.toLowerCase() as Hex.Hex
|
|
4002
|
-
const findEvent = (name: string) => finalEvents.find((e) => e.eventName === name)
|
|
4003
|
-
|
|
4004
|
-
// DEX bookkeeping (taker swaps and `OrderPlaced` already returned above).
|
|
4005
|
-
const orderFilled = findEvent('OrderFilled')
|
|
4006
|
-
if (orderFilled) {
|
|
4007
|
-
const a = orderFilled.args as {
|
|
4488
|
+
// event to a typed item; every remaining occurrence emits. These carry no
|
|
4489
|
+
// token movement, so the event itself is the whole signal.
|
|
4490
|
+
recognize(named('OrderFilled'), (event) => {
|
|
4491
|
+
const a = event.args as {
|
|
4008
4492
|
amountFilled: bigint
|
|
4009
4493
|
maker: Address.Address
|
|
4010
4494
|
orderId: bigint
|
|
4011
4495
|
partialFill: boolean
|
|
4012
4496
|
taker: Address.Address
|
|
4013
4497
|
}
|
|
4014
|
-
return
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
taker: lc(a.taker),
|
|
4024
|
-
},
|
|
4025
|
-
type: 'order-filled',
|
|
4498
|
+
return {
|
|
4499
|
+
...itemBase(event.logIndex),
|
|
4500
|
+
data: {
|
|
4501
|
+
amountFilled: a.amountFilled.toString(),
|
|
4502
|
+
maker: lc(a.maker),
|
|
4503
|
+
orderId: a.orderId.toString(),
|
|
4504
|
+
partialFill: a.partialFill,
|
|
4505
|
+
signer,
|
|
4506
|
+
taker: lc(a.taker),
|
|
4026
4507
|
},
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
const a =
|
|
4508
|
+
type: 'order-filled',
|
|
4509
|
+
}
|
|
4510
|
+
})
|
|
4511
|
+
recognize(named('OrderFlipped'), (event) => {
|
|
4512
|
+
const a = event.args as {
|
|
4032
4513
|
amount: bigint
|
|
4033
4514
|
flipTick: number
|
|
4034
4515
|
isBid: boolean
|
|
@@ -4037,342 +4518,299 @@ function classifyFromLogsCore(
|
|
|
4037
4518
|
tick: number
|
|
4038
4519
|
token: Address.Address
|
|
4039
4520
|
}
|
|
4040
|
-
return
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
tick: Number(a.tick),
|
|
4051
|
-
},
|
|
4052
|
-
type: 'order-flipped',
|
|
4053
|
-
},
|
|
4054
|
-
]
|
|
4055
|
-
}
|
|
4056
|
-
const pairCreated = findEvent('PairCreated')
|
|
4057
|
-
if (pairCreated) {
|
|
4058
|
-
const a = pairCreated.args as { base: Address.Address; key: Hex.Hex; quote: Address.Address }
|
|
4059
|
-
return [
|
|
4060
|
-
{
|
|
4061
|
-
...itemBase(logIndexOfEvent(pairCreated)),
|
|
4062
|
-
data: { base: lc(a.base), key: hx(a.key), quote: lc(a.quote), signer },
|
|
4063
|
-
type: 'pair-created',
|
|
4521
|
+
return {
|
|
4522
|
+
...itemBase(event.logIndex),
|
|
4523
|
+
data: {
|
|
4524
|
+
flipTick: Number(a.flipTick),
|
|
4525
|
+
maker: lc(a.maker),
|
|
4526
|
+
orderId: a.orderId.toString(),
|
|
4527
|
+
side: a.isBid ? 'bid' : 'ask',
|
|
4528
|
+
signer,
|
|
4529
|
+
sourceToken: token(a.amount, a.token),
|
|
4530
|
+
tick: Number(a.tick),
|
|
4064
4531
|
},
|
|
4065
|
-
|
|
4066
|
-
|
|
4532
|
+
type: 'order-flipped',
|
|
4533
|
+
}
|
|
4534
|
+
})
|
|
4535
|
+
recognize(named('PairCreated'), (event) => {
|
|
4536
|
+
const a = event.args as { base: Address.Address; key: Hex.Hex; quote: Address.Address }
|
|
4537
|
+
return {
|
|
4538
|
+
...itemBase(event.logIndex),
|
|
4539
|
+
data: { base: lc(a.base), key: hx(a.key), quote: lc(a.quote), signer },
|
|
4540
|
+
type: 'pair-created',
|
|
4541
|
+
}
|
|
4542
|
+
})
|
|
4067
4543
|
|
|
4068
4544
|
// TIP-20 token configuration / admin.
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
{
|
|
4074
|
-
|
|
4075
|
-
data: { paused: a.isPaused, signer, updater: lc(a.updater) },
|
|
4076
|
-
type: 'token-pause-set',
|
|
4077
|
-
},
|
|
4078
|
-
]
|
|
4079
|
-
}
|
|
4080
|
-
const supplyCapUpdate = findEvent('SupplyCapUpdate')
|
|
4081
|
-
if (supplyCapUpdate) {
|
|
4082
|
-
const a = supplyCapUpdate.args as { newSupplyCap: bigint; updater: Address.Address }
|
|
4083
|
-
return [
|
|
4084
|
-
{
|
|
4085
|
-
...itemBase(logIndexOfEvent(supplyCapUpdate)),
|
|
4086
|
-
data: { signer, supplyCap: a.newSupplyCap.toString(), updater: lc(a.updater) },
|
|
4087
|
-
type: 'token-supply-cap-set',
|
|
4088
|
-
},
|
|
4089
|
-
]
|
|
4090
|
-
}
|
|
4091
|
-
const transferPolicyUpdate = findEvent('TransferPolicyUpdate')
|
|
4092
|
-
if (transferPolicyUpdate) {
|
|
4093
|
-
const a = transferPolicyUpdate.args as { newPolicyId: bigint; updater: Address.Address }
|
|
4094
|
-
return [
|
|
4095
|
-
{
|
|
4096
|
-
...itemBase(logIndexOfEvent(transferPolicyUpdate)),
|
|
4097
|
-
data: { policyId: a.newPolicyId.toString(), signer, updater: lc(a.updater) },
|
|
4098
|
-
type: 'token-transfer-policy-set',
|
|
4099
|
-
},
|
|
4100
|
-
]
|
|
4101
|
-
}
|
|
4102
|
-
const quoteTokenUpdate = findEvent('QuoteTokenUpdate')
|
|
4103
|
-
if (quoteTokenUpdate) {
|
|
4104
|
-
const a = quoteTokenUpdate.args as { newQuoteToken: Address.Address; updater: Address.Address }
|
|
4105
|
-
return [
|
|
4106
|
-
{
|
|
4107
|
-
...itemBase(logIndexOfEvent(quoteTokenUpdate)),
|
|
4108
|
-
data: { quoteToken: lc(a.newQuoteToken), signer, updater: lc(a.updater) },
|
|
4109
|
-
type: 'token-quote-token-set',
|
|
4110
|
-
},
|
|
4111
|
-
]
|
|
4112
|
-
}
|
|
4113
|
-
const nextQuoteTokenSet = findEvent('NextQuoteTokenSet')
|
|
4114
|
-
if (nextQuoteTokenSet) {
|
|
4115
|
-
const a = nextQuoteTokenSet.args as {
|
|
4116
|
-
nextQuoteToken: Address.Address
|
|
4117
|
-
updater: Address.Address
|
|
4545
|
+
recognize(named('PauseStateUpdate'), (event) => {
|
|
4546
|
+
const a = event.args as { isPaused: boolean; updater: Address.Address }
|
|
4547
|
+
return {
|
|
4548
|
+
...itemBase(event.logIndex),
|
|
4549
|
+
data: { paused: a.isPaused, signer, updater: lc(a.updater) },
|
|
4550
|
+
type: 'token-pause-set',
|
|
4118
4551
|
}
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
},
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
const a =
|
|
4130
|
-
return
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4552
|
+
})
|
|
4553
|
+
recognize(named('SupplyCapUpdate'), (event) => {
|
|
4554
|
+
const a = event.args as { newSupplyCap: bigint; updater: Address.Address }
|
|
4555
|
+
return {
|
|
4556
|
+
...itemBase(event.logIndex),
|
|
4557
|
+
data: { signer, supplyCap: a.newSupplyCap.toString(), updater: lc(a.updater) },
|
|
4558
|
+
type: 'token-supply-cap-set',
|
|
4559
|
+
}
|
|
4560
|
+
})
|
|
4561
|
+
recognize(named('TransferPolicyUpdate'), (event) => {
|
|
4562
|
+
const a = event.args as { newPolicyId: bigint; updater: Address.Address }
|
|
4563
|
+
return {
|
|
4564
|
+
...itemBase(event.logIndex),
|
|
4565
|
+
data: { policyId: a.newPolicyId.toString(), signer, updater: lc(a.updater) },
|
|
4566
|
+
type: 'token-transfer-policy-set',
|
|
4567
|
+
}
|
|
4568
|
+
})
|
|
4569
|
+
recognize(named('QuoteTokenUpdate'), (event) => {
|
|
4570
|
+
const a = event.args as { newQuoteToken: Address.Address; updater: Address.Address }
|
|
4571
|
+
return {
|
|
4572
|
+
...itemBase(event.logIndex),
|
|
4573
|
+
data: { quoteToken: lc(a.newQuoteToken), signer, updater: lc(a.updater) },
|
|
4574
|
+
type: 'token-quote-token-set',
|
|
4575
|
+
}
|
|
4576
|
+
})
|
|
4577
|
+
recognize(named('NextQuoteTokenSet'), (event) => {
|
|
4578
|
+
const a = event.args as { nextQuoteToken: Address.Address; updater: Address.Address }
|
|
4579
|
+
return {
|
|
4580
|
+
...itemBase(event.logIndex),
|
|
4581
|
+
data: { nextQuoteToken: lc(a.nextQuoteToken), signer, updater: lc(a.updater) },
|
|
4582
|
+
type: 'token-next-quote-token-set',
|
|
4583
|
+
}
|
|
4584
|
+
})
|
|
4585
|
+
recognize(named('LogoURIUpdated'), (event) => {
|
|
4586
|
+
const a = event.args as { newLogoURI: string; updater: Address.Address }
|
|
4587
|
+
return {
|
|
4588
|
+
...itemBase(event.logIndex),
|
|
4589
|
+
data: { logoUri: a.newLogoURI, signer, updater: lc(a.updater) },
|
|
4590
|
+
type: 'token-logo-set',
|
|
4591
|
+
}
|
|
4592
|
+
})
|
|
4593
|
+
recognize(named('RoleMembershipUpdated'), (event) => {
|
|
4594
|
+
const a = event.args as {
|
|
4141
4595
|
account: Address.Address
|
|
4142
4596
|
hasRole: boolean
|
|
4143
4597
|
role: Hex.Hex
|
|
4144
4598
|
sender: Address.Address
|
|
4145
4599
|
}
|
|
4146
|
-
return
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
signer,
|
|
4155
|
-
},
|
|
4156
|
-
type: 'role-membership-set',
|
|
4600
|
+
return {
|
|
4601
|
+
...itemBase(event.logIndex),
|
|
4602
|
+
data: {
|
|
4603
|
+
account: lc(a.account),
|
|
4604
|
+
granted: a.hasRole,
|
|
4605
|
+
role: hx(a.role),
|
|
4606
|
+
sender: lc(a.sender),
|
|
4607
|
+
signer,
|
|
4157
4608
|
},
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
const a =
|
|
4609
|
+
type: 'role-membership-set',
|
|
4610
|
+
}
|
|
4611
|
+
})
|
|
4612
|
+
recognize(named('RoleAdminUpdated'), (event) => {
|
|
4613
|
+
const a = event.args as {
|
|
4163
4614
|
newAdminRole: Hex.Hex
|
|
4164
4615
|
role: Hex.Hex
|
|
4165
4616
|
sender: Address.Address
|
|
4166
4617
|
}
|
|
4167
|
-
return
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
signer,
|
|
4175
|
-
},
|
|
4176
|
-
type: 'role-admin-set',
|
|
4618
|
+
return {
|
|
4619
|
+
...itemBase(event.logIndex),
|
|
4620
|
+
data: {
|
|
4621
|
+
newAdminRole: hx(a.newAdminRole),
|
|
4622
|
+
role: hx(a.role),
|
|
4623
|
+
sender: lc(a.sender),
|
|
4624
|
+
signer,
|
|
4177
4625
|
},
|
|
4178
|
-
|
|
4179
|
-
|
|
4626
|
+
type: 'role-admin-set',
|
|
4627
|
+
}
|
|
4628
|
+
})
|
|
4180
4629
|
|
|
4181
4630
|
// Fee-manager token configuration.
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
{
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
...itemBase(logIndexOfEvent(validatorTokenSet)),
|
|
4199
|
-
data: { signer, token: lc(a.token), validator: lc(a.validator) },
|
|
4200
|
-
type: 'fee-validator-token-set',
|
|
4201
|
-
},
|
|
4202
|
-
]
|
|
4203
|
-
}
|
|
4631
|
+
recognize(named('UserTokenSet'), (event) => {
|
|
4632
|
+
const a = event.args as { token: Address.Address; user: Address.Address }
|
|
4633
|
+
return {
|
|
4634
|
+
...itemBase(event.logIndex),
|
|
4635
|
+
data: { signer, token: lc(a.token), user: lc(a.user) },
|
|
4636
|
+
type: 'fee-user-token-set',
|
|
4637
|
+
}
|
|
4638
|
+
})
|
|
4639
|
+
recognize(named('ValidatorTokenSet'), (event) => {
|
|
4640
|
+
const a = event.args as { token: Address.Address; validator: Address.Address }
|
|
4641
|
+
return {
|
|
4642
|
+
...itemBase(event.logIndex),
|
|
4643
|
+
data: { signer, token: lc(a.token), validator: lc(a.validator) },
|
|
4644
|
+
type: 'fee-validator-token-set',
|
|
4645
|
+
}
|
|
4646
|
+
})
|
|
4204
4647
|
|
|
4205
|
-
// TIP-403 policy registry
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
const a =
|
|
4648
|
+
// TIP-403 policy registry. Creation events claim the member events they
|
|
4649
|
+
// bundle (which precede the anchor); later member updates stay independent.
|
|
4650
|
+
recognize(named('CompoundPolicyCreated'), (event) => {
|
|
4651
|
+
const a = event.args as {
|
|
4209
4652
|
creator: Address.Address
|
|
4210
4653
|
mintRecipientPolicyId: bigint
|
|
4211
4654
|
policyId: bigint
|
|
4212
4655
|
recipientPolicyId: bigint
|
|
4213
4656
|
senderPolicyId: bigint
|
|
4214
4657
|
}
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4658
|
+
const componentIds = new Set(
|
|
4659
|
+
[a.mintRecipientPolicyId, a.recipientPolicyId, a.senderPolicyId].map(String),
|
|
4660
|
+
)
|
|
4661
|
+
unclaimed(
|
|
4662
|
+
(e) =>
|
|
4663
|
+
e.logIndex < event.logIndex &&
|
|
4664
|
+
Address.isEqual(e.address, event.address) &&
|
|
4665
|
+
['BlacklistUpdated', 'PolicyAdminUpdated', 'PolicyCreated', 'WhitelistUpdated'].includes(
|
|
4666
|
+
e.eventName,
|
|
4667
|
+
) &&
|
|
4668
|
+
'policyId' in e.args &&
|
|
4669
|
+
componentIds.has(String((e.args as { policyId: bigint }).policyId)),
|
|
4670
|
+
).forEach(claim)
|
|
4671
|
+
return {
|
|
4672
|
+
...itemBase(event.logIndex),
|
|
4673
|
+
data: {
|
|
4674
|
+
creator: lc(a.creator),
|
|
4675
|
+
mintRecipientPolicyId: a.mintRecipientPolicyId.toString(),
|
|
4676
|
+
policyId: a.policyId.toString(),
|
|
4677
|
+
recipientPolicyId: a.recipientPolicyId.toString(),
|
|
4678
|
+
senderPolicyId: a.senderPolicyId.toString(),
|
|
4679
|
+
signer,
|
|
4227
4680
|
},
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
const a =
|
|
4681
|
+
type: 'compound-policy-created',
|
|
4682
|
+
}
|
|
4683
|
+
})
|
|
4684
|
+
recognize(named('PolicyCreated'), (event) => {
|
|
4685
|
+
const a = event.args as {
|
|
4233
4686
|
policyId: bigint
|
|
4234
4687
|
policyType: number
|
|
4235
4688
|
updater: Address.Address
|
|
4236
4689
|
}
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4690
|
+
unclaimed(
|
|
4691
|
+
(e) =>
|
|
4692
|
+
e.logIndex < event.logIndex &&
|
|
4693
|
+
Address.isEqual(e.address, event.address) &&
|
|
4694
|
+
['BlacklistUpdated', 'PolicyAdminUpdated', 'WhitelistUpdated'].includes(e.eventName) &&
|
|
4695
|
+
'policyId' in e.args &&
|
|
4696
|
+
(e.args as { policyId: bigint }).policyId === a.policyId,
|
|
4697
|
+
).forEach(claim)
|
|
4698
|
+
return {
|
|
4699
|
+
...itemBase(event.logIndex),
|
|
4700
|
+
data: {
|
|
4701
|
+
policyId: a.policyId.toString(),
|
|
4702
|
+
policyType: Number(a.policyType),
|
|
4703
|
+
signer,
|
|
4704
|
+
updater: lc(a.updater),
|
|
4247
4705
|
},
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
const a =
|
|
4706
|
+
type: 'policy-created',
|
|
4707
|
+
}
|
|
4708
|
+
})
|
|
4709
|
+
recognize(named('PolicyAdminUpdated'), (event) => {
|
|
4710
|
+
const a = event.args as {
|
|
4253
4711
|
admin: Address.Address
|
|
4254
4712
|
policyId: bigint
|
|
4255
4713
|
updater: Address.Address
|
|
4256
4714
|
}
|
|
4257
|
-
return
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
updater: lc(a.updater),
|
|
4265
|
-
},
|
|
4266
|
-
type: 'policy-admin-set',
|
|
4715
|
+
return {
|
|
4716
|
+
...itemBase(event.logIndex),
|
|
4717
|
+
data: {
|
|
4718
|
+
admin: lc(a.admin),
|
|
4719
|
+
policyId: a.policyId.toString(),
|
|
4720
|
+
signer,
|
|
4721
|
+
updater: lc(a.updater),
|
|
4267
4722
|
},
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
const a =
|
|
4723
|
+
type: 'policy-admin-set',
|
|
4724
|
+
}
|
|
4725
|
+
})
|
|
4726
|
+
recognize(named('WhitelistUpdated'), (event) => {
|
|
4727
|
+
const a = event.args as {
|
|
4273
4728
|
account: Address.Address
|
|
4274
4729
|
allowed: boolean
|
|
4275
4730
|
policyId: bigint
|
|
4276
4731
|
updater: Address.Address
|
|
4277
4732
|
}
|
|
4278
|
-
return
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
updater: lc(a.updater),
|
|
4287
|
-
},
|
|
4288
|
-
type: 'whitelist-updated',
|
|
4733
|
+
return {
|
|
4734
|
+
...itemBase(event.logIndex),
|
|
4735
|
+
data: {
|
|
4736
|
+
account: lc(a.account),
|
|
4737
|
+
allowed: a.allowed,
|
|
4738
|
+
policyId: a.policyId.toString(),
|
|
4739
|
+
signer,
|
|
4740
|
+
updater: lc(a.updater),
|
|
4289
4741
|
},
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
const a =
|
|
4742
|
+
type: 'whitelist-updated',
|
|
4743
|
+
}
|
|
4744
|
+
})
|
|
4745
|
+
recognize(named('BlacklistUpdated'), (event) => {
|
|
4746
|
+
const a = event.args as {
|
|
4295
4747
|
account: Address.Address
|
|
4296
4748
|
policyId: bigint
|
|
4297
4749
|
restricted: boolean
|
|
4298
4750
|
updater: Address.Address
|
|
4299
4751
|
}
|
|
4300
|
-
return
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
updater: lc(a.updater),
|
|
4309
|
-
},
|
|
4310
|
-
type: 'blacklist-updated',
|
|
4752
|
+
return {
|
|
4753
|
+
...itemBase(event.logIndex),
|
|
4754
|
+
data: {
|
|
4755
|
+
account: lc(a.account),
|
|
4756
|
+
policyId: a.policyId.toString(),
|
|
4757
|
+
restricted: a.restricted,
|
|
4758
|
+
signer,
|
|
4759
|
+
updater: lc(a.updater),
|
|
4311
4760
|
},
|
|
4312
|
-
|
|
4313
|
-
|
|
4761
|
+
type: 'blacklist-updated',
|
|
4762
|
+
}
|
|
4763
|
+
})
|
|
4314
4764
|
|
|
4315
4765
|
// Address / nonce registry.
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
{
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
}
|
|
4327
|
-
const nonceIncremented = findEvent('NonceIncremented')
|
|
4328
|
-
if (nonceIncremented) {
|
|
4329
|
-
const a = nonceIncremented.args as {
|
|
4766
|
+
recognize(named('MasterRegistered'), (event) => {
|
|
4767
|
+
const a = event.args as { masterAddress: Address.Address; masterId: Hex.Hex }
|
|
4768
|
+
return {
|
|
4769
|
+
...itemBase(event.logIndex),
|
|
4770
|
+
data: { masterAddress: lc(a.masterAddress), masterId: hx(a.masterId), signer },
|
|
4771
|
+
type: 'master-registered',
|
|
4772
|
+
}
|
|
4773
|
+
})
|
|
4774
|
+
recognize(named('NonceIncremented'), (event) => {
|
|
4775
|
+
const a = event.args as {
|
|
4330
4776
|
account: Address.Address
|
|
4331
4777
|
newNonce: bigint
|
|
4332
4778
|
nonceKey: bigint
|
|
4333
4779
|
}
|
|
4334
|
-
return
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
signer,
|
|
4342
|
-
},
|
|
4343
|
-
type: 'nonce-incremented',
|
|
4780
|
+
return {
|
|
4781
|
+
...itemBase(event.logIndex),
|
|
4782
|
+
data: {
|
|
4783
|
+
account: lc(a.account),
|
|
4784
|
+
nonce: a.newNonce.toString(),
|
|
4785
|
+
nonceKey: a.nonceKey.toString(),
|
|
4786
|
+
signer,
|
|
4344
4787
|
},
|
|
4345
|
-
|
|
4346
|
-
|
|
4788
|
+
type: 'nonce-incremented',
|
|
4789
|
+
}
|
|
4790
|
+
})
|
|
4347
4791
|
|
|
4348
4792
|
// Access-key authorization witnesses.
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
{
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
...itemBase(logIndexOfEvent(witnessBurned)),
|
|
4366
|
-
data: { account: lc(a.account), signer, witness: hx(a.witness) },
|
|
4367
|
-
type: 'key-authorization-witness-burned',
|
|
4368
|
-
},
|
|
4369
|
-
]
|
|
4370
|
-
}
|
|
4793
|
+
recognize(named('KeyAuthorizationWitness'), (event) => {
|
|
4794
|
+
const a = event.args as { account: Address.Address; witness: Hex.Hex }
|
|
4795
|
+
return {
|
|
4796
|
+
...itemBase(event.logIndex),
|
|
4797
|
+
data: { account: lc(a.account), signer, witness: hx(a.witness) },
|
|
4798
|
+
type: 'key-authorization-witness',
|
|
4799
|
+
}
|
|
4800
|
+
})
|
|
4801
|
+
recognize(named('KeyAuthorizationWitnessBurned'), (event) => {
|
|
4802
|
+
const a = event.args as { account: Address.Address; witness: Hex.Hex }
|
|
4803
|
+
return {
|
|
4804
|
+
...itemBase(event.logIndex),
|
|
4805
|
+
data: { account: lc(a.account), signer, witness: hx(a.witness) },
|
|
4806
|
+
type: 'key-authorization-witness-burned',
|
|
4807
|
+
}
|
|
4808
|
+
})
|
|
4371
4809
|
|
|
4372
|
-
// Validator configuration
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
const a =
|
|
4810
|
+
// Validator configuration. Rotation claims the add/deactivate pair it
|
|
4811
|
+
// bundles, matched by validator index.
|
|
4812
|
+
recognize(named('ValidatorRotated'), (event) => {
|
|
4813
|
+
const a = event.args as {
|
|
4376
4814
|
caller: Address.Address
|
|
4377
4815
|
deactivatedIndex: bigint
|
|
4378
4816
|
egress: string
|
|
@@ -4382,27 +4820,32 @@ function classifyFromLogsCore(
|
|
|
4382
4820
|
oldPublicKey: Hex.Hex
|
|
4383
4821
|
validatorAddress: Address.Address
|
|
4384
4822
|
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4823
|
+
unclaimed((e) => {
|
|
4824
|
+
if (!('index' in e.args)) return false
|
|
4825
|
+
const index = (e.args as { index: bigint }).index
|
|
4826
|
+
return (
|
|
4827
|
+
(e.eventName === 'ValidatorAdded' && index === a.index) ||
|
|
4828
|
+
(e.eventName === 'ValidatorDeactivated' && index === a.deactivatedIndex)
|
|
4829
|
+
)
|
|
4830
|
+
}).forEach(claim)
|
|
4831
|
+
return {
|
|
4832
|
+
...itemBase(event.logIndex),
|
|
4833
|
+
data: {
|
|
4834
|
+
caller: lc(a.caller),
|
|
4835
|
+
deactivatedIndex: a.deactivatedIndex.toString(),
|
|
4836
|
+
egress: a.egress,
|
|
4837
|
+
index: a.index.toString(),
|
|
4838
|
+
ingress: a.ingress,
|
|
4839
|
+
newPublicKey: hx(a.newPublicKey),
|
|
4840
|
+
oldPublicKey: hx(a.oldPublicKey),
|
|
4841
|
+
signer,
|
|
4842
|
+
validator: lc(a.validatorAddress),
|
|
4400
4843
|
},
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
const a =
|
|
4844
|
+
type: 'validator-rotated',
|
|
4845
|
+
}
|
|
4846
|
+
})
|
|
4847
|
+
recognize(named('ValidatorAdded'), (event) => {
|
|
4848
|
+
const a = event.args as {
|
|
4406
4849
|
egress: string
|
|
4407
4850
|
feeRecipient: Address.Address
|
|
4408
4851
|
index: bigint
|
|
@@ -4410,236 +4853,207 @@ function classifyFromLogsCore(
|
|
|
4410
4853
|
publicKey: Hex.Hex
|
|
4411
4854
|
validatorAddress: Address.Address
|
|
4412
4855
|
}
|
|
4413
|
-
return
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
validator: lc(a.validatorAddress),
|
|
4424
|
-
},
|
|
4425
|
-
type: 'validator-added',
|
|
4426
|
-
},
|
|
4427
|
-
]
|
|
4428
|
-
}
|
|
4429
|
-
const validatorDeactivated = findEvent('ValidatorDeactivated')
|
|
4430
|
-
if (validatorDeactivated) {
|
|
4431
|
-
const a = validatorDeactivated.args as { index: bigint; validatorAddress: Address.Address }
|
|
4432
|
-
return [
|
|
4433
|
-
{
|
|
4434
|
-
...itemBase(logIndexOfEvent(validatorDeactivated)),
|
|
4435
|
-
data: { index: a.index.toString(), signer, validator: lc(a.validatorAddress) },
|
|
4436
|
-
type: 'validator-deactivated',
|
|
4856
|
+
return {
|
|
4857
|
+
...itemBase(event.logIndex),
|
|
4858
|
+
data: {
|
|
4859
|
+
egress: a.egress,
|
|
4860
|
+
feeRecipient: lc(a.feeRecipient),
|
|
4861
|
+
index: a.index.toString(),
|
|
4862
|
+
ingress: a.ingress,
|
|
4863
|
+
publicKey: hx(a.publicKey),
|
|
4864
|
+
signer,
|
|
4865
|
+
validator: lc(a.validatorAddress),
|
|
4437
4866
|
},
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
const a =
|
|
4867
|
+
type: 'validator-added',
|
|
4868
|
+
}
|
|
4869
|
+
})
|
|
4870
|
+
recognize(named('ValidatorDeactivated'), (event) => {
|
|
4871
|
+
const a = event.args as { index: bigint; validatorAddress: Address.Address }
|
|
4872
|
+
return {
|
|
4873
|
+
...itemBase(event.logIndex),
|
|
4874
|
+
data: { index: a.index.toString(), signer, validator: lc(a.validatorAddress) },
|
|
4875
|
+
type: 'validator-deactivated',
|
|
4876
|
+
}
|
|
4877
|
+
})
|
|
4878
|
+
recognize(named('ValidatorMigrated'), (event) => {
|
|
4879
|
+
const a = event.args as {
|
|
4443
4880
|
index: bigint
|
|
4444
4881
|
publicKey: Hex.Hex
|
|
4445
4882
|
validatorAddress: Address.Address
|
|
4446
4883
|
}
|
|
4447
|
-
return
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
validator: lc(a.validatorAddress),
|
|
4455
|
-
},
|
|
4456
|
-
type: 'validator-migrated',
|
|
4884
|
+
return {
|
|
4885
|
+
...itemBase(event.logIndex),
|
|
4886
|
+
data: {
|
|
4887
|
+
index: a.index.toString(),
|
|
4888
|
+
publicKey: hx(a.publicKey),
|
|
4889
|
+
signer,
|
|
4890
|
+
validator: lc(a.validatorAddress),
|
|
4457
4891
|
},
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
const a =
|
|
4892
|
+
type: 'validator-migrated',
|
|
4893
|
+
}
|
|
4894
|
+
})
|
|
4895
|
+
recognize(named('SkippedValidatorMigration'), (event) => {
|
|
4896
|
+
const a = event.args as {
|
|
4463
4897
|
index: bigint
|
|
4464
4898
|
publicKey: Hex.Hex
|
|
4465
4899
|
validatorAddress: Address.Address
|
|
4466
4900
|
}
|
|
4467
|
-
return
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
validator: lc(a.validatorAddress),
|
|
4475
|
-
},
|
|
4476
|
-
type: 'validator-migration-skipped',
|
|
4901
|
+
return {
|
|
4902
|
+
...itemBase(event.logIndex),
|
|
4903
|
+
data: {
|
|
4904
|
+
index: a.index.toString(),
|
|
4905
|
+
publicKey: hx(a.publicKey),
|
|
4906
|
+
signer,
|
|
4907
|
+
validator: lc(a.validatorAddress),
|
|
4477
4908
|
},
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
const a =
|
|
4909
|
+
type: 'validator-migration-skipped',
|
|
4910
|
+
}
|
|
4911
|
+
})
|
|
4912
|
+
recognize(named('FeeRecipientUpdated'), (event) => {
|
|
4913
|
+
const a = event.args as {
|
|
4483
4914
|
caller: Address.Address
|
|
4484
4915
|
feeRecipient: Address.Address
|
|
4485
4916
|
index: bigint
|
|
4486
4917
|
}
|
|
4487
|
-
return
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
signer,
|
|
4495
|
-
},
|
|
4496
|
-
type: 'validator-fee-recipient-set',
|
|
4918
|
+
return {
|
|
4919
|
+
...itemBase(event.logIndex),
|
|
4920
|
+
data: {
|
|
4921
|
+
caller: lc(a.caller),
|
|
4922
|
+
feeRecipient: lc(a.feeRecipient),
|
|
4923
|
+
index: a.index.toString(),
|
|
4924
|
+
signer,
|
|
4497
4925
|
},
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
const a =
|
|
4926
|
+
type: 'validator-fee-recipient-set',
|
|
4927
|
+
}
|
|
4928
|
+
})
|
|
4929
|
+
recognize(named('IpAddressesUpdated'), (event) => {
|
|
4930
|
+
const a = event.args as {
|
|
4503
4931
|
caller: Address.Address
|
|
4504
4932
|
egress: string
|
|
4505
4933
|
index: bigint
|
|
4506
4934
|
ingress: string
|
|
4507
4935
|
}
|
|
4508
|
-
return
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
signer,
|
|
4517
|
-
},
|
|
4518
|
-
type: 'validator-ip-set',
|
|
4936
|
+
return {
|
|
4937
|
+
...itemBase(event.logIndex),
|
|
4938
|
+
data: {
|
|
4939
|
+
caller: lc(a.caller),
|
|
4940
|
+
egress: a.egress,
|
|
4941
|
+
index: a.index.toString(),
|
|
4942
|
+
ingress: a.ingress,
|
|
4943
|
+
signer,
|
|
4519
4944
|
},
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
const a =
|
|
4945
|
+
type: 'validator-ip-set',
|
|
4946
|
+
}
|
|
4947
|
+
})
|
|
4948
|
+
recognize(named('ValidatorOwnershipTransferred'), (event) => {
|
|
4949
|
+
const a = event.args as {
|
|
4525
4950
|
caller: Address.Address
|
|
4526
4951
|
index: bigint
|
|
4527
4952
|
newAddress: Address.Address
|
|
4528
4953
|
oldAddress: Address.Address
|
|
4529
4954
|
}
|
|
4530
|
-
return
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
signer,
|
|
4539
|
-
},
|
|
4540
|
-
type: 'validator-ownership-transferred',
|
|
4955
|
+
return {
|
|
4956
|
+
...itemBase(event.logIndex),
|
|
4957
|
+
data: {
|
|
4958
|
+
caller: lc(a.caller),
|
|
4959
|
+
index: a.index.toString(),
|
|
4960
|
+
newAddress: lc(a.newAddress),
|
|
4961
|
+
oldAddress: lc(a.oldAddress),
|
|
4962
|
+
signer,
|
|
4541
4963
|
},
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
const a =
|
|
4964
|
+
type: 'validator-ownership-transferred',
|
|
4965
|
+
}
|
|
4966
|
+
})
|
|
4967
|
+
recognize(named('OwnershipTransferred'), (event) => {
|
|
4968
|
+
const a = event.args as {
|
|
4547
4969
|
newOwner: Address.Address
|
|
4548
4970
|
oldOwner: Address.Address
|
|
4549
4971
|
}
|
|
4550
|
-
return
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
data: {
|
|
4565
|
-
nextEpoch: a.nextEpoch.toString(),
|
|
4566
|
-
previousEpoch: a.previousEpoch.toString(),
|
|
4567
|
-
signer,
|
|
4568
|
-
},
|
|
4569
|
-
type: 'network-identity-rotation-epoch-set',
|
|
4570
|
-
},
|
|
4571
|
-
]
|
|
4572
|
-
}
|
|
4573
|
-
const initialized = findEvent('Initialized')
|
|
4574
|
-
if (initialized) {
|
|
4575
|
-
const a = initialized.args as { height: bigint }
|
|
4576
|
-
return [
|
|
4577
|
-
{
|
|
4578
|
-
...itemBase(logIndexOfEvent(initialized)),
|
|
4579
|
-
data: { height: a.height.toString(), signer },
|
|
4580
|
-
type: 'initialized',
|
|
4972
|
+
return {
|
|
4973
|
+
...itemBase(event.logIndex),
|
|
4974
|
+
data: { newOwner: lc(a.newOwner), oldOwner: lc(a.oldOwner), signer },
|
|
4975
|
+
type: 'ownership-transferred',
|
|
4976
|
+
}
|
|
4977
|
+
})
|
|
4978
|
+
recognize(named('NetworkIdentityRotationEpochSet'), (event) => {
|
|
4979
|
+
const a = event.args as { nextEpoch: bigint; previousEpoch: bigint }
|
|
4980
|
+
return {
|
|
4981
|
+
...itemBase(event.logIndex),
|
|
4982
|
+
data: {
|
|
4983
|
+
nextEpoch: a.nextEpoch.toString(),
|
|
4984
|
+
previousEpoch: a.previousEpoch.toString(),
|
|
4985
|
+
signer,
|
|
4581
4986
|
},
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
const args = t.args as {
|
|
4592
|
-
amount: bigint
|
|
4593
|
-
from: Address.Address
|
|
4594
|
-
memo?: Hex.Hex | undefined
|
|
4595
|
-
to: Address.Address
|
|
4987
|
+
type: 'network-identity-rotation-epoch-set',
|
|
4988
|
+
}
|
|
4989
|
+
})
|
|
4990
|
+
recognize(named('Initialized'), (event) => {
|
|
4991
|
+
const a = event.args as { height: bigint }
|
|
4992
|
+
return {
|
|
4993
|
+
...itemBase(event.logIndex),
|
|
4994
|
+
data: { height: a.height.toString(), signer },
|
|
4995
|
+
type: 'initialized',
|
|
4596
4996
|
}
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4997
|
+
})
|
|
4998
|
+
|
|
4999
|
+
// Plain transfers: every remaining viewer-party transfer is its own
|
|
5000
|
+
// activity. Non-viewer transfers are internal legs of what classified
|
|
5001
|
+
// above and surface only via the fallback below.
|
|
5002
|
+
const buildTransfer = (event: Decoded): RawItem => {
|
|
5003
|
+
const args = transferArgs(event)
|
|
5004
|
+
const to = lc(args.to)
|
|
5005
|
+
const from = lc(args.from)
|
|
5006
|
+
const rawMemo = event.eventName === 'TransferWithMemo' ? args.memo : undefined
|
|
4600
5007
|
// The raw memo (not the suppressed display string) carries the MPP
|
|
4601
5008
|
// attribution fingerprint, so resolve the service name from it.
|
|
4602
5009
|
const memo = rawMemo ? Transfers.memoToString(rawMemo) : undefined
|
|
4603
5010
|
const attribution = Mpp.resolve({ fingerprintMap, memo: rawMemo })
|
|
4604
|
-
const sourceToken = token(args.amount,
|
|
5011
|
+
const sourceToken = token(args.amount, event.address as Address.Address)
|
|
4605
5012
|
if (Address.isEqual(to, viewer))
|
|
4606
|
-
return
|
|
4607
|
-
transfer(itemBase(logIndexOfEvent(t)), blockNum, {
|
|
4608
|
-
attribution,
|
|
4609
|
-
direction: 'in',
|
|
4610
|
-
memo,
|
|
4611
|
-
recipient: viewer,
|
|
4612
|
-
sender: from,
|
|
4613
|
-
sourceToken,
|
|
4614
|
-
}),
|
|
4615
|
-
...accessKeyItems,
|
|
4616
|
-
]
|
|
4617
|
-
return [
|
|
4618
|
-
transfer(itemBase(logIndexOfEvent(t)), blockNum, {
|
|
5013
|
+
return transfer(itemBase(event.logIndex), blockNum, {
|
|
4619
5014
|
attribution,
|
|
4620
|
-
direction: '
|
|
5015
|
+
direction: 'in',
|
|
4621
5016
|
memo,
|
|
4622
|
-
recipient:
|
|
5017
|
+
recipient: viewer,
|
|
4623
5018
|
sender: from,
|
|
4624
|
-
signer,
|
|
4625
5019
|
sourceToken,
|
|
4626
|
-
})
|
|
4627
|
-
|
|
4628
|
-
|
|
5020
|
+
})
|
|
5021
|
+
return transfer(itemBase(event.logIndex), blockNum, {
|
|
5022
|
+
attribution,
|
|
5023
|
+
direction: 'out',
|
|
5024
|
+
memo,
|
|
5025
|
+
recipient: to,
|
|
5026
|
+
sender: from,
|
|
5027
|
+
signer,
|
|
5028
|
+
sourceToken,
|
|
5029
|
+
})
|
|
4629
5030
|
}
|
|
5031
|
+
const isUserTransfer = (event: Decoded) =>
|
|
5032
|
+
isTransfer(event) && !Address.isEqual(transferArgs(event).to, Addresses.feeManager)
|
|
5033
|
+
recognize(
|
|
5034
|
+
(event) =>
|
|
5035
|
+
isUserTransfer(event) &&
|
|
5036
|
+
(Address.isEqual(transferArgs(event).from, viewer) ||
|
|
5037
|
+
Address.isEqual(transferArgs(event).to, viewer)),
|
|
5038
|
+
buildTransfer,
|
|
5039
|
+
)
|
|
4630
5040
|
|
|
4631
|
-
//
|
|
4632
|
-
|
|
5041
|
+
// Fallback when nothing viewer-facing classified: surface fold-only vault
|
|
5042
|
+
// items, third-party approvals, and transfers (e.g. a sponsored batch)
|
|
5043
|
+
// instead of reporting `unknown`.
|
|
5044
|
+
if (items.length === 0) {
|
|
5045
|
+
items.push(...suppressedVaultItems)
|
|
5046
|
+
recognize(isErc20Approval, approvalItem)
|
|
5047
|
+
recognize(isUserTransfer, buildTransfer)
|
|
5048
|
+
}
|
|
4633
5049
|
|
|
4634
|
-
// Unclassified: a tx the address sent or paid for whose logs matched no
|
|
4635
|
-
// classifier. Surface it honestly as an `unknown` item carrying every
|
|
4636
|
-
// (decoded when possible), rather than fabricating a zero transfer.
|
|
4637
|
-
|
|
4638
|
-
}
|
|
5050
|
+
// Unclassified: a tx the address sent or paid for whose logs matched no
|
|
5051
|
+
// known classifier. Surface it honestly as an `unknown` item carrying every
|
|
5052
|
+
// log (decoded when possible), rather than fabricating a zero transfer.
|
|
5053
|
+
if (items.length === 0)
|
|
5054
|
+
return [unknown(itemBase(minLogIndex), { events: buildTxEvents(rows, events), signer })]
|
|
4639
5055
|
|
|
4640
|
-
|
|
4641
|
-
function logIndexOfEvent(event: { logIndex: number }): number {
|
|
4642
|
-
return event.logIndex
|
|
5056
|
+
return items.sort((a, b) => a.logIndex - b.logIndex)
|
|
4643
5057
|
}
|
|
4644
5058
|
|
|
4645
5059
|
/**
|
|
@@ -4703,8 +5117,8 @@ function unknown(
|
|
|
4703
5117
|
|
|
4704
5118
|
/** Decodes a group's raw rows into `viem/tempo` events via `parseEventLogs` (non-strict). */
|
|
4705
5119
|
function decodeRows(rows: readonly Row[]) {
|
|
4706
|
-
|
|
4707
|
-
|
|
5120
|
+
const logs = rows.map(
|
|
5121
|
+
(r): Log => ({
|
|
4708
5122
|
address: r.logAddress as `0x${string}`,
|
|
4709
5123
|
blockHash: '0x0' as Hex.Hex,
|
|
4710
5124
|
blockNumber: BigInt(r.blockNum),
|
|
@@ -4714,17 +5128,21 @@ function decodeRows(rows: readonly Row[]) {
|
|
|
4714
5128
|
topics: [r.selector, r.topic1, r.topic2, r.topic3].filter(Boolean) as [Hex.Hex, ...Hex.Hex[]],
|
|
4715
5129
|
transactionHash: r.txHash as `0x${string}`,
|
|
4716
5130
|
transactionIndex: r.txIdx,
|
|
4717
|
-
}
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
5131
|
+
}),
|
|
5132
|
+
)
|
|
5133
|
+
try {
|
|
5134
|
+
// One batched call: viem re-processes the merged ABI per invocation, so
|
|
5135
|
+
// per-row calls cost ~55x more. Non-strict decoding skips undecodable
|
|
5136
|
+
// logs per log and can yield `args: undefined`; normalize so `in`-guards
|
|
5137
|
+
// never throw.
|
|
5138
|
+
return parseEventLogs({
|
|
5139
|
+
abi: [...Abis.abis, ...Abis.earnRouter, ...Abis.earnVault, ...erc4626Events],
|
|
5140
|
+
logs,
|
|
5141
|
+
strict: false,
|
|
5142
|
+
}).map((event) => ({ ...event, args: event.args ?? {} }))
|
|
5143
|
+
} catch {
|
|
5144
|
+
return []
|
|
5145
|
+
}
|
|
4728
5146
|
}
|
|
4729
5147
|
|
|
4730
5148
|
/**
|
|
@@ -4937,12 +5355,12 @@ declare namespace enrichTokens {
|
|
|
4937
5355
|
}
|
|
4938
5356
|
}
|
|
4939
5357
|
|
|
4940
|
-
/** Resolves token metadata on each item's source
|
|
4941
|
-
async function
|
|
5358
|
+
/** Resolves token metadata on each item's source chain without changing feed order. */
|
|
5359
|
+
async function enrichChainTokens(
|
|
4942
5360
|
c: Context<App.Environment>,
|
|
4943
|
-
options:
|
|
5361
|
+
options: enrichChainTokens.Options,
|
|
4944
5362
|
): Promise<{ items: Item[]; rates: FxOracle.RateSet | undefined }> {
|
|
4945
|
-
const byChain = new Map<number,
|
|
5363
|
+
const byChain = new Map<number, listChains.Item[]>()
|
|
4946
5364
|
for (const item of options.items) {
|
|
4947
5365
|
const items = byChain.get(item.chainId) ?? []
|
|
4948
5366
|
items.push(item)
|
|
@@ -4972,14 +5390,14 @@ async function enrichZoneTokens(
|
|
|
4972
5390
|
}
|
|
4973
5391
|
}
|
|
4974
5392
|
|
|
4975
|
-
declare namespace
|
|
5393
|
+
declare namespace enrichChainTokens {
|
|
4976
5394
|
type Options = {
|
|
4977
5395
|
/** Currency the amount valuations are denominated in. */
|
|
4978
5396
|
denomination?: string | undefined
|
|
4979
5397
|
/** Optional token fields to resolve. */
|
|
4980
5398
|
include: readonly z.output<typeof schema.Include>[]
|
|
4981
5399
|
/** Chain-tagged items awaiting token enrichment. */
|
|
4982
|
-
items: readonly
|
|
5400
|
+
items: readonly listChains.Item[]
|
|
4983
5401
|
/** FX rate oracle backing amount valuation. */
|
|
4984
5402
|
oracle: FxOracle.Oracle
|
|
4985
5403
|
}
|