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
|
@@ -37,29 +37,26 @@ export var schema;
|
|
|
37
37
|
.union([z.literal('self'), Schema.Address])
|
|
38
38
|
.check(z.describe('Who signed the activity: `self`, or the access-key address used to sign it.'));
|
|
39
39
|
/** A token with required RPC metadata and optional curated fields. */
|
|
40
|
-
schema.Token = Schema.describe(Tokens.schema.TokenReference, 'A token referenced by an activity item.');
|
|
41
|
-
|
|
42
|
-
const ValuedAmount = z.extend(Schema.TokenAmount, {
|
|
40
|
+
schema.Token = OpenApi.component(Schema.describe(Tokens.schema.TokenReference, 'A token referenced by an activity item.'), 'ActivityToken');
|
|
41
|
+
const ValuedAmount = Schema.describe(z.extend(Schema.TokenAmount, {
|
|
43
42
|
valuation: z
|
|
44
43
|
.optional(z.nullable(Valuation.schema.Value))
|
|
45
44
|
.check(z.describe('The amount’s nominal value in the requested `valuation.currency`. `null` when the token is ' +
|
|
46
45
|
'unverified, its display currency has no rate, or rates were unavailable.')),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const DestinationAmount =
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
/** Self-contained amount for a source token. */
|
|
53
|
-
const SourceAmount = ValuedAmount.check(z.describe('Amount associated with `sourceToken`.'));
|
|
46
|
+
}), 'A token amount carrying its nominal value in the requested denomination.');
|
|
47
|
+
const valuedAmount = OpenApi.component(ValuedAmount, 'ActivityValuedAmount');
|
|
48
|
+
const DestinationAmount = valuedAmount;
|
|
49
|
+
const RefundAmount = valuedAmount;
|
|
50
|
+
const SourceAmount = valuedAmount;
|
|
54
51
|
/** Page-level resources: valuation rate provenance. */
|
|
55
|
-
schema.Meta = z
|
|
52
|
+
schema.Meta = OpenApi.component(z
|
|
56
53
|
.object({
|
|
57
54
|
valuation: z
|
|
58
55
|
.optional(Valuation.schema.Pricing)
|
|
59
56
|
.check(z.describe('Rate provenance for amount valuations. Present when conversion rates were ' +
|
|
60
57
|
'consulted; absent when every value was identity-valued or rates were unavailable.')),
|
|
61
58
|
})
|
|
62
|
-
.check(z.describe('Page-level resources attached to this response.'));
|
|
59
|
+
.check(z.describe('Page-level resources attached to this response.')), 'ActivityMeta');
|
|
63
60
|
/** A token transfer with subject-relative direction and signer fields. */
|
|
64
61
|
schema.Transfer = z
|
|
65
62
|
.object({
|
|
@@ -97,6 +94,7 @@ export var schema;
|
|
|
97
94
|
error: '`destinationAmount` and `destinationToken` must be returned together.',
|
|
98
95
|
path: ['destinationAmount'],
|
|
99
96
|
}), z.describe('One token transfer involving the requested address.'));
|
|
97
|
+
const activityTransfer = OpenApi.component(schema.Transfer, 'ActivityTransfer');
|
|
100
98
|
/**
|
|
101
99
|
* One transaction log, decoded into a known Tempo event when possible. The
|
|
102
100
|
* raw fields (`address`/`data`/`logIndex`/`topics`) are always present so no
|
|
@@ -124,6 +122,7 @@ export var schema;
|
|
|
124
122
|
.check(z.describe('Raw log topics; the first topic is the event signature hash.')),
|
|
125
123
|
})
|
|
126
124
|
.check(z.describe('One transaction log. Decoded fields (`eventName`/`args`/`title`) are present when it matched a known Tempo ABI; otherwise only the raw fields are set.'));
|
|
125
|
+
const activityEvent = OpenApi.component(event, 'ActivityEvent');
|
|
127
126
|
/**
|
|
128
127
|
* Fields shared by every activity item. `transactionHash`/`timestamp` identify and sort
|
|
129
128
|
* the entry; `data` carries the type-specific payload — shaped like the
|
|
@@ -133,12 +132,12 @@ export var schema;
|
|
|
133
132
|
const base = {
|
|
134
133
|
chainId: z
|
|
135
134
|
.optional(Schema.ChainId)
|
|
136
|
-
.check(z.describe('Chain
|
|
135
|
+
.check(z.describe('Chain ID containing this activity. Returned when `include=zones`.'), z.meta({ examples: [421700001] })),
|
|
137
136
|
perspective: z
|
|
138
137
|
.enum(['incoming', 'outgoing'])
|
|
139
138
|
.check(z.describe('Your role in the transaction: `outgoing` when you sent or paid for it, `incoming` when it happened to you (someone else initiated it). For a `transfer`, `data.direction` is the finer-grained token-flow view.')),
|
|
140
139
|
events: z
|
|
141
|
-
.optional(z.array(
|
|
140
|
+
.optional(z.array(activityEvent))
|
|
142
141
|
.check(z.describe('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.')),
|
|
143
142
|
id: z.string().check(z.describe('Stable activity ID built from the transaction hash and log index (`${transactionHash}-${logIndex}`).'), z.meta({
|
|
144
143
|
examples: ['0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665-3'],
|
|
@@ -154,8 +153,7 @@ export var schema;
|
|
|
154
153
|
.check(z.describe('Human-readable label for the activity `type`, suitable for display in a feed. Past-tense, sentence case (e.g. `Token transferred`, `Tokens swapped`, `Burn blocked`).'), z.meta({ examples: ['Token transferred'] })),
|
|
155
154
|
transactionHash: Schema.Hash.check(z.describe('Transaction hash for this activity.')),
|
|
156
155
|
};
|
|
157
|
-
|
|
158
|
-
schema.items = [
|
|
156
|
+
const itemSchemas = [
|
|
159
157
|
z
|
|
160
158
|
.object({
|
|
161
159
|
...base,
|
|
@@ -299,7 +297,7 @@ export var schema;
|
|
|
299
297
|
z
|
|
300
298
|
.object({
|
|
301
299
|
...base,
|
|
302
|
-
data:
|
|
300
|
+
data: activityTransfer.check(z.describe('Transfer details with feed-relative direction and signer fields.')),
|
|
303
301
|
type: z
|
|
304
302
|
.literal('transfer')
|
|
305
303
|
.check(z.describe('Activity type that tells you which `data` shape this entry uses.')),
|
|
@@ -1256,16 +1254,24 @@ export var schema;
|
|
|
1256
1254
|
})
|
|
1257
1255
|
.check(z.meta({ title: 'Unknown' })),
|
|
1258
1256
|
];
|
|
1257
|
+
/** The classified activity item variants (discriminated by `type`). */
|
|
1258
|
+
schema.items = itemSchemas.map((item) => {
|
|
1259
|
+
const [type] = item.shape.type.def.values;
|
|
1260
|
+
if (typeof type !== 'string')
|
|
1261
|
+
throw new Error('Activity item schemas require a string type.');
|
|
1262
|
+
const name = `ActivityItem${type
|
|
1263
|
+
.replace(/[^A-Za-z0-9]+(.)/g, (_, character) => character.toUpperCase())
|
|
1264
|
+
.replace(/^(.)/, (character) => character.toUpperCase())}`;
|
|
1265
|
+
return OpenApi.component(item, name);
|
|
1266
|
+
});
|
|
1259
1267
|
/** A single classified activity item. */
|
|
1260
|
-
schema.Item = z
|
|
1261
|
-
.union(schema.items)
|
|
1262
|
-
.check(z.describe('One classified activity item in the address feed.'));
|
|
1268
|
+
schema.Item = OpenApi.component(z.union(schema.items).check(z.describe('One classified activity item in the address feed.')), 'ActivityItem');
|
|
1263
1269
|
/** A folded access-key group: consecutive same-signer items on one UTC day. */
|
|
1264
1270
|
schema.Group = z
|
|
1265
1271
|
.object({
|
|
1266
1272
|
chainId: z
|
|
1267
1273
|
.optional(Schema.ChainId)
|
|
1268
|
-
.check(z.describe('Chain
|
|
1274
|
+
.check(z.describe('Chain ID containing this activity group. Returned when `include=zones`.'), z.meta({ examples: [421700001] })),
|
|
1269
1275
|
data: z
|
|
1270
1276
|
.object({
|
|
1271
1277
|
items: z.array(schema.Item).check(z.describe('Activity items included in this group.')),
|
|
@@ -1289,16 +1295,17 @@ export var schema;
|
|
|
1289
1295
|
type: z.literal('group'),
|
|
1290
1296
|
})
|
|
1291
1297
|
.check(z.meta({ title: 'Group' }), z.describe('A group of consecutive activity items signed by the same access key.'));
|
|
1298
|
+
const activityGroup = OpenApi.component(schema.Group, 'ActivityGroup');
|
|
1292
1299
|
/** A single activity item, or a folded access-key group. */
|
|
1293
|
-
schema.Entry = z
|
|
1294
|
-
.union([
|
|
1295
|
-
.check(z.describe('Either one activity item, or a grouped set of access-key activity items.'));
|
|
1300
|
+
schema.Entry = OpenApi.component(z
|
|
1301
|
+
.union([schema.Item, activityGroup])
|
|
1302
|
+
.check(z.describe('Either one activity item, or a grouped set of access-key activity items.')), 'ActivityEntry');
|
|
1296
1303
|
/** Optional activity resources and selection modes. */
|
|
1297
1304
|
schema.Include = z
|
|
1298
1305
|
.enum(includeFields)
|
|
1299
1306
|
.check(z.describe('Additional activity resources and selection modes to include.'));
|
|
1300
1307
|
/** Parses comma-separated optional activity resources and selection modes. */
|
|
1301
|
-
schema.includeQuery = Schema.includeQuery(schema.Include, 'Comma-separated activity options
|
|
1308
|
+
schema.includeQuery = Schema.includeQuery(schema.Include, 'Comma-separated activity options. `zones` combines the selected parent chain with every readable Zone.');
|
|
1302
1309
|
/** Schemas for the getAddressActivities operation. */
|
|
1303
1310
|
let getAddressActivities;
|
|
1304
1311
|
(function (getAddressActivities) {
|
|
@@ -1333,7 +1340,7 @@ export var schema;
|
|
|
1333
1340
|
* `group=true`) plus the root `nextCursor` pagination field shared with
|
|
1334
1341
|
* other list endpoints.
|
|
1335
1342
|
*/
|
|
1336
|
-
getAddressActivities.Response = z
|
|
1343
|
+
getAddressActivities.Response = OpenApi.component(z
|
|
1337
1344
|
.object({
|
|
1338
1345
|
data: z.array(schema.Entry).check(z.describe('Activity feed entries for the address.')),
|
|
1339
1346
|
meta: z
|
|
@@ -1341,7 +1348,7 @@ export var schema;
|
|
|
1341
1348
|
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1342
1349
|
nextCursor: Schema.NextCursor,
|
|
1343
1350
|
})
|
|
1344
|
-
.check(z.describe('A page of human-readable activity for the address.'));
|
|
1351
|
+
.check(z.describe('A page of human-readable activity for the address.')), 'AddressActivityList');
|
|
1345
1352
|
})(getAddressActivities = schema.getAddressActivities || (schema.getAddressActivities = {}));
|
|
1346
1353
|
/** Schemas for the getTransactionActivities operation. */
|
|
1347
1354
|
let getTransactionActivities;
|
|
@@ -1366,20 +1373,20 @@ export var schema;
|
|
|
1366
1373
|
})
|
|
1367
1374
|
.check(z.describe('Query parameters for the transaction activity request.'));
|
|
1368
1375
|
/**
|
|
1369
|
-
* The classified activity for one transaction: a list of `data` items
|
|
1370
|
-
*
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1376
|
+
* The classified activity for one transaction: a list of `data` items,
|
|
1377
|
+
* one per independent activity, describing what the transaction did
|
|
1378
|
+
* onchain. Unpaginated (a single transaction is finite), so there is no
|
|
1379
|
+
* `nextCursor`.
|
|
1373
1380
|
*/
|
|
1374
|
-
getTransactionActivities.Response = z
|
|
1381
|
+
getTransactionActivities.Response = OpenApi.component(z
|
|
1375
1382
|
.object({
|
|
1376
|
-
chainId: Schema.ChainId.check(z.describe('Chain
|
|
1383
|
+
chainId: Schema.ChainId.check(z.describe('Chain ID containing the transaction.'), z.meta({ examples: [4217] })),
|
|
1377
1384
|
data: z.array(schema.Item).check(z.describe('Activity items classified from the transaction.')),
|
|
1378
1385
|
meta: z
|
|
1379
1386
|
.optional(schema.Meta)
|
|
1380
1387
|
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1381
1388
|
})
|
|
1382
|
-
.check(z.describe('The human-readable activity that happened on a transaction.'));
|
|
1389
|
+
.check(z.describe('The human-readable activity that happened on a transaction.')), 'TransactionActivityList');
|
|
1383
1390
|
})(getTransactionActivities = schema.getTransactionActivities || (schema.getTransactionActivities = {}));
|
|
1384
1391
|
})(schema || (schema = {}));
|
|
1385
1392
|
/**
|
|
@@ -1468,6 +1475,7 @@ const titleByEventName = {
|
|
|
1468
1475
|
BlacklistUpdated: 'Blacklist updated',
|
|
1469
1476
|
Burn: 'Token burned',
|
|
1470
1477
|
BurnBlocked: 'Burn blocked',
|
|
1478
|
+
Deposit: 'Assets deposited',
|
|
1471
1479
|
Deposited: 'Assets deposited',
|
|
1472
1480
|
EarnDeposit: 'Private assets deposited',
|
|
1473
1481
|
EarnRedeem: 'Private shares redeemed',
|
|
@@ -1476,6 +1484,7 @@ const titleByEventName = {
|
|
|
1476
1484
|
RedeemRequested: 'Share redemption requested',
|
|
1477
1485
|
Redeemed: 'Shares redeemed',
|
|
1478
1486
|
VenueSharesDeposited: 'Shares deposited',
|
|
1487
|
+
Withdraw: 'Assets withdrawn',
|
|
1479
1488
|
WithdrewExact: 'Assets withdrawn',
|
|
1480
1489
|
ChannelClosed: 'Channel closed',
|
|
1481
1490
|
ChannelOpened: 'Channel opened',
|
|
@@ -1539,12 +1548,43 @@ const closeRequestedSelector = '0xf5a36fc00a96cbb9cf1f8f59299165e1d8ffffe94396d8
|
|
|
1539
1548
|
const accessKeySpendSelector = '0xe0815e3aaadddf4dd75bde97fc060f0c38afe18e87a169be86a3f5c28247f192';
|
|
1540
1549
|
/** TIP-20 / ERC-20 `Transfer` event signature for the decoded CTE. */
|
|
1541
1550
|
const transferSignature = 'event Transfer(address indexed from, address indexed to, uint256 value)';
|
|
1551
|
+
// Standard ERC-4626 vault events, absent from the merged `viem/tempo` ABI.
|
|
1552
|
+
// Decoding them lets external vault deposits/withdrawals classify instead of
|
|
1553
|
+
// falling through to the share-token mint/burn legs.
|
|
1554
|
+
const erc4626Events = [
|
|
1555
|
+
{
|
|
1556
|
+
inputs: [
|
|
1557
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1558
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1559
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1560
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1561
|
+
],
|
|
1562
|
+
name: 'Deposit',
|
|
1563
|
+
type: 'event',
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
inputs: [
|
|
1567
|
+
{ indexed: true, name: 'sender', type: 'address' },
|
|
1568
|
+
{ indexed: true, name: 'receiver', type: 'address' },
|
|
1569
|
+
{ indexed: true, name: 'owner', type: 'address' },
|
|
1570
|
+
{ indexed: false, name: 'assets', type: 'uint256' },
|
|
1571
|
+
{ indexed: false, name: 'shares', type: 'uint256' },
|
|
1572
|
+
],
|
|
1573
|
+
name: 'Withdraw',
|
|
1574
|
+
type: 'event',
|
|
1575
|
+
},
|
|
1576
|
+
];
|
|
1542
1577
|
// TIP-20 payment-channel reserve precompile. Not exported by `viem/tempo`
|
|
1543
1578
|
// `Addresses`, so the known address is pinned here: channel events
|
|
1544
1579
|
// (`ChannelOpened`/`Settled`/`TopUp`/`ChannelClosed`) are emitted here, and
|
|
1545
1580
|
// their paired TIP-20 `Transfer` legs use it as the funding counterparty, which
|
|
1546
1581
|
// is how `channel-funded`/`channel-settled`/`channel-closed` recover the token.
|
|
1547
1582
|
const tip20ChannelReserve = '0x4d50500000000000000000000000000000000000';
|
|
1583
|
+
// Tempo system addresses other than the DEX are never swap-route
|
|
1584
|
+
// counterparties; a newly shipped precompile joins the exclusion automatically.
|
|
1585
|
+
const nonRouteSystemAddresses = new Set([...Object.values(Addresses), tip20ChannelReserve]
|
|
1586
|
+
.filter((value) => value !== Addresses.stablecoinDex)
|
|
1587
|
+
.map((value) => value.toLowerCase()));
|
|
1548
1588
|
/** Number of grouped entries to target before stopping the page-fetch loop. */
|
|
1549
1589
|
const minGroupEntries = 5;
|
|
1550
1590
|
/** Largest number of items folded into a single access-key group. */
|
|
@@ -1596,7 +1636,12 @@ export function addresses(options = {}) {
|
|
|
1596
1636
|
description: 'Get a human-readable feed of what an address has been doing onchain, including payments sent and received, swaps, approvals, and more.',
|
|
1597
1637
|
operationId: 'getAddressActivities',
|
|
1598
1638
|
responses: OpenApi.responses({
|
|
1599
|
-
errors: {
|
|
1639
|
+
errors: {
|
|
1640
|
+
400: {
|
|
1641
|
+
codes: ['address_invalid', 'chain_id_invalid', 'chain_id_unsupported', 'query_invalid'],
|
|
1642
|
+
},
|
|
1643
|
+
502: 'Tempo could not read activity or token data from an upstream service.',
|
|
1644
|
+
},
|
|
1600
1645
|
success: {
|
|
1601
1646
|
description: 'A page of address activity entries.',
|
|
1602
1647
|
schema: schema.getAddressActivities.Response,
|
|
@@ -1629,7 +1674,7 @@ export function addresses(options = {}) {
|
|
|
1629
1674
|
const chainId = query.chainId ?? c.get('chainId');
|
|
1630
1675
|
const listed = await Timing.time(c, 'address_activities', async () => {
|
|
1631
1676
|
if (!query.include.includes('zones')) {
|
|
1632
|
-
const result = await Store.memoize(() => list(tidx({ chainId, tidx: c.get('getTidx')(chainId) }), {
|
|
1677
|
+
const result = await Store.memoize((signal) => list(tidx({ chainId, signal, tidx: c.get('getTidx')(chainId) }), {
|
|
1633
1678
|
address: address,
|
|
1634
1679
|
cursor: query.cursor,
|
|
1635
1680
|
group: query.group,
|
|
@@ -1646,29 +1691,31 @@ export function addresses(options = {}) {
|
|
|
1646
1691
|
const zoneChainIds = c.get('zoneChainIds');
|
|
1647
1692
|
if (!zoneChainIds)
|
|
1648
1693
|
throw new Error('Inferred Zone selection was not resolved.');
|
|
1649
|
-
const
|
|
1694
|
+
const chainIds = [chainId, ...zoneChainIds];
|
|
1695
|
+
const result = await Store.memoize((signal) => listChains({
|
|
1650
1696
|
address: address,
|
|
1651
1697
|
cursor: query.cursor,
|
|
1652
1698
|
group: query.group,
|
|
1653
1699
|
limit: query.limit,
|
|
1654
1700
|
logs: query.logs,
|
|
1655
|
-
providers:
|
|
1656
|
-
chainId
|
|
1701
|
+
providers: chainIds.map((chainId) => ({
|
|
1702
|
+
chainId,
|
|
1657
1703
|
provider: tidx({
|
|
1658
|
-
chainId
|
|
1659
|
-
|
|
1704
|
+
chainId,
|
|
1705
|
+
signal,
|
|
1706
|
+
tidx: c.get('getTidx')(chainId),
|
|
1660
1707
|
}),
|
|
1661
1708
|
})),
|
|
1662
1709
|
store: c.get('store'),
|
|
1663
1710
|
}), {
|
|
1664
|
-
key: `activities:zones:
|
|
1711
|
+
key: `activities:zones:v2:${chainIds.join(',')}:${address.toLowerCase()}:${query.group ?? false}:${query.logs ?? false}:${query.cursor ?? 'head'}:${query.limit}`,
|
|
1665
1712
|
store: c.get('store'),
|
|
1666
1713
|
ttl: Ttl.seconds(15),
|
|
1667
1714
|
});
|
|
1668
1715
|
return { kind: 'zones', ...result };
|
|
1669
1716
|
});
|
|
1670
1717
|
const { items, rates } = listed.kind === 'zones'
|
|
1671
|
-
? await
|
|
1718
|
+
? await enrichChainTokens(c, {
|
|
1672
1719
|
denomination: query['valuation.currency'],
|
|
1673
1720
|
include: query.include,
|
|
1674
1721
|
items: listed.items,
|
|
@@ -1704,7 +1751,7 @@ export function addresses(options = {}) {
|
|
|
1704
1751
|
* Runs the same `groupByTx` → `classifyGroup` pipeline the address feed uses
|
|
1705
1752
|
* over a single transaction's logs, anchored on the transaction sender as the
|
|
1706
1753
|
* viewer (a transaction-scoped feed has no subject account). Returns the
|
|
1707
|
-
* classified
|
|
1754
|
+
* classified items (one per independent activity) with the full per-tx
|
|
1708
1755
|
* event set available via `logs=true`.
|
|
1709
1756
|
*/
|
|
1710
1757
|
export function transactions(options = {}) {
|
|
@@ -1720,12 +1767,20 @@ export function transactions(options = {}) {
|
|
|
1720
1767
|
operationId: 'getTransactionActivities',
|
|
1721
1768
|
responses: OpenApi.responses({
|
|
1722
1769
|
errors: {
|
|
1770
|
+
400: {
|
|
1771
|
+
codes: [
|
|
1772
|
+
'chain_id_invalid',
|
|
1773
|
+
'chain_id_unsupported',
|
|
1774
|
+
'query_invalid',
|
|
1775
|
+
'transaction_invalid',
|
|
1776
|
+
],
|
|
1777
|
+
},
|
|
1723
1778
|
404: {
|
|
1724
1779
|
description: 'No mined transaction was found for that hash.',
|
|
1725
1780
|
codes: ['transaction_not_found'],
|
|
1726
1781
|
},
|
|
1727
1782
|
409: {
|
|
1728
|
-
description: 'More than one readable
|
|
1783
|
+
description: 'More than one readable chain contains that transaction hash.',
|
|
1729
1784
|
codes: ['transaction_ambiguous'],
|
|
1730
1785
|
},
|
|
1731
1786
|
502: 'Tempo could not read activity or token data from an upstream service.',
|
|
@@ -1770,10 +1825,11 @@ export function transactions(options = {}) {
|
|
|
1770
1825
|
const zoneChainIds = c.get('zoneChainIds');
|
|
1771
1826
|
if (!zoneChainIds)
|
|
1772
1827
|
throw new Error('Inferred Zone selection was not resolved.');
|
|
1828
|
+
const chainIds = [query.chainId ?? c.get('chainId'), ...zoneChainIds];
|
|
1773
1829
|
// Cache only positive resolution. Missing transactions must remain
|
|
1774
|
-
// visible as soon as
|
|
1830
|
+
// visible as soon as any selected indexer catches up.
|
|
1775
1831
|
const match = await Store.memoize(async () => {
|
|
1776
|
-
const probes = await Promise.all(
|
|
1832
|
+
const probes = await Promise.all(chainIds.map(async (chainId) => {
|
|
1777
1833
|
try {
|
|
1778
1834
|
return {
|
|
1779
1835
|
chainId,
|
|
@@ -1790,7 +1846,7 @@ export function transactions(options = {}) {
|
|
|
1790
1846
|
}));
|
|
1791
1847
|
return ZoneSelection.uniqueMatch(probes);
|
|
1792
1848
|
}, {
|
|
1793
|
-
key: `activities:zones:
|
|
1849
|
+
key: `activities:zones:v2:${chainIds.join(',')}:tx:${transactionHash.toLowerCase()}`,
|
|
1794
1850
|
shouldCache: (match) => match.kind === 'selected',
|
|
1795
1851
|
store: c.get('store'),
|
|
1796
1852
|
ttl: Ttl.seconds(15),
|
|
@@ -1810,11 +1866,12 @@ export function transactions(options = {}) {
|
|
|
1810
1866
|
return selected;
|
|
1811
1867
|
const { chainId } = selected;
|
|
1812
1868
|
const unenrichedItems = await Store.memoize(() => selected.receipt === null
|
|
1813
|
-
?
|
|
1869
|
+
? getTransactionActivities_inner({
|
|
1814
1870
|
chainId,
|
|
1815
1871
|
logs: query.logs,
|
|
1816
1872
|
store: c.get('store'),
|
|
1817
1873
|
tidx: c.get('getTidx')(chainId),
|
|
1874
|
+
time: (name, fn) => Timing.time(c, name, fn),
|
|
1818
1875
|
transactionHash,
|
|
1819
1876
|
})
|
|
1820
1877
|
: classifyTransactionActivities({
|
|
@@ -1823,6 +1880,7 @@ export function transactions(options = {}) {
|
|
|
1823
1880
|
receipt: selected.receipt,
|
|
1824
1881
|
store: c.get('store'),
|
|
1825
1882
|
tidx: c.get('getTidx')(chainId),
|
|
1883
|
+
time: (name, fn) => Timing.time(c, name, fn),
|
|
1826
1884
|
transactionHash,
|
|
1827
1885
|
}), {
|
|
1828
1886
|
key: `activities:v1:${chainId}:tx:${transactionHash.toLowerCase()}:${query.logs ?? false}`,
|
|
@@ -1842,7 +1900,7 @@ export function transactions(options = {}) {
|
|
|
1842
1900
|
if (result.kind === 'ambiguous')
|
|
1843
1901
|
return Response.error(c, {
|
|
1844
1902
|
code: 'transaction_ambiguous',
|
|
1845
|
-
message: 'Transaction hash matched more than one readable
|
|
1903
|
+
message: 'Transaction hash matched more than one readable chain',
|
|
1846
1904
|
status: 409,
|
|
1847
1905
|
});
|
|
1848
1906
|
// Mined activity can carry current valuations and verification data.
|
|
@@ -1881,7 +1939,13 @@ export function transactions(options = {}) {
|
|
|
1881
1939
|
* Items carry stub tokens; the caller enriches them via {@link enrichTokens}.
|
|
1882
1940
|
*/
|
|
1883
1941
|
export async function getTransactionActivities(options) {
|
|
1884
|
-
|
|
1942
|
+
return getTransactionActivities_inner({
|
|
1943
|
+
...options,
|
|
1944
|
+
time: async (_name, fn) => fn(),
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
async function getTransactionActivities_inner(options) {
|
|
1948
|
+
const receipt = await options.time('transaction_activity_receipt', () => getTransactionReceipt(options));
|
|
1885
1949
|
if (!receipt)
|
|
1886
1950
|
return null;
|
|
1887
1951
|
return classifyTransactionActivities({ ...options, receipt });
|
|
@@ -1910,13 +1974,15 @@ async function classifyTransactionActivities(options) {
|
|
|
1910
1974
|
const { chainId, logs = false, receipt, tidx: tidxClient } = options;
|
|
1911
1975
|
const hash = options.transactionHash.toLowerCase();
|
|
1912
1976
|
const { blockNum, feePayer, txSender } = receipt;
|
|
1913
|
-
const fingerprintMap_promise =
|
|
1977
|
+
const fingerprintMap_promise = options
|
|
1978
|
+
.time('transaction_activity_mpp', () => Mpp.fingerprintMap({ store: options.store }))
|
|
1979
|
+
.catch(() => ({}));
|
|
1914
1980
|
// `logs` is sorted by block position, so a bare `tx_hash =` scans to genesis;
|
|
1915
1981
|
// pair it with the receipt's block number (see `embedReceipts` in receipts.ts).
|
|
1916
|
-
const logsResult = await tidxClient.fetch({
|
|
1982
|
+
const logsResult = await options.time('transaction_activity_logs', () => tidxClient.fetch({
|
|
1917
1983
|
chainId,
|
|
1918
1984
|
query: `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`,
|
|
1919
|
-
});
|
|
1985
|
+
}));
|
|
1920
1986
|
const rows = logsResult.rows.map((r) => ({
|
|
1921
1987
|
blockNum: Number(Value.toNumber(r['block_num'])),
|
|
1922
1988
|
blockTimestamp: Number(Value.toNumber(r['block_timestamp'])),
|
|
@@ -1993,17 +2059,17 @@ export async function list(provider, options) {
|
|
|
1993
2059
|
// public token references and structured amounts via {@link enrichTokens}.
|
|
1994
2060
|
return { items: allItems, nextCursor };
|
|
1995
2061
|
}
|
|
1996
|
-
const
|
|
2062
|
+
const chainCursorVersion = 2;
|
|
1997
2063
|
/**
|
|
1998
|
-
* Lists address activity across multiple
|
|
1999
|
-
* feed. Its opaque cursor retains each
|
|
2064
|
+
* Lists address activity across multiple chains in one stable timestamp-ordered
|
|
2065
|
+
* feed. Its opaque cursor retains each provider's native continuation.
|
|
2000
2066
|
*/
|
|
2001
|
-
export async function
|
|
2067
|
+
export async function listChains(options) {
|
|
2002
2068
|
const { address, cursor, limit = 20, logs = false, providers, store } = options;
|
|
2003
2069
|
const group = options.group ?? false;
|
|
2004
2070
|
const orderedProviders = [...providers].sort((a, b) => a.chainId - b.chainId);
|
|
2005
2071
|
const chainIds = orderedProviders.map(({ chainId }) => chainId);
|
|
2006
|
-
const cursors =
|
|
2072
|
+
const cursors = decodeChainCursor(cursor, chainIds);
|
|
2007
2073
|
const viewer = address.toLowerCase();
|
|
2008
2074
|
const fingerprintMap_promise = Mpp.fingerprintMap({ store }).catch(() => ({}));
|
|
2009
2075
|
const states = orderedProviders.map(({ chainId, provider }) => ({
|
|
@@ -2076,16 +2142,16 @@ export async function listZones(options) {
|
|
|
2076
2142
|
hasMore = states.some((state) => !state.exhausted || state.groups.length > 0);
|
|
2077
2143
|
return {
|
|
2078
2144
|
items: allItems,
|
|
2079
|
-
nextCursor: hasMore ?
|
|
2145
|
+
nextCursor: hasMore ? encodeChainCursor(cursors, chainIds) : null,
|
|
2080
2146
|
};
|
|
2081
2147
|
}
|
|
2082
|
-
function
|
|
2148
|
+
function decodeChainCursor(cursor, chainIds) {
|
|
2083
2149
|
const fields = ['int'];
|
|
2084
2150
|
for (const _chainId of chainIds)
|
|
2085
2151
|
fields.push('int', 'id');
|
|
2086
2152
|
const decoded = cursor ? Cursor.decode(cursor, fields) : undefined;
|
|
2087
2153
|
const cursors = new Map();
|
|
2088
|
-
if (!decoded || decoded[0] !==
|
|
2154
|
+
if (!decoded || decoded[0] !== chainCursorVersion)
|
|
2089
2155
|
return cursors;
|
|
2090
2156
|
for (let index = 0; index < chainIds.length; index++) {
|
|
2091
2157
|
const chainId = chainIds[index];
|
|
@@ -2098,9 +2164,9 @@ function decodeZoneCursor(cursor, chainIds) {
|
|
|
2098
2164
|
}
|
|
2099
2165
|
return cursors;
|
|
2100
2166
|
}
|
|
2101
|
-
function
|
|
2167
|
+
function encodeChainCursor(cursors, chainIds) {
|
|
2102
2168
|
return Cursor.encode([
|
|
2103
|
-
|
|
2169
|
+
chainCursorVersion,
|
|
2104
2170
|
...chainIds.flatMap((chainId) => [chainId, cursors.get(chainId) ?? 'head']),
|
|
2105
2171
|
]);
|
|
2106
2172
|
}
|
|
@@ -2114,7 +2180,16 @@ function encodeZoneCursor(cursors, chainIds) {
|
|
|
2114
2180
|
* (catches access-key-signed sends where `tx.from` differs from the address).
|
|
2115
2181
|
*/
|
|
2116
2182
|
export function tidx(deps) {
|
|
2117
|
-
const { chainId, tidx: tidxClient } = deps;
|
|
2183
|
+
const { chainId, signal, tidx: tidxClient } = deps;
|
|
2184
|
+
const fetch = (options) => {
|
|
2185
|
+
const requestSignal = signal && options.signal
|
|
2186
|
+
? AbortSignal.any([signal, options.signal])
|
|
2187
|
+
: (signal ?? options.signal);
|
|
2188
|
+
return tidxClient.fetch({
|
|
2189
|
+
...options,
|
|
2190
|
+
...(requestSignal ? { signal: requestSignal } : {}),
|
|
2191
|
+
});
|
|
2192
|
+
};
|
|
2118
2193
|
function parseCursor(cursor) {
|
|
2119
2194
|
// Opaque `[blockNum, txIdx]` keyset cursor, the same encoding every other
|
|
2120
2195
|
// list endpoint uses; a malformed/forged cursor decodes to `undefined` and
|
|
@@ -2134,7 +2209,7 @@ export function tidx(deps) {
|
|
|
2134
2209
|
? `AND block_num <= ${cursor.blockNum} AND (block_num < ${cursor.blockNum} OR tx_idx < ${cursor.txIdx})`
|
|
2135
2210
|
: '';
|
|
2136
2211
|
const queryLimit = limit + 1;
|
|
2137
|
-
const fetchReceipts = (side) =>
|
|
2212
|
+
const fetchReceipts = (side) => fetch({
|
|
2138
2213
|
chainId,
|
|
2139
2214
|
// Raw-table query (no signature-decoded CTE), so the inline SQL is cast
|
|
2140
2215
|
// to `string`.
|
|
@@ -2178,7 +2253,7 @@ export function tidx(deps) {
|
|
|
2178
2253
|
const txHashes = receipts.map((receipt) => `'${receipt.txHash}'`).join(', ');
|
|
2179
2254
|
// `logs` is not keyed by transaction hash, so pair the bounded hash list
|
|
2180
2255
|
// with the selected receipts' block range to avoid a full archive scan.
|
|
2181
|
-
const logs = await
|
|
2256
|
+
const logs = await fetch({
|
|
2182
2257
|
chainId,
|
|
2183
2258
|
// Raw-table query (no signature-decoded CTE), so the inline SQL is cast
|
|
2184
2259
|
// to `string`.
|
|
@@ -2231,7 +2306,7 @@ export function tidx(deps) {
|
|
|
2231
2306
|
`;
|
|
2232
2307
|
const result = await (async () => {
|
|
2233
2308
|
try {
|
|
2234
|
-
return await
|
|
2309
|
+
return await fetch({
|
|
2235
2310
|
chainId,
|
|
2236
2311
|
query: query('block_num'),
|
|
2237
2312
|
signatures: [transferSignature],
|
|
@@ -2242,7 +2317,7 @@ export function tidx(deps) {
|
|
|
2242
2317
|
throw error;
|
|
2243
2318
|
try {
|
|
2244
2319
|
// The expression blocks a mis-selected block-ordered top-N walk.
|
|
2245
|
-
return await
|
|
2320
|
+
return await fetch({
|
|
2246
2321
|
chainId,
|
|
2247
2322
|
query: query('block_num + 0'),
|
|
2248
2323
|
signatures: [transferSignature],
|
|
@@ -2251,7 +2326,7 @@ export function tidx(deps) {
|
|
|
2251
2326
|
catch (fallbackError) {
|
|
2252
2327
|
if (!Tidx.isDeterministicError(fallbackError))
|
|
2253
2328
|
throw fallbackError;
|
|
2254
|
-
const result = await
|
|
2329
|
+
const result = await fetch({
|
|
2255
2330
|
chainId,
|
|
2256
2331
|
query: query(),
|
|
2257
2332
|
signatures: [transferSignature],
|
|
@@ -2309,8 +2384,10 @@ export function mergeAndSort(q1, q2, q3) {
|
|
|
2309
2384
|
const q1Hashes = new Set(q1.map((r) => r.txHash));
|
|
2310
2385
|
const filteredQ2 = q2.filter((r) => !q1Hashes.has(r.txHash));
|
|
2311
2386
|
const filteredQ3 = q3.filter((r) => !q1Hashes.has(r.txHash));
|
|
2312
|
-
|
|
2313
|
-
|
|
2387
|
+
// Dedup by row, not transaction: a tx that both credits and debits the
|
|
2388
|
+
// viewer keeps its rows from both sides, one item each.
|
|
2389
|
+
const q2Keys = new Set(filteredQ2.map((r) => `${r.txHash}:${r.logIdx}`));
|
|
2390
|
+
const dedupedQ3 = filteredQ3.filter((r) => !q2Keys.has(`${r.txHash}:${r.logIdx}`));
|
|
2314
2391
|
return [...q1, ...filteredQ2, ...dedupedQ3].sort((a, b) => {
|
|
2315
2392
|
if (a.blockNum !== b.blockNum)
|
|
2316
2393
|
return b.blockNum - a.blockNum;
|
|
@@ -2375,34 +2452,38 @@ function classifyGroup(group, viewer, fingerprintMap, includeLogs = false) {
|
|
|
2375
2452
|
signer,
|
|
2376
2453
|
viewer,
|
|
2377
2454
|
});
|
|
2378
|
-
// Q2-only
|
|
2379
|
-
//
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2455
|
+
// Q2-only: transfer rows without a joined receipt, one item per row so a
|
|
2456
|
+
// batch paying the viewer several times surfaces every payment. Classify by
|
|
2457
|
+
// comparing `from`/`to` against the viewer (zero-address sides mint/burn).
|
|
2458
|
+
if (q2Rows.length > 0)
|
|
2459
|
+
return [...q2Rows]
|
|
2460
|
+
.sort((a, b) => a.logIdx - b.logIdx)
|
|
2461
|
+
.map((r) => {
|
|
2462
|
+
const base = itemBase(r.logIdx);
|
|
2463
|
+
const from = r.transferFrom;
|
|
2464
|
+
const to = r.transferTo;
|
|
2465
|
+
const sourceToken = token(r.transferAmount, r.transferToken);
|
|
2466
|
+
if (Address.isEqual(from, zeroAddress))
|
|
2467
|
+
return { ...base, data: { recipient: viewer, sourceToken }, type: 'mint' };
|
|
2468
|
+
if (Address.isEqual(to, zeroAddress))
|
|
2469
|
+
return { ...base, data: { sender: from, signer, sourceToken }, type: 'burn' };
|
|
2470
|
+
// Q2-only rows carry no memo, so MPP service attribution is unavailable
|
|
2471
|
+
// here; `attribution` is resolved only on the log-bearing (Q1) path.
|
|
2472
|
+
if (Address.isEqual(from, viewer))
|
|
2473
|
+
return transfer(base, blockNum, {
|
|
2395
2474
|
direction: 'out',
|
|
2396
2475
|
recipient: to,
|
|
2397
2476
|
sender: viewer,
|
|
2398
2477
|
signer,
|
|
2399
2478
|
sourceToken,
|
|
2400
|
-
})
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2479
|
+
});
|
|
2480
|
+
return transfer(base, blockNum, {
|
|
2481
|
+
direction: 'in',
|
|
2482
|
+
recipient: viewer,
|
|
2483
|
+
sender: from,
|
|
2484
|
+
sourceToken,
|
|
2485
|
+
});
|
|
2486
|
+
});
|
|
2406
2487
|
// Fallback — should not happen (a group with neither q1 nor q2 rows).
|
|
2407
2488
|
return [
|
|
2408
2489
|
unknown(itemBase(Math.min(...group.rows.map((r) => r.logIdx))), {
|
|
@@ -2430,7 +2511,7 @@ function classifyFromLogs(rows, ctx) {
|
|
|
2430
2511
|
})();
|
|
2431
2512
|
// When the caller requested raw logs, attach the full per-transaction log set
|
|
2432
2513
|
// (decoded when possible) to every classified item so no co-located log is
|
|
2433
|
-
// hidden
|
|
2514
|
+
// hidden. `unknown` items already carry it.
|
|
2434
2515
|
if (!ctx.includeLogs)
|
|
2435
2516
|
return items;
|
|
2436
2517
|
const events = buildTxEvents(rows, decoded);
|
|
@@ -2447,1214 +2528,1453 @@ function signerForRows(rows, options) {
|
|
|
2447
2528
|
? accessKeySigner
|
|
2448
2529
|
: options.fallback;
|
|
2449
2530
|
}
|
|
2450
|
-
/**
|
|
2531
|
+
/**
|
|
2532
|
+
* Classifies every activity in a Q1 (log-bearing) transaction. Recognizers run
|
|
2533
|
+
* in priority order over a pool of decoded events, each emitting one item per
|
|
2534
|
+
* matched anchor and claiming the exact logs backing it.
|
|
2535
|
+
*/
|
|
2451
2536
|
function classifyFromLogsCore(rows, ctx, events = decodeRows(rows)) {
|
|
2452
2537
|
const { blockNum, fingerprintMap, itemBase, viewer } = ctx;
|
|
2453
2538
|
const signer = signerForRows(rows, { fallback: ctx.signer, viewer });
|
|
2454
2539
|
const minLogIndex = Math.min(...rows.map((r) => r.logIdx));
|
|
2455
|
-
//
|
|
2456
|
-
//
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
'to' in e.args &&
|
|
2462
|
-
Address.isEqual(e.args.to, viewer));
|
|
2463
|
-
const refund = refundTransfer
|
|
2464
|
-
? token(refundTransfer.args.amount, refundTransfer.address)
|
|
2465
|
-
: undefined;
|
|
2466
|
-
return [
|
|
2467
|
-
{
|
|
2468
|
-
...itemBase(closeRequestedLogIndex),
|
|
2469
|
-
data: { ...(refund ? { refund } : {}), signer },
|
|
2470
|
-
type: 'session-closed',
|
|
2471
|
-
},
|
|
2472
|
-
];
|
|
2473
|
-
}
|
|
2474
|
-
// Dedup preference: TransferWithMemo beats Transfer; Mint/Burn beat
|
|
2475
|
-
// zero-address Transfer.
|
|
2476
|
-
const preferenceMap = new Map();
|
|
2540
|
+
// Pair folds are counted: each TransferWithMemo folds one matching plain
|
|
2541
|
+
// Transfer and each Mint/Burn event folds one zero-address transfer, so an
|
|
2542
|
+
// extra identical payment survives as its own item. Keys require the full
|
|
2543
|
+
// event shape; a malformed event must not fold away a classifiable leg.
|
|
2544
|
+
const foldCounts = new Map();
|
|
2545
|
+
const addFold = (key) => foldCounts.set(key, (foldCounts.get(key) ?? 0) + 1);
|
|
2477
2546
|
for (const event of events) {
|
|
2478
2547
|
if (event.eventName === 'TransferWithMemo' && 'from' in event.args && 'to' in event.args) {
|
|
2479
2548
|
const args = event.args;
|
|
2480
|
-
|
|
2549
|
+
addFold(`${event.address}:${args.from}:${args.to}:${args.amount}`);
|
|
2481
2550
|
}
|
|
2482
|
-
if (event.eventName === 'Mint' && 'amount' in event.args)
|
|
2483
|
-
|
|
2484
|
-
if (event.eventName === 'Burn' && 'amount' in event.args)
|
|
2485
|
-
|
|
2551
|
+
if (event.eventName === 'Mint' && 'amount' in event.args && 'to' in event.args)
|
|
2552
|
+
addFold(`mint:${event.address}:${event.args.amount}`);
|
|
2553
|
+
if (event.eventName === 'Burn' && 'amount' in event.args && 'from' in event.args)
|
|
2554
|
+
addFold(`burn:${event.address}:${event.args.amount}`);
|
|
2486
2555
|
}
|
|
2487
|
-
const
|
|
2488
|
-
|
|
2556
|
+
const takeFold = (key) => {
|
|
2557
|
+
const count = foldCounts.get(key) ?? 0;
|
|
2558
|
+
if (count === 0)
|
|
2559
|
+
return false;
|
|
2560
|
+
foldCounts.set(key, count - 1);
|
|
2561
|
+
return true;
|
|
2562
|
+
};
|
|
2563
|
+
const finalEvents = events.filter((event) => {
|
|
2564
|
+
if (event.eventName !== 'Transfer' && event.eventName !== 'TransferWithMemo')
|
|
2565
|
+
return true;
|
|
2566
|
+
if (!('from' in event.args) || !('to' in event.args))
|
|
2489
2567
|
return true;
|
|
2490
2568
|
const args = event.args;
|
|
2491
|
-
if (
|
|
2569
|
+
if (event.eventName === 'Transfer' &&
|
|
2570
|
+
takeFold(`${event.address}:${args.from}:${args.to}:${args.amount}`))
|
|
2492
2571
|
return false;
|
|
2493
2572
|
if (Address.isEqual(args.from, zeroAddress) &&
|
|
2494
|
-
|
|
2573
|
+
takeFold(`mint:${event.address}:${args.amount}`))
|
|
2495
2574
|
return false;
|
|
2496
2575
|
if (Address.isEqual(args.to, zeroAddress) &&
|
|
2497
|
-
|
|
2576
|
+
takeFold(`burn:${event.address}:${args.amount}`))
|
|
2498
2577
|
return false;
|
|
2499
2578
|
return true;
|
|
2500
2579
|
});
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2580
|
+
const pool = [...finalEvents].sort((a, b) => a.logIndex - b.logIndex);
|
|
2581
|
+
const claimed = new Set();
|
|
2582
|
+
const items = [];
|
|
2583
|
+
// Claims made while building an item are journaled, so a build that throws
|
|
2584
|
+
// on a malformed event releases its legs and consumes only the anchor.
|
|
2585
|
+
let journal;
|
|
2586
|
+
const claim = (event) => {
|
|
2587
|
+
claimed.add(event.logIndex);
|
|
2588
|
+
journal?.push(event.logIndex);
|
|
2589
|
+
};
|
|
2590
|
+
// A predicate throwing on a malformed decode must not discard the whole
|
|
2591
|
+
// transaction; treat that event as a non-match instead.
|
|
2592
|
+
const unclaimed = (predicate) => pool.filter((event) => {
|
|
2593
|
+
if (claimed.has(event.logIndex))
|
|
2594
|
+
return false;
|
|
2595
|
+
if (!predicate)
|
|
2503
2596
|
return true;
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2597
|
+
try {
|
|
2598
|
+
return predicate(event);
|
|
2599
|
+
}
|
|
2600
|
+
catch {
|
|
2507
2601
|
return false;
|
|
2508
|
-
|
|
2509
|
-
|
|
2602
|
+
}
|
|
2603
|
+
});
|
|
2604
|
+
const lc = (value) => value.toLowerCase();
|
|
2605
|
+
const hx = (value) => value.toLowerCase();
|
|
2606
|
+
// Non-strict decoding can surface Transfer-shaped events with malformed
|
|
2607
|
+
// args; validating (once per event) keeps predicate comparisons throw-free.
|
|
2608
|
+
const transferIndexes = new Set(pool
|
|
2609
|
+
.filter((event) => {
|
|
2610
|
+
if (event.eventName !== 'Transfer' && event.eventName !== 'TransferWithMemo')
|
|
2510
2611
|
return false;
|
|
2511
|
-
|
|
2612
|
+
const args = event.args;
|
|
2613
|
+
return (typeof args.amount === 'bigint' &&
|
|
2614
|
+
typeof args.from === 'string' &&
|
|
2615
|
+
Address.validate(args.from) &&
|
|
2616
|
+
typeof args.to === 'string' &&
|
|
2617
|
+
Address.validate(args.to));
|
|
2618
|
+
})
|
|
2619
|
+
.map((event) => event.logIndex));
|
|
2620
|
+
const isTransfer = (event) => transferIndexes.has(event.logIndex);
|
|
2621
|
+
const transferArgs = (event) => event.args;
|
|
2622
|
+
// Finds and claims the first unclaimed event matching `predicate`, so a
|
|
2623
|
+
// later identical anchor pairs with the next matching leg instead of
|
|
2624
|
+
// reusing this one.
|
|
2625
|
+
const takeEvent = (predicate) => {
|
|
2626
|
+
for (const event of pool) {
|
|
2627
|
+
if (claimed.has(event.logIndex))
|
|
2628
|
+
continue;
|
|
2629
|
+
const match = (() => {
|
|
2630
|
+
try {
|
|
2631
|
+
return predicate(event);
|
|
2632
|
+
}
|
|
2633
|
+
catch {
|
|
2634
|
+
return false;
|
|
2635
|
+
}
|
|
2636
|
+
})();
|
|
2637
|
+
if (!match)
|
|
2638
|
+
continue;
|
|
2639
|
+
claim(event);
|
|
2640
|
+
return event;
|
|
2641
|
+
}
|
|
2642
|
+
return undefined;
|
|
2643
|
+
};
|
|
2644
|
+
const takeTransfer = (predicate) => takeEvent((event) => isTransfer(event) && predicate(event));
|
|
2645
|
+
// Runs `build` once per unclaimed matching event, in log order; `build`
|
|
2646
|
+
// claims its legs and returns the item(s) to emit, or nothing for
|
|
2647
|
+
// fold-only recognitions. A throwing build consumes only its anchor.
|
|
2648
|
+
const recognize = (predicate, build) => {
|
|
2649
|
+
for (const anchor of unclaimed(predicate)) {
|
|
2650
|
+
// A build may claim a later anchor of the same pass (or a log decoded
|
|
2651
|
+
// into duplicate ABI copies); never process a claimed anchor again.
|
|
2652
|
+
if (claimed.has(anchor.logIndex))
|
|
2653
|
+
continue;
|
|
2654
|
+
claim(anchor);
|
|
2655
|
+
journal = [];
|
|
2656
|
+
try {
|
|
2657
|
+
const built = build(anchor);
|
|
2658
|
+
if (built)
|
|
2659
|
+
items.push(...(Array.isArray(built) ? built : [built]));
|
|
2660
|
+
}
|
|
2661
|
+
catch {
|
|
2662
|
+
// Indexed logs can match a known signature without satisfying that
|
|
2663
|
+
// event's expected shape; release the legs and skip the anchor.
|
|
2664
|
+
for (const index of journal ?? [])
|
|
2665
|
+
claimed.delete(index);
|
|
2666
|
+
}
|
|
2667
|
+
finally {
|
|
2668
|
+
journal = undefined;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
const named = (name) => (event) => event.eventName === name;
|
|
2673
|
+
// Claims and returns a vault operation's token legs, matched by role and
|
|
2674
|
+
// amount: asset legs in or out of the vault, share mint/burn legs on it.
|
|
2675
|
+
const claimVaultLegs = (vault, amounts) => {
|
|
2676
|
+
const legs = [];
|
|
2677
|
+
for (const event of unclaimed()) {
|
|
2678
|
+
const isVaultMintBurn = (event.eventName === 'Mint' || event.eventName === 'Burn') &&
|
|
2679
|
+
Address.isEqual(event.address, vault);
|
|
2680
|
+
const isVaultLeg = (() => {
|
|
2681
|
+
if (!isTransfer(event))
|
|
2682
|
+
return false;
|
|
2683
|
+
const args = transferArgs(event);
|
|
2684
|
+
if (Address.isEqual(args.from, vault) || Address.isEqual(args.to, vault))
|
|
2685
|
+
return true;
|
|
2686
|
+
return (Address.isEqual(event.address, vault) &&
|
|
2687
|
+
(Address.isEqual(args.from, zeroAddress) ||
|
|
2688
|
+
Address.isEqual(args.to, zeroAddress)));
|
|
2689
|
+
})();
|
|
2690
|
+
if (!isVaultMintBurn && !isVaultLeg)
|
|
2691
|
+
continue;
|
|
2692
|
+
const amount = event.args.amount;
|
|
2693
|
+
if (amount === undefined || !amounts.includes(amount))
|
|
2694
|
+
continue;
|
|
2695
|
+
claim(event);
|
|
2696
|
+
legs.push(event);
|
|
2697
|
+
}
|
|
2698
|
+
// The allowance granted for this operation (approve+deposit multicall) is
|
|
2699
|
+
// internal: exact amount, spender is the vault, on a token a leg moved.
|
|
2700
|
+
const legTokens = new Set(legs.map((leg) => leg.address.toLowerCase()));
|
|
2701
|
+
for (const event of unclaimed()) {
|
|
2702
|
+
if (event.eventName !== 'Approval' || !('spender' in event.args))
|
|
2703
|
+
continue;
|
|
2704
|
+
if (!legTokens.has(event.address.toLowerCase()))
|
|
2705
|
+
continue;
|
|
2706
|
+
if (!Address.isEqual(event.args.spender, vault))
|
|
2707
|
+
continue;
|
|
2708
|
+
const amount = event.args.amount;
|
|
2709
|
+
if (amount === undefined || !amounts.includes(amount))
|
|
2710
|
+
continue;
|
|
2711
|
+
claim(event);
|
|
2712
|
+
legs.push(event);
|
|
2713
|
+
}
|
|
2714
|
+
return legs;
|
|
2715
|
+
};
|
|
2716
|
+
// Private Earn router operations. Each wraps a public vault operation on the
|
|
2717
|
+
// vault it names; fold that vault's own event and token legs, leaving vault
|
|
2718
|
+
// activity elsewhere in the batch unclaimed.
|
|
2719
|
+
recognize(named('EarnDeposit'), (event) => {
|
|
2720
|
+
const args = event.args;
|
|
2721
|
+
const vault = lc(args.earnVault);
|
|
2722
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
2723
|
+
const data = {
|
|
2724
|
+
actionId: args.actionId,
|
|
2725
|
+
assets: args.vaultAssets.toString(),
|
|
2726
|
+
inputAmount: args.inputAmount.toString(),
|
|
2727
|
+
inputToken: lc(args.inputToken),
|
|
2728
|
+
shares: args.earnShares.toString(),
|
|
2729
|
+
signer,
|
|
2730
|
+
status: 'completed',
|
|
2731
|
+
vault,
|
|
2732
|
+
zoneDepositHash: args.zoneDepositHash,
|
|
2733
|
+
};
|
|
2734
|
+
// Fold the vault event this router call produced: the nearest preceding
|
|
2735
|
+
// amount match, so a separate same-vault deposit keeps its own.
|
|
2736
|
+
const nested = unclaimed((e) => e.eventName === 'Deposited' &&
|
|
2737
|
+
e.logIndex < event.logIndex &&
|
|
2738
|
+
Address.isEqual(e.address, vault) &&
|
|
2739
|
+
e.args.assets === args.vaultAssets).at(-1);
|
|
2740
|
+
if (nested)
|
|
2741
|
+
claim(nested);
|
|
2742
|
+
else
|
|
2743
|
+
takeEvent((e) => e.eventName === 'Deposited' && Address.isEqual(e.address, vault));
|
|
2744
|
+
claimVaultLegs(vault, [args.inputAmount, args.vaultAssets, args.earnShares]);
|
|
2745
|
+
// The funding leg pays the router (the emitting contract), not the vault.
|
|
2746
|
+
claimVaultLegs(event.address, [args.inputAmount]);
|
|
2747
|
+
return { ...itemBase(event.logIndex), data, type: 'private-assets-deposited' };
|
|
2512
2748
|
});
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2749
|
+
recognize(named('EarnRedeem'), (event) => {
|
|
2750
|
+
const args = event.args;
|
|
2751
|
+
const vault = lc(args.earnVault);
|
|
2752
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
2753
|
+
const data = {
|
|
2754
|
+
actionId: args.actionId,
|
|
2755
|
+
assets: args.vaultAssets.toString(),
|
|
2756
|
+
outputAmount: args.outputAmount.toString(),
|
|
2757
|
+
outputToken: lc(args.outputToken),
|
|
2758
|
+
shares: args.earnShares.toString(),
|
|
2759
|
+
signer,
|
|
2760
|
+
status: 'completed',
|
|
2761
|
+
vault,
|
|
2762
|
+
zoneDepositHash: args.zoneDepositHash,
|
|
2763
|
+
};
|
|
2764
|
+
// Fold the vault event this router call produced: the nearest preceding
|
|
2765
|
+
// amount match, so a separate same-vault redemption keeps its own.
|
|
2766
|
+
const nested = unclaimed((e) => (e.eventName === 'Redeemed' || e.eventName === 'WithdrewExact') &&
|
|
2767
|
+
e.logIndex < event.logIndex &&
|
|
2768
|
+
Address.isEqual(e.address, vault) &&
|
|
2769
|
+
e.args.assets === args.vaultAssets).at(-1);
|
|
2770
|
+
if (nested)
|
|
2771
|
+
claim(nested);
|
|
2772
|
+
else
|
|
2773
|
+
takeEvent((e) => (e.eventName === 'Redeemed' || e.eventName === 'WithdrewExact') &&
|
|
2774
|
+
Address.isEqual(e.address, vault));
|
|
2775
|
+
claimVaultLegs(vault, [args.outputAmount, args.vaultAssets, args.earnShares]);
|
|
2776
|
+
// The payout leg leaves through the router (the emitting contract).
|
|
2777
|
+
claimVaultLegs(event.address, [args.outputAmount]);
|
|
2778
|
+
return { ...itemBase(event.logIndex), data, type: 'private-shares-redeemed' };
|
|
2779
|
+
});
|
|
2780
|
+
// Earn vault operations, each claiming its own token legs.
|
|
2781
|
+
recognize(named('Deposited'), (event) => {
|
|
2782
|
+
const args = event.args;
|
|
2783
|
+
claimVaultLegs(event.address, [args.assets, args.earnShares]);
|
|
2784
|
+
return {
|
|
2785
|
+
...itemBase(event.logIndex),
|
|
2786
|
+
data: {
|
|
2787
|
+
assets: args.assets.toString(),
|
|
2788
|
+
caller: lc(args.caller),
|
|
2789
|
+
receiver: lc(args.receiver),
|
|
2790
|
+
shares: args.earnShares.toString(),
|
|
2791
|
+
signer,
|
|
2792
|
+
status: 'completed',
|
|
2793
|
+
vault: lc(event.address),
|
|
2556
2794
|
},
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
const args =
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2795
|
+
type: 'assets-deposited',
|
|
2796
|
+
};
|
|
2797
|
+
});
|
|
2798
|
+
recognize(named('VenueSharesDeposited'), (event) => {
|
|
2799
|
+
const args = event.args;
|
|
2800
|
+
claimVaultLegs(event.address, [
|
|
2801
|
+
args.earnShares,
|
|
2802
|
+
args.receivedEngineShares,
|
|
2803
|
+
args.requestedVenueShares,
|
|
2804
|
+
]);
|
|
2805
|
+
return {
|
|
2806
|
+
...itemBase(event.logIndex),
|
|
2807
|
+
data: {
|
|
2808
|
+
caller: lc(args.caller),
|
|
2809
|
+
receivedEngineShares: args.receivedEngineShares.toString(),
|
|
2810
|
+
receiver: lc(args.receiver),
|
|
2811
|
+
requestedVenueShares: args.requestedVenueShares.toString(),
|
|
2812
|
+
shares: args.earnShares.toString(),
|
|
2813
|
+
signer,
|
|
2814
|
+
status: 'completed',
|
|
2815
|
+
vault: lc(event.address),
|
|
2575
2816
|
},
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
const args =
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
vault: venueDeposit.address.toLowerCase(),
|
|
2593
|
-
},
|
|
2594
|
-
type: 'shares-deposited',
|
|
2817
|
+
type: 'shares-deposited',
|
|
2818
|
+
};
|
|
2819
|
+
});
|
|
2820
|
+
recognize(named('Redeemed'), (event) => {
|
|
2821
|
+
const args = event.args;
|
|
2822
|
+
claimVaultLegs(event.address, [args.assets, args.earnShares]);
|
|
2823
|
+
return {
|
|
2824
|
+
...itemBase(event.logIndex),
|
|
2825
|
+
data: {
|
|
2826
|
+
assets: args.assets.toString(),
|
|
2827
|
+
caller: lc(args.caller),
|
|
2828
|
+
receiver: lc(args.receiver),
|
|
2829
|
+
shares: args.earnShares.toString(),
|
|
2830
|
+
signer,
|
|
2831
|
+
status: 'completed',
|
|
2832
|
+
vault: lc(event.address),
|
|
2595
2833
|
},
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
const args =
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
},
|
|
2613
|
-
type: 'shares-redeemed',
|
|
2834
|
+
type: 'shares-redeemed',
|
|
2835
|
+
};
|
|
2836
|
+
});
|
|
2837
|
+
recognize(named('WithdrewExact'), (event) => {
|
|
2838
|
+
const args = event.args;
|
|
2839
|
+
claimVaultLegs(event.address, [args.assets, args.earnSharesBurned]);
|
|
2840
|
+
return {
|
|
2841
|
+
...itemBase(event.logIndex),
|
|
2842
|
+
data: {
|
|
2843
|
+
assets: args.assets.toString(),
|
|
2844
|
+
caller: lc(args.caller),
|
|
2845
|
+
receiver: lc(args.receiver),
|
|
2846
|
+
sharesBurned: args.earnSharesBurned.toString(),
|
|
2847
|
+
signer,
|
|
2848
|
+
status: 'completed',
|
|
2849
|
+
vault: lc(event.address),
|
|
2614
2850
|
},
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
const args =
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
},
|
|
2632
|
-
type: 'assets-withdrawn',
|
|
2851
|
+
type: 'assets-withdrawn',
|
|
2852
|
+
};
|
|
2853
|
+
});
|
|
2854
|
+
recognize(named('RedeemRequested'), (event) => {
|
|
2855
|
+
const args = event.args;
|
|
2856
|
+
claimVaultLegs(event.address, [args.earnShares]);
|
|
2857
|
+
return {
|
|
2858
|
+
...itemBase(event.logIndex),
|
|
2859
|
+
data: {
|
|
2860
|
+
receiver: lc(args.receiver),
|
|
2861
|
+
requestId: args.requestId,
|
|
2862
|
+
requester: lc(args.requester),
|
|
2863
|
+
shares: args.earnShares.toString(),
|
|
2864
|
+
signer,
|
|
2865
|
+
status: 'pending',
|
|
2866
|
+
vault: lc(event.address),
|
|
2633
2867
|
},
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
const args =
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
type: 'shares-redemption-requested',
|
|
2868
|
+
type: 'shares-redemption-requested',
|
|
2869
|
+
};
|
|
2870
|
+
});
|
|
2871
|
+
recognize(named('RedeemFinalized'), (event) => {
|
|
2872
|
+
const args = event.args;
|
|
2873
|
+
claimVaultLegs(event.address, [args.assets, args.earnShares]);
|
|
2874
|
+
return {
|
|
2875
|
+
...itemBase(event.logIndex),
|
|
2876
|
+
data: {
|
|
2877
|
+
asset: lc(args.asset),
|
|
2878
|
+
assets: args.assets.toString(),
|
|
2879
|
+
receiver: lc(args.receiver),
|
|
2880
|
+
requestId: args.requestId,
|
|
2881
|
+
shares: args.earnShares.toString(),
|
|
2882
|
+
signer,
|
|
2883
|
+
status: 'completed',
|
|
2884
|
+
vault: lc(event.address),
|
|
2652
2885
|
},
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
const args =
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
status: 'completed',
|
|
2669
|
-
vault: finalized.address.toLowerCase(),
|
|
2670
|
-
},
|
|
2671
|
-
type: 'shares-redemption-finalized',
|
|
2886
|
+
type: 'shares-redemption-finalized',
|
|
2887
|
+
};
|
|
2888
|
+
});
|
|
2889
|
+
recognize(named('RedeemCancelled'), (event) => {
|
|
2890
|
+
const args = event.args;
|
|
2891
|
+
claimVaultLegs(event.address, [args.earnShares]);
|
|
2892
|
+
return {
|
|
2893
|
+
...itemBase(event.logIndex),
|
|
2894
|
+
data: {
|
|
2895
|
+
receiver: lc(args.receiver),
|
|
2896
|
+
requestId: args.requestId,
|
|
2897
|
+
shares: args.earnShares.toString(),
|
|
2898
|
+
signer,
|
|
2899
|
+
status: 'cancelled',
|
|
2900
|
+
vault: lc(event.address),
|
|
2672
2901
|
},
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2902
|
+
type: 'shares-redemption-cancelled',
|
|
2903
|
+
};
|
|
2904
|
+
});
|
|
2905
|
+
// External ERC-4626 vaults. Claim the share and asset legs unconditionally
|
|
2906
|
+
// so nested vault plumbing never resurfaces as burns or transfers, but emit
|
|
2907
|
+
// only operations the viewer is a party to. Fold-only recognitions are
|
|
2908
|
+
// buffered and surface only when nothing else classified.
|
|
2909
|
+
const suppressedVaultItems = [];
|
|
2910
|
+
const viewerInvolved = (parties, legs) => parties.some((party) => Address.isEqual(party, viewer)) ||
|
|
2911
|
+
legs.some((leg) => isTransfer(leg) &&
|
|
2912
|
+
(Address.isEqual(transferArgs(leg).from, viewer) ||
|
|
2913
|
+
Address.isEqual(transferArgs(leg).to, viewer)));
|
|
2914
|
+
recognize(named('Withdraw'), (event) => {
|
|
2915
|
+
const args = event.args;
|
|
2916
|
+
const vault = event.address;
|
|
2917
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
2918
|
+
const data = {
|
|
2919
|
+
assets: args.assets.toString(),
|
|
2920
|
+
caller: lc(args.sender),
|
|
2921
|
+
receiver: lc(args.receiver),
|
|
2922
|
+
sharesBurned: args.shares.toString(),
|
|
2923
|
+
signer,
|
|
2924
|
+
status: 'completed',
|
|
2925
|
+
vault: lc(vault),
|
|
2926
|
+
};
|
|
2927
|
+
const legs = claimVaultLegs(vault, [args.assets, args.shares]);
|
|
2928
|
+
const item = { ...itemBase(event.logIndex), data, type: 'assets-withdrawn' };
|
|
2929
|
+
if (viewerInvolved([args.sender, args.receiver, args.owner], legs))
|
|
2930
|
+
return item;
|
|
2931
|
+
suppressedVaultItems.push(item);
|
|
2932
|
+
return undefined;
|
|
2933
|
+
});
|
|
2934
|
+
recognize(named('Deposit'), (event) => {
|
|
2935
|
+
const args = event.args;
|
|
2936
|
+
const vault = event.address;
|
|
2937
|
+
// Parse before claiming so a malformed event consumes nothing but itself.
|
|
2938
|
+
const data = {
|
|
2939
|
+
assets: args.assets.toString(),
|
|
2940
|
+
caller: lc(args.sender),
|
|
2941
|
+
receiver: lc(args.owner),
|
|
2942
|
+
shares: args.shares.toString(),
|
|
2943
|
+
signer,
|
|
2944
|
+
status: 'completed',
|
|
2945
|
+
vault: lc(vault),
|
|
2946
|
+
};
|
|
2947
|
+
const legs = claimVaultLegs(vault, [args.assets, args.shares]);
|
|
2948
|
+
const item = { ...itemBase(event.logIndex), data, type: 'assets-deposited' };
|
|
2949
|
+
if (viewerInvolved([args.sender, args.owner], legs))
|
|
2950
|
+
return item;
|
|
2951
|
+
suppressedVaultItems.push(item);
|
|
2952
|
+
return undefined;
|
|
2953
|
+
});
|
|
2954
|
+
// DEX order placed: `OrderPlaced` carries the escrow token/amount and side;
|
|
2955
|
+
// claim the escrow transfer that funded it.
|
|
2956
|
+
const orderPlacedItem = (event) => {
|
|
2957
|
+
const args = event.args;
|
|
2958
|
+
const escrow = (leg) => isTransfer(leg) &&
|
|
2959
|
+
Address.isEqual(leg.address, args.token) &&
|
|
2960
|
+
Address.isEqual(transferArgs(leg).to, event.address);
|
|
2961
|
+
// The escrow funds the placement, so it is the nearest preceding match; a
|
|
2962
|
+
// same-token earlier swap input must not be taken instead.
|
|
2963
|
+
const escrowLeg = unclaimed((leg) => escrow(leg) && leg.logIndex < event.logIndex && transferArgs(leg).amount === args.amount).at(-1) ?? unclaimed((leg) => escrow(leg) && leg.logIndex < event.logIndex).at(-1);
|
|
2964
|
+
if (escrowLeg)
|
|
2965
|
+
claim(escrowLeg);
|
|
2966
|
+
return {
|
|
2967
|
+
...itemBase(event.logIndex),
|
|
2968
|
+
data: {
|
|
2969
|
+
orderId: args.orderId.toString(),
|
|
2970
|
+
side: args.isBid ? 'bid' : 'ask',
|
|
2971
|
+
signer,
|
|
2972
|
+
sourceToken: token(args.amount, args.token),
|
|
2973
|
+
tick: Number(args.tick),
|
|
2690
2974
|
},
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
const
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
//
|
|
2703
|
-
//
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2975
|
+
type: 'order-placed',
|
|
2976
|
+
};
|
|
2977
|
+
};
|
|
2978
|
+
const isFlipOrder = (event) => 'isFlipOrder' in event.args && event.args.isFlipOrder === true;
|
|
2979
|
+
// Non-flip order escrow legs claim before swap pairing so they cannot
|
|
2980
|
+
// mispair with an unrelated swap's output leg.
|
|
2981
|
+
recognize((event) => event.eventName === 'OrderPlaced' && !isFlipOrder(event), orderPlacedItem);
|
|
2982
|
+
// Swap: a transfer into a route paired with a different token coming back
|
|
2983
|
+
// out. The DEX matches any swapper; other routes must be viewer-bounded
|
|
2984
|
+
// with route-emitted logs between the legs.
|
|
2985
|
+
{
|
|
2986
|
+
// Outgoing transfers indexed by lowercase sender, for O(candidates)
|
|
2987
|
+
// destination lookups. Claimed entries are skipped at scan time.
|
|
2988
|
+
const transfersByFrom = new Map();
|
|
2989
|
+
for (const event of pool) {
|
|
2990
|
+
if (!isTransfer(event))
|
|
2991
|
+
continue;
|
|
2992
|
+
const key = transferArgs(event).from.toLowerCase();
|
|
2993
|
+
const list = transfersByFrom.get(key);
|
|
2994
|
+
if (list)
|
|
2995
|
+
list.push(event);
|
|
2996
|
+
else
|
|
2997
|
+
transfersByFrom.set(key, [event]);
|
|
2998
|
+
}
|
|
2999
|
+
// One forward pass: claims only shrink the pool, so a source that finds
|
|
3000
|
+
// no destination now never finds one later, and scanning each source once
|
|
3001
|
+
// keeps the cost linear in sources instead of restarting per pair.
|
|
3002
|
+
for (const source of unclaimed(isTransfer)) {
|
|
3003
|
+
if (claimed.has(source.logIndex))
|
|
3004
|
+
continue;
|
|
3005
|
+
const sourceArgs = transferArgs(source);
|
|
3006
|
+
const route = sourceArgs.to;
|
|
3007
|
+
const routeKey = route.toLowerCase();
|
|
3008
|
+
// Tempo system addresses (the reserve, fee manager, outbox, and every
|
|
3009
|
+
// other non-DEX precompile) are never swap routes; new precompiles join
|
|
3010
|
+
// the exclusion automatically.
|
|
3011
|
+
if (nonRouteSystemAddresses.has(routeKey))
|
|
3012
|
+
continue;
|
|
3013
|
+
if (Address.isEqual(route, zeroAddress))
|
|
3014
|
+
continue;
|
|
3015
|
+
if (Address.isEqual(route, viewer))
|
|
3016
|
+
continue;
|
|
3017
|
+
const isDex = Address.isEqual(route, Addresses.stablecoinDex);
|
|
3018
|
+
if (!isDex && !Address.isEqual(sourceArgs.from, viewer))
|
|
3019
|
+
continue;
|
|
3020
|
+
const candidates = (transfersByFrom.get(routeKey) ?? []).filter((event) => {
|
|
3021
|
+
if (claimed.has(event.logIndex) || event.logIndex <= source.logIndex)
|
|
3022
|
+
return false;
|
|
3023
|
+
if (Address.isEqual(event.address, source.address))
|
|
3024
|
+
return false;
|
|
3025
|
+
return !Address.isEqual(transferArgs(event).to, Addresses.feeManager);
|
|
3026
|
+
});
|
|
3027
|
+
// Prefer the output paying the swapper back (so interleaved swaps by
|
|
3028
|
+
// different accounts keep their own outputs); otherwise a DEX pairs
|
|
3029
|
+
// first-in-first-out, and a route's last leg is a direct delivery
|
|
3030
|
+
// (earlier different-token legs are intermediate hops).
|
|
3031
|
+
const destination = candidates.find((event) => Address.isEqual(transferArgs(event).to, sourceArgs.from)) ??
|
|
3032
|
+
(isDex ? candidates[0] : candidates.at(-1));
|
|
3033
|
+
if (!destination)
|
|
3034
|
+
continue;
|
|
3035
|
+
// Route evidence for non-precompile routes: the route itself emitted a
|
|
3036
|
+
// log between the legs (raw rows, so undecoded router events count). A
|
|
3037
|
+
// counterparty that merely received and later paid back is not a route.
|
|
3038
|
+
if (!isDex) {
|
|
3039
|
+
const evidence = rows.some((r) => r.logIdx > source.logIndex &&
|
|
3040
|
+
r.logIdx < destination.logIndex &&
|
|
3041
|
+
r.logAddress.toLowerCase() === routeKey);
|
|
3042
|
+
if (!evidence)
|
|
3043
|
+
continue;
|
|
3044
|
+
}
|
|
3045
|
+
journal = [];
|
|
3046
|
+
try {
|
|
3047
|
+
claim(source);
|
|
3048
|
+
claim(destination);
|
|
3049
|
+
const destinationArgs = transferArgs(destination);
|
|
3050
|
+
const swapper = isDex ? destinationArgs.to : sourceArgs.from;
|
|
3051
|
+
const destTokenAddress = destination.address;
|
|
3052
|
+
const sourceToken = token(sourceArgs.amount, source.address);
|
|
3053
|
+
// Router plumbing between the legs: route-touching or zero-address
|
|
3054
|
+
// token moves and router-side approvals, never viewer-party transfers.
|
|
3055
|
+
// DEX-precompile pairs claim nothing here, so an interleaved swap by
|
|
3056
|
+
// another account keeps its own legs.
|
|
3057
|
+
if (!isDex)
|
|
3058
|
+
for (const event of unclaimed()) {
|
|
3059
|
+
if (event.logIndex <= source.logIndex || event.logIndex >= destination.logIndex)
|
|
3060
|
+
continue;
|
|
3061
|
+
if (isTransfer(event)) {
|
|
3062
|
+
const args = transferArgs(event);
|
|
3063
|
+
if (Address.isEqual(args.from, viewer) || Address.isEqual(args.to, viewer))
|
|
3064
|
+
continue;
|
|
3065
|
+
if (Address.isEqual(args.from, route) ||
|
|
3066
|
+
Address.isEqual(args.to, route) ||
|
|
3067
|
+
Address.isEqual(args.from, zeroAddress) ||
|
|
3068
|
+
Address.isEqual(args.to, zeroAddress))
|
|
3069
|
+
claim(event);
|
|
3070
|
+
}
|
|
3071
|
+
else if (event.eventName === 'Approval' &&
|
|
3072
|
+
'owner' in event.args &&
|
|
3073
|
+
!Address.isEqual(event.args.owner, viewer))
|
|
3074
|
+
claim(event);
|
|
3075
|
+
}
|
|
3076
|
+
// The allowance granted for this swap: same token, owner, and amount,
|
|
3077
|
+
// and causally tied to the input leg (spender is the route, or the
|
|
3078
|
+
// approval immediately precedes it). Unrelated approvals stay.
|
|
3079
|
+
unclaimed((event) => event.eventName === 'Approval' &&
|
|
3080
|
+
'amount' in event.args &&
|
|
3081
|
+
'owner' in event.args &&
|
|
3082
|
+
'spender' in event.args &&
|
|
3083
|
+
Address.isEqual(event.address, source.address) &&
|
|
3084
|
+
Address.isEqual(event.args.owner, sourceArgs.from) &&
|
|
3085
|
+
event.args.amount === sourceArgs.amount &&
|
|
3086
|
+
event.logIndex < source.logIndex &&
|
|
3087
|
+
(Address.isEqual(event.args.spender, route) ||
|
|
3088
|
+
event.logIndex === source.logIndex - 1))
|
|
3089
|
+
.slice(-1)
|
|
3090
|
+
.forEach(claim);
|
|
3091
|
+
// DEX bookkeeping this swap produced: fills against resting orders and
|
|
3092
|
+
// the auto-flip re-placement, acted by the swapper or its route account.
|
|
3093
|
+
// A fill of the viewer's own resting order stays its own item.
|
|
3094
|
+
const isBookkeepingActor = (value) => Address.isEqual(value, swapper) || Address.isEqual(value, route);
|
|
3095
|
+
unclaimed((event) => (Address.isEqual(event.address, route) ||
|
|
3096
|
+
Address.isEqual(event.address, Addresses.stablecoinDex)) &&
|
|
3097
|
+
((event.eventName === 'OrderFilled' &&
|
|
3098
|
+
'taker' in event.args &&
|
|
3099
|
+
isBookkeepingActor(event.args.taker) &&
|
|
3100
|
+
!('maker' in event.args &&
|
|
3101
|
+
Address.isEqual(event.args.maker, viewer))) ||
|
|
3102
|
+
(event.eventName === 'OrderPlaced' &&
|
|
3103
|
+
isFlipOrder(event) &&
|
|
3104
|
+
'maker' in event.args &&
|
|
3105
|
+
isBookkeepingActor(event.args.maker)))).forEach(claim);
|
|
3106
|
+
// Cross-token transfer: the swapped-out token is forwarded whole from
|
|
3107
|
+
// the swapper to a new recipient after the swap (the account server's
|
|
3108
|
+
// approve+swap+transfer multicall), or the route delivered it to the
|
|
3109
|
+
// recipient directly; fold both shapes into one `transfer`. A leg
|
|
3110
|
+
// whose recipient later forwards a different token is the input of a
|
|
3111
|
+
// chained swap, not a delivery.
|
|
3112
|
+
const forwardsOn = (candidate) => {
|
|
3113
|
+
const args = transferArgs(candidate);
|
|
3114
|
+
return (transfersByFrom.get(args.to.toLowerCase()) ?? []).some((onward) => !claimed.has(onward.logIndex) &&
|
|
3115
|
+
onward.logIndex > candidate.logIndex &&
|
|
3116
|
+
!Address.isEqual(onward.address, candidate.address));
|
|
3117
|
+
};
|
|
3118
|
+
const delivery = !isDex && !Address.isEqual(destinationArgs.to, viewer)
|
|
3119
|
+
? destination
|
|
3120
|
+
: takeTransfer((event) => {
|
|
3121
|
+
const args = transferArgs(event);
|
|
3122
|
+
return (event.logIndex > destination.logIndex &&
|
|
3123
|
+
Address.isEqual(event.address, destTokenAddress) &&
|
|
3124
|
+
Address.isEqual(args.from, swapper) &&
|
|
3125
|
+
args.amount === destinationArgs.amount &&
|
|
3126
|
+
!Address.isEqual(args.to, route) &&
|
|
3127
|
+
!Address.isEqual(args.to, swapper) &&
|
|
3128
|
+
!forwardsOn(event));
|
|
3129
|
+
});
|
|
3130
|
+
// A malformed delivery memo degrades to a plain swap item; the claimed
|
|
3131
|
+
// legs must still produce an activity.
|
|
3132
|
+
const item = (() => {
|
|
3133
|
+
if (!delivery)
|
|
3134
|
+
return undefined;
|
|
3135
|
+
try {
|
|
3136
|
+
const args = transferArgs(delivery);
|
|
3137
|
+
const rawMemo = delivery.eventName === 'TransferWithMemo' ? args.memo : undefined;
|
|
3138
|
+
const memo = rawMemo ? Transfers.memoToString(rawMemo) : undefined;
|
|
3139
|
+
const attribution = Mpp.resolve({ fingerprintMap, memo: rawMemo });
|
|
3140
|
+
return transfer(itemBase(delivery.logIndex), blockNum, {
|
|
3141
|
+
attribution,
|
|
3142
|
+
destinationToken: token(args.amount, delivery.address),
|
|
3143
|
+
direction: Address.isEqual(args.to, viewer) ? 'in' : 'out',
|
|
3144
|
+
memo,
|
|
3145
|
+
recipient: lc(args.to),
|
|
3146
|
+
sender: lc(swapper),
|
|
3147
|
+
signer,
|
|
3148
|
+
sourceToken,
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
catch {
|
|
3152
|
+
return undefined;
|
|
3153
|
+
}
|
|
3154
|
+
})();
|
|
3155
|
+
items.push(item ?? {
|
|
3156
|
+
...itemBase(source.logIndex),
|
|
3157
|
+
data: {
|
|
3158
|
+
destinationToken: token(destinationArgs.amount, destTokenAddress),
|
|
3159
|
+
signer,
|
|
3160
|
+
sourceToken,
|
|
3161
|
+
},
|
|
3162
|
+
type: 'swap',
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
catch {
|
|
3166
|
+
// A malformed leg costs only this pair; release its claims so the
|
|
3167
|
+
// legs fall through to the later recognizers.
|
|
3168
|
+
for (const index of journal ?? [])
|
|
3169
|
+
claimed.delete(index);
|
|
3170
|
+
}
|
|
3171
|
+
finally {
|
|
3172
|
+
journal = undefined;
|
|
3173
|
+
}
|
|
2727
3174
|
}
|
|
2728
|
-
return [
|
|
2729
|
-
{
|
|
2730
|
-
...itemBase(Math.min(logIndexOfEvent(toDex), logIndexOfEvent(fromDex))),
|
|
2731
|
-
data: {
|
|
2732
|
-
destinationToken: token(fromDex.args.amount, destTokenAddress),
|
|
2733
|
-
signer,
|
|
2734
|
-
sourceToken,
|
|
2735
|
-
},
|
|
2736
|
-
type: 'swap',
|
|
2737
|
-
},
|
|
2738
|
-
];
|
|
2739
|
-
}
|
|
2740
|
-
// Mint: explicit `Mint` event (not the fee manager) or a transfer from zero.
|
|
2741
|
-
const mintEvent = finalEvents.find((e) => e.eventName === 'Mint' &&
|
|
2742
|
-
'amount' in e.args &&
|
|
2743
|
-
!Address.isEqual(e.address, Addresses.feeManager));
|
|
2744
|
-
if (mintEvent) {
|
|
2745
|
-
const args = mintEvent.args;
|
|
2746
|
-
return [
|
|
2747
|
-
{
|
|
2748
|
-
...itemBase(logIndexOfEvent(mintEvent)),
|
|
2749
|
-
data: {
|
|
2750
|
-
recipient: args.to.toLowerCase(),
|
|
2751
|
-
sourceToken: token(args.amount, mintEvent.address),
|
|
2752
|
-
},
|
|
2753
|
-
type: 'mint',
|
|
2754
|
-
},
|
|
2755
|
-
];
|
|
2756
|
-
}
|
|
2757
|
-
const zeroTransferIn = transfers.find((e) => Address.isEqual(e.args.from, zeroAddress));
|
|
2758
|
-
if (zeroTransferIn) {
|
|
2759
|
-
const args = zeroTransferIn.args;
|
|
2760
|
-
return [
|
|
2761
|
-
{
|
|
2762
|
-
...itemBase(logIndexOfEvent(zeroTransferIn)),
|
|
2763
|
-
data: {
|
|
2764
|
-
recipient: args.to.toLowerCase(),
|
|
2765
|
-
sourceToken: token(args.amount, zeroTransferIn.address),
|
|
2766
|
-
},
|
|
2767
|
-
type: 'mint',
|
|
2768
|
-
},
|
|
2769
|
-
];
|
|
2770
3175
|
}
|
|
3176
|
+
// Mint: explicit `Mint` events (not the fee manager) and remaining transfers
|
|
3177
|
+
// from the zero address (the dedup above already folded event/transfer pairs).
|
|
3178
|
+
const mintItem = (event, args) => ({
|
|
3179
|
+
...itemBase(event.logIndex),
|
|
3180
|
+
data: {
|
|
3181
|
+
recipient: lc(args.to),
|
|
3182
|
+
sourceToken: token(args.amount, event.address),
|
|
3183
|
+
},
|
|
3184
|
+
type: 'mint',
|
|
3185
|
+
});
|
|
3186
|
+
recognize((event) => event.eventName === 'Mint' &&
|
|
3187
|
+
'amount' in event.args &&
|
|
3188
|
+
'to' in event.args &&
|
|
3189
|
+
!Address.isEqual(event.address, Addresses.feeManager), (event) => mintItem(event, event.args));
|
|
3190
|
+
recognize((event) => isTransfer(event) &&
|
|
3191
|
+
Address.isEqual(transferArgs(event).from, zeroAddress), (event) => mintItem(event, transferArgs(event)));
|
|
2771
3192
|
// Burn blocked: a compliance burn of receive-policy-blocked funds. It also
|
|
2772
|
-
// emits a zero-address `Transfer
|
|
2773
|
-
//
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
type: 'burn-blocked',
|
|
2786
|
-
},
|
|
2787
|
-
];
|
|
2788
|
-
}
|
|
2789
|
-
// Burn: explicit `Burn` event or a transfer to zero.
|
|
2790
|
-
const burnEvent = finalEvents.find((e) => e.eventName === 'Burn' && 'amount' in e.args);
|
|
2791
|
-
if (burnEvent) {
|
|
2792
|
-
const args = burnEvent.args;
|
|
2793
|
-
return [
|
|
2794
|
-
{
|
|
2795
|
-
...itemBase(logIndexOfEvent(burnEvent)),
|
|
2796
|
-
data: {
|
|
2797
|
-
sender: args.from.toLowerCase(),
|
|
2798
|
-
signer,
|
|
2799
|
-
sourceToken: token(args.amount, burnEvent.address),
|
|
2800
|
-
},
|
|
2801
|
-
type: 'burn',
|
|
2802
|
-
},
|
|
2803
|
-
];
|
|
2804
|
-
}
|
|
2805
|
-
const zeroTransferOut = transfers.find((e) => Address.isEqual(e.args.to, zeroAddress));
|
|
2806
|
-
if (zeroTransferOut) {
|
|
2807
|
-
const args = zeroTransferOut.args;
|
|
2808
|
-
return [
|
|
2809
|
-
{
|
|
2810
|
-
...itemBase(logIndexOfEvent(zeroTransferOut)),
|
|
2811
|
-
data: {
|
|
2812
|
-
sender: args.from.toLowerCase(),
|
|
2813
|
-
signer,
|
|
2814
|
-
sourceToken: token(args.amount, zeroTransferOut.address),
|
|
2815
|
-
},
|
|
2816
|
-
type: 'burn',
|
|
3193
|
+
// emits a zero-address `Transfer`; claim that paired leg so it cannot
|
|
3194
|
+
// classify as a plain `burn`.
|
|
3195
|
+
recognize(named('BurnBlocked'), (event) => {
|
|
3196
|
+
const args = event.args;
|
|
3197
|
+
takeTransfer((leg) => Address.isEqual(leg.address, event.address) &&
|
|
3198
|
+
Address.isEqual(transferArgs(leg).to, zeroAddress) &&
|
|
3199
|
+
transferArgs(leg).amount === args.amount);
|
|
3200
|
+
return {
|
|
3201
|
+
...itemBase(event.logIndex),
|
|
3202
|
+
data: {
|
|
3203
|
+
sender: lc(args.from),
|
|
3204
|
+
signer,
|
|
3205
|
+
sourceToken: token(args.amount, event.address),
|
|
2817
3206
|
},
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
3207
|
+
type: 'burn-blocked',
|
|
3208
|
+
};
|
|
3209
|
+
});
|
|
3210
|
+
// Burn: explicit `Burn` events and remaining transfers to the zero address.
|
|
3211
|
+
const burnItem = (event, args) => ({
|
|
3212
|
+
...itemBase(event.logIndex),
|
|
3213
|
+
data: {
|
|
3214
|
+
sender: lc(args.from),
|
|
3215
|
+
signer,
|
|
3216
|
+
sourceToken: token(args.amount, event.address),
|
|
3217
|
+
},
|
|
3218
|
+
type: 'burn',
|
|
3219
|
+
});
|
|
3220
|
+
recognize((event) => event.eventName === 'Burn' && 'amount' in event.args && 'from' in event.args, (event) => burnItem(event, event.args));
|
|
3221
|
+
recognize((event) => isTransfer(event) && Address.isEqual(transferArgs(event).to, zeroAddress), (event) => burnItem(event, transferArgs(event)));
|
|
3222
|
+
// Approval (excluding zone-outbox spenders): only viewer-party approvals
|
|
3223
|
+
// surface here; the rest stay for the nothing-else-classified fallback.
|
|
3224
|
+
const isErc20Approval = (event) => {
|
|
3225
|
+
if (event.eventName !== 'Approval')
|
|
3226
|
+
return false;
|
|
3227
|
+
const args = event.args;
|
|
3228
|
+
if (typeof args.spender !== 'string' || !Address.validate(args.spender))
|
|
3229
|
+
return false;
|
|
2822
3230
|
// ERC-721 uses the same selector but indexes its token ID as topic3. In
|
|
2823
3231
|
// non-strict mode viem otherwise decodes the missing ERC-20 amount as zero.
|
|
2824
|
-
rows.some((row) => row.logIdx ===
|
|
3232
|
+
return (rows.some((row) => row.logIdx === event.logIndex &&
|
|
2825
3233
|
row.topic3 === null &&
|
|
2826
|
-
/^0x[0-9a-fA-F]{64}$/.test(row.data ?? '')) &&
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
const args =
|
|
2830
|
-
return
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
spender: args.spender.toLowerCase(),
|
|
2837
|
-
},
|
|
2838
|
-
type: 'approval',
|
|
3234
|
+
/^0x[0-9a-fA-F]{64}$/.test(row.data ?? '')) && !Address.isEqual(args.spender, Addresses.zoneOutbox));
|
|
3235
|
+
};
|
|
3236
|
+
const approvalItem = (event) => {
|
|
3237
|
+
const args = event.args;
|
|
3238
|
+
return {
|
|
3239
|
+
...itemBase(event.logIndex),
|
|
3240
|
+
data: {
|
|
3241
|
+
signer,
|
|
3242
|
+
sourceToken: token(args.amount, event.address),
|
|
3243
|
+
spender: lc(args.spender),
|
|
2839
3244
|
},
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
//
|
|
2848
|
-
//
|
|
2849
|
-
//
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
type: 'channel-opened',
|
|
3245
|
+
type: 'approval',
|
|
3246
|
+
};
|
|
3247
|
+
};
|
|
3248
|
+
recognize((event) => isErc20Approval(event) &&
|
|
3249
|
+
'owner' in event.args &&
|
|
3250
|
+
(Address.isEqual(event.args.owner, viewer) ||
|
|
3251
|
+
Address.isEqual(event.args.spender, viewer)), approvalItem);
|
|
3252
|
+
// Payment channels. The reserve doesn't put the token on `TopUp`/`Settled`/
|
|
3253
|
+
// `ChannelClosed`, so each anchor claims its own reserve leg to recover it;
|
|
3254
|
+
// batched channel operations pair with their own legs one at a time.
|
|
3255
|
+
recognize(named('ChannelOpened'), (event) => {
|
|
3256
|
+
const args = event.args;
|
|
3257
|
+
takeTransfer((leg) => Address.isEqual(leg.address, args.token) &&
|
|
3258
|
+
Address.isEqual(transferArgs(leg).to, tip20ChannelReserve) &&
|
|
3259
|
+
transferArgs(leg).amount === args.deposit);
|
|
3260
|
+
return {
|
|
3261
|
+
...itemBase(event.logIndex),
|
|
3262
|
+
data: {
|
|
3263
|
+
channelId: hx(args.channelId),
|
|
3264
|
+
payee: lc(args.payee),
|
|
3265
|
+
payer: lc(args.payer),
|
|
3266
|
+
signer,
|
|
3267
|
+
sourceToken: token(args.deposit, args.token),
|
|
2864
3268
|
},
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
3269
|
+
type: 'channel-opened',
|
|
3270
|
+
};
|
|
3271
|
+
});
|
|
3272
|
+
recognize(named('TopUp'), (event) => {
|
|
3273
|
+
const args = event.args;
|
|
3274
|
+
const inflow = takeTransfer((leg) => Address.isEqual(transferArgs(leg).to, tip20ChannelReserve) &&
|
|
3275
|
+
transferArgs(leg).amount === args.additionalDeposit) ?? takeTransfer((leg) => Address.isEqual(transferArgs(leg).to, tip20ChannelReserve));
|
|
3276
|
+
// A top-up cancels any pending close request on the channel; fold the
|
|
3277
|
+
// cancellation event it bundles.
|
|
3278
|
+
unclaimed((e) => e.eventName === 'CloseRequestCancelled' &&
|
|
3279
|
+
'channelId' in e.args &&
|
|
3280
|
+
e.args.channelId === args.channelId)
|
|
3281
|
+
.slice(0, 1)
|
|
3282
|
+
.forEach(claim);
|
|
3283
|
+
const tokenAddress = inflow
|
|
3284
|
+
? inflow.address
|
|
2879
3285
|
: zeroAddress;
|
|
2880
|
-
return
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
sourceToken: token(args.additionalDeposit, tokenAddress),
|
|
2887
|
-
},
|
|
2888
|
-
type: 'channel-funded',
|
|
3286
|
+
return {
|
|
3287
|
+
...itemBase(event.logIndex),
|
|
3288
|
+
data: {
|
|
3289
|
+
channelId: hx(args.channelId),
|
|
3290
|
+
signer,
|
|
3291
|
+
sourceToken: token(args.additionalDeposit, tokenAddress),
|
|
2889
3292
|
},
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
3293
|
+
type: 'channel-funded',
|
|
3294
|
+
};
|
|
3295
|
+
});
|
|
3296
|
+
recognize(named('Settled'), (event) => {
|
|
3297
|
+
const args = event.args;
|
|
3298
|
+
const payout = (leg) => Address.isEqual(transferArgs(leg).from, tip20ChannelReserve) &&
|
|
3299
|
+
Address.isEqual(transferArgs(leg).to, args.payee);
|
|
3300
|
+
// Prefer the amount-matched payout so batched channel operations sharing
|
|
3301
|
+
// a payee keep their own legs.
|
|
3302
|
+
const payeeLeg = takeTransfer((leg) => payout(leg) && transferArgs(leg).amount === args.deltaPaid) ??
|
|
3303
|
+
takeTransfer(payout);
|
|
2898
3304
|
const tokenAddress = payeeLeg
|
|
2899
3305
|
? payeeLeg.address
|
|
2900
3306
|
: zeroAddress;
|
|
2901
|
-
return
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
sourceToken: token(args.deltaPaid, tokenAddress),
|
|
2910
|
-
},
|
|
2911
|
-
type: 'channel-settled',
|
|
2912
|
-
},
|
|
2913
|
-
];
|
|
2914
|
-
}
|
|
2915
|
-
// Payment channel closed: payout to the payee and refund to the payer arrive
|
|
2916
|
-
// as up to two transfers out of the reserve; attach whichever are present.
|
|
2917
|
-
const channelClosed = finalEvents.find((e) => e.eventName === 'ChannelClosed');
|
|
2918
|
-
if (channelClosed) {
|
|
2919
|
-
const args = channelClosed.args;
|
|
2920
|
-
const payeeLeg = reserveOutflowTo(args.payee.toLowerCase());
|
|
2921
|
-
const payerLeg = reserveOutflowTo(args.payer.toLowerCase());
|
|
2922
|
-
return [
|
|
2923
|
-
{
|
|
2924
|
-
...itemBase(logIndexOfEvent(channelClosed)),
|
|
2925
|
-
data: {
|
|
2926
|
-
channelId: args.channelId.toLowerCase(),
|
|
2927
|
-
payee: args.payee.toLowerCase(),
|
|
2928
|
-
payer: args.payer.toLowerCase(),
|
|
2929
|
-
...(payerLeg && args.refundedToPayer > 0n
|
|
2930
|
-
? { refund: token(args.refundedToPayer, payerLeg.address) }
|
|
2931
|
-
: {}),
|
|
2932
|
-
signer,
|
|
2933
|
-
...(payeeLeg
|
|
2934
|
-
? { sourceToken: token(args.settledToPayee, payeeLeg.address) }
|
|
2935
|
-
: {}),
|
|
2936
|
-
},
|
|
2937
|
-
type: 'channel-closed',
|
|
3307
|
+
return {
|
|
3308
|
+
...itemBase(event.logIndex),
|
|
3309
|
+
data: {
|
|
3310
|
+
channelId: hx(args.channelId),
|
|
3311
|
+
payee: lc(args.payee),
|
|
3312
|
+
payer: lc(args.payer),
|
|
3313
|
+
signer,
|
|
3314
|
+
sourceToken: token(args.deltaPaid, tokenAddress),
|
|
2938
3315
|
},
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
const
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3316
|
+
type: 'channel-settled',
|
|
3317
|
+
};
|
|
3318
|
+
});
|
|
3319
|
+
recognize(named('ChannelClosed'), (event) => {
|
|
3320
|
+
const args = event.args;
|
|
3321
|
+
// A zero settlement or refund has no leg; claiming one would steal it
|
|
3322
|
+
// from a batched sibling operation.
|
|
3323
|
+
const reserveLegTo = (to, amount) => {
|
|
3324
|
+
if (amount === 0n)
|
|
3325
|
+
return undefined;
|
|
3326
|
+
const payout = (leg) => Address.isEqual(transferArgs(leg).from, tip20ChannelReserve) &&
|
|
3327
|
+
Address.isEqual(transferArgs(leg).to, to);
|
|
3328
|
+
return (takeTransfer((leg) => payout(leg) && transferArgs(leg).amount === amount) ??
|
|
3329
|
+
takeTransfer(payout));
|
|
3330
|
+
};
|
|
3331
|
+
const payeeLeg = reserveLegTo(args.payee, args.settledToPayee);
|
|
3332
|
+
const payerLeg = reserveLegTo(args.payer, args.refundedToPayer);
|
|
3333
|
+
return {
|
|
3334
|
+
...itemBase(event.logIndex),
|
|
3335
|
+
data: {
|
|
3336
|
+
channelId: hx(args.channelId),
|
|
3337
|
+
payee: lc(args.payee),
|
|
3338
|
+
payer: lc(args.payer),
|
|
3339
|
+
...(payerLeg && args.refundedToPayer > 0n
|
|
3340
|
+
? { refund: token(args.refundedToPayer, payerLeg.address) }
|
|
3341
|
+
: {}),
|
|
3342
|
+
signer,
|
|
3343
|
+
...(payeeLeg
|
|
3344
|
+
? { sourceToken: token(args.settledToPayee, payeeLeg.address) }
|
|
3345
|
+
: {}),
|
|
2957
3346
|
},
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
//
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
const args =
|
|
2965
|
-
return
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
sourceToken: token(args.amount, args.token),
|
|
2973
|
-
tick: Number(args.tick),
|
|
2974
|
-
},
|
|
2975
|
-
type: 'order-placed',
|
|
3347
|
+
type: 'channel-closed',
|
|
3348
|
+
};
|
|
3349
|
+
});
|
|
3350
|
+
// Payment channel close cancelled: carries no transfer, so the event itself
|
|
3351
|
+
// is the signal. Cancellations bundled into a top-up were claimed above.
|
|
3352
|
+
recognize(named('CloseRequestCancelled'), (event) => {
|
|
3353
|
+
const args = event.args;
|
|
3354
|
+
return {
|
|
3355
|
+
...itemBase(event.logIndex),
|
|
3356
|
+
data: {
|
|
3357
|
+
channelId: hx(args.channelId),
|
|
3358
|
+
payee: lc(args.payee),
|
|
3359
|
+
payer: lc(args.payer),
|
|
3360
|
+
signer,
|
|
2976
3361
|
},
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
//
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
3362
|
+
type: 'channel-close-cancelled',
|
|
3363
|
+
};
|
|
3364
|
+
});
|
|
3365
|
+
// Payment-channel close requested: matched on the raw selector (the event
|
|
3366
|
+
// is not in the merged ABI). Runs after the channel recognizers claimed
|
|
3367
|
+
// their own reserve legs, then takes the leftover refund back to the viewer.
|
|
3368
|
+
for (const row of rows.filter((r) => r.selector === closeRequestedSelector)) {
|
|
3369
|
+
const refundLeg = takeTransfer((event) => {
|
|
3370
|
+
const args = transferArgs(event);
|
|
3371
|
+
if (!Address.isEqual(args.to, viewer))
|
|
3372
|
+
return false;
|
|
3373
|
+
return (Address.isEqual(args.from, tip20ChannelReserve) ||
|
|
3374
|
+
Address.isEqual(args.from, row.logAddress));
|
|
3375
|
+
});
|
|
3376
|
+
const refund = refundLeg
|
|
3377
|
+
? token(transferArgs(refundLeg).amount, refundLeg.address)
|
|
3378
|
+
: undefined;
|
|
3379
|
+
items.push({
|
|
3380
|
+
...itemBase(row.logIdx),
|
|
3381
|
+
data: { ...(refund ? { refund } : {}), signer },
|
|
3382
|
+
type: 'session-closed',
|
|
3383
|
+
});
|
|
2991
3384
|
}
|
|
2992
|
-
//
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3385
|
+
// Auto-flip order placements not folded into a swap above.
|
|
3386
|
+
recognize(named('OrderPlaced'), orderPlacedItem);
|
|
3387
|
+
// DEX order cancelled: refund is credited to the internal DEX balance, so
|
|
3388
|
+
// there is no co-located transfer; the event itself is the signal.
|
|
3389
|
+
recognize(named('OrderCancelled'), (event) => {
|
|
3390
|
+
const args = event.args;
|
|
3391
|
+
return {
|
|
3392
|
+
...itemBase(event.logIndex),
|
|
3393
|
+
data: { orderId: args.orderId.toString(), signer },
|
|
3394
|
+
type: 'order-cancelled',
|
|
3395
|
+
};
|
|
3396
|
+
});
|
|
3397
|
+
// Fee-AMM rebalance swap: `validatorToken` in, `userToken` out; claim both
|
|
3398
|
+
// fee-manager legs so neither resurfaces as a plain transfer.
|
|
3399
|
+
recognize(named('RebalanceSwap'), (event) => {
|
|
3400
|
+
const args = event.args;
|
|
3401
|
+
takeTransfer((leg) => Address.isEqual(leg.address, args.validatorToken) &&
|
|
3402
|
+
Address.isEqual(transferArgs(leg).from, args.swapper) &&
|
|
3403
|
+
Address.isEqual(transferArgs(leg).to, Addresses.feeManager) &&
|
|
3404
|
+
transferArgs(leg).amount === args.amountIn);
|
|
3405
|
+
takeTransfer((leg) => Address.isEqual(leg.address, args.userToken) &&
|
|
3406
|
+
Address.isEqual(transferArgs(leg).from, Addresses.feeManager) &&
|
|
3407
|
+
Address.isEqual(transferArgs(leg).to, args.swapper) &&
|
|
3408
|
+
transferArgs(leg).amount === args.amountOut);
|
|
3409
|
+
return {
|
|
3410
|
+
...itemBase(event.logIndex),
|
|
3411
|
+
data: {
|
|
3412
|
+
destinationToken: token(args.amountOut, args.userToken),
|
|
3413
|
+
signer,
|
|
3414
|
+
sourceToken: token(args.amountIn, args.validatorToken),
|
|
3415
|
+
swapper: lc(args.swapper),
|
|
3006
3416
|
},
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
const args =
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3417
|
+
type: 'fee-rebalance-swap',
|
|
3418
|
+
};
|
|
3419
|
+
});
|
|
3420
|
+
// Validator fees distributed; claim the fee-manager payout leg.
|
|
3421
|
+
recognize(named('FeesDistributed'), (event) => {
|
|
3422
|
+
const args = event.args;
|
|
3423
|
+
takeTransfer((leg) => Address.isEqual(leg.address, args.token) &&
|
|
3424
|
+
Address.isEqual(transferArgs(leg).from, Addresses.feeManager) &&
|
|
3425
|
+
Address.isEqual(transferArgs(leg).to, args.validator) &&
|
|
3426
|
+
transferArgs(leg).amount === args.amount);
|
|
3427
|
+
return {
|
|
3428
|
+
...itemBase(event.logIndex),
|
|
3429
|
+
data: {
|
|
3430
|
+
signer,
|
|
3431
|
+
sourceToken: token(args.amount, args.token),
|
|
3432
|
+
validator: lc(args.validator),
|
|
3022
3433
|
},
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3434
|
+
type: 'fees-distributed',
|
|
3435
|
+
};
|
|
3436
|
+
});
|
|
3437
|
+
// Reward pool funded: the funded token is the emitting contract itself;
|
|
3438
|
+
// claim the funding transfer.
|
|
3439
|
+
recognize(named('RewardDistributed'), (event) => {
|
|
3440
|
+
const args = event.args;
|
|
3441
|
+
takeTransfer((leg) => Address.isEqual(leg.address, event.address) &&
|
|
3442
|
+
Address.isEqual(transferArgs(leg).from, args.funder) &&
|
|
3443
|
+
Address.isEqual(transferArgs(leg).to, event.address) &&
|
|
3444
|
+
transferArgs(leg).amount === args.amount);
|
|
3445
|
+
return {
|
|
3446
|
+
...itemBase(event.logIndex),
|
|
3447
|
+
data: {
|
|
3448
|
+
funder: lc(args.funder),
|
|
3449
|
+
signer,
|
|
3450
|
+
sourceToken: token(args.amount, event.address),
|
|
3038
3451
|
},
|
|
3039
|
-
|
|
3040
|
-
|
|
3452
|
+
type: 'reward-distributed',
|
|
3453
|
+
};
|
|
3454
|
+
});
|
|
3041
3455
|
// Reward recipient redirected.
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
{
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
recipient: args.recipient.toLowerCase(),
|
|
3051
|
-
signer,
|
|
3052
|
-
},
|
|
3053
|
-
type: 'reward-recipient-set',
|
|
3456
|
+
recognize(named('RewardRecipientSet'), (event) => {
|
|
3457
|
+
const args = event.args;
|
|
3458
|
+
return {
|
|
3459
|
+
...itemBase(event.logIndex),
|
|
3460
|
+
data: {
|
|
3461
|
+
holder: lc(args.holder),
|
|
3462
|
+
recipient: lc(args.recipient),
|
|
3463
|
+
signer,
|
|
3054
3464
|
},
|
|
3055
|
-
|
|
3056
|
-
|
|
3465
|
+
type: 'reward-recipient-set',
|
|
3466
|
+
};
|
|
3467
|
+
});
|
|
3057
3468
|
// Access-key spending limit updated: `newLimit` is carried as the token
|
|
3058
3469
|
// amount so it enriches with the token's metadata.
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
{
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
signer,
|
|
3069
|
-
sourceToken: token(args.newLimit, args.token),
|
|
3070
|
-
},
|
|
3071
|
-
type: 'spending-limit-updated',
|
|
3072
|
-
},
|
|
3073
|
-
];
|
|
3074
|
-
}
|
|
3075
|
-
// Access-key lifecycle events — emitted alongside any co-located transfer.
|
|
3076
|
-
const accessKeyItems = [];
|
|
3077
|
-
for (const event of finalEvents) {
|
|
3078
|
-
if (event.eventName === 'KeyAuthorized' &&
|
|
3079
|
-
Address.isEqual(event.address, Addresses.accountKeychain)) {
|
|
3080
|
-
const args = event.args;
|
|
3081
|
-
accessKeyItems.push({
|
|
3082
|
-
...itemBase(logIndexOfEvent(event)),
|
|
3083
|
-
data: {
|
|
3084
|
-
account: args.account.toLowerCase(),
|
|
3085
|
-
expiry: Number(args.expiry),
|
|
3086
|
-
publicKey: args.publicKey.toLowerCase(),
|
|
3087
|
-
signatureType: Number(args.signatureType),
|
|
3088
|
-
},
|
|
3089
|
-
type: 'access-key-created',
|
|
3090
|
-
});
|
|
3091
|
-
}
|
|
3092
|
-
else if (event.eventName === 'KeyRevoked' &&
|
|
3093
|
-
Address.isEqual(event.address, Addresses.accountKeychain)) {
|
|
3094
|
-
const args = event.args;
|
|
3095
|
-
accessKeyItems.push({
|
|
3096
|
-
...itemBase(logIndexOfEvent(event)),
|
|
3097
|
-
data: {
|
|
3098
|
-
account: args.account.toLowerCase(),
|
|
3099
|
-
publicKey: args.publicKey.toLowerCase(),
|
|
3100
|
-
},
|
|
3101
|
-
type: 'access-key-revoked',
|
|
3102
|
-
});
|
|
3103
|
-
}
|
|
3104
|
-
}
|
|
3105
|
-
// Token creation.
|
|
3106
|
-
const tokenCreated = finalEvents.find((e) => e.eventName === 'TokenCreated' && Address.isEqual(e.address, Addresses.tip20Factory));
|
|
3107
|
-
if (tokenCreated) {
|
|
3108
|
-
const args = tokenCreated.args;
|
|
3109
|
-
return [
|
|
3110
|
-
{
|
|
3111
|
-
...itemBase(logIndexOfEvent(tokenCreated)),
|
|
3112
|
-
data: {
|
|
3113
|
-
address: args.token.toLowerCase(),
|
|
3114
|
-
currency: args.currency,
|
|
3115
|
-
name: args.name,
|
|
3116
|
-
symbol: args.symbol,
|
|
3117
|
-
},
|
|
3118
|
-
type: 'token-created',
|
|
3119
|
-
},
|
|
3120
|
-
...accessKeyItems,
|
|
3121
|
-
];
|
|
3122
|
-
}
|
|
3123
|
-
// Governance / admin / DEX-bookkeeping events. Each maps a single decoded
|
|
3124
|
-
// event to a typed item. Checked after token creation (a token-creation tx
|
|
3125
|
-
// also emits `RoleMembershipUpdated`) and before the plain-transfer fallback;
|
|
3126
|
-
// these carry no token movement, so the event itself is the whole signal.
|
|
3127
|
-
const lc = (value) => value.toLowerCase();
|
|
3128
|
-
const hx = (value) => value.toLowerCase();
|
|
3129
|
-
const findEvent = (name) => finalEvents.find((e) => e.eventName === name);
|
|
3130
|
-
// DEX bookkeeping (taker swaps and `OrderPlaced` already returned above).
|
|
3131
|
-
const orderFilled = findEvent('OrderFilled');
|
|
3132
|
-
if (orderFilled) {
|
|
3133
|
-
const a = orderFilled.args;
|
|
3134
|
-
return [
|
|
3135
|
-
{
|
|
3136
|
-
...itemBase(logIndexOfEvent(orderFilled)),
|
|
3137
|
-
data: {
|
|
3138
|
-
amountFilled: a.amountFilled.toString(),
|
|
3139
|
-
maker: lc(a.maker),
|
|
3140
|
-
orderId: a.orderId.toString(),
|
|
3141
|
-
partialFill: a.partialFill,
|
|
3142
|
-
signer,
|
|
3143
|
-
taker: lc(a.taker),
|
|
3144
|
-
},
|
|
3145
|
-
type: 'order-filled',
|
|
3146
|
-
},
|
|
3147
|
-
];
|
|
3148
|
-
}
|
|
3149
|
-
const orderFlipped = findEvent('OrderFlipped');
|
|
3150
|
-
if (orderFlipped) {
|
|
3151
|
-
const a = orderFlipped.args;
|
|
3152
|
-
return [
|
|
3153
|
-
{
|
|
3154
|
-
...itemBase(logIndexOfEvent(orderFlipped)),
|
|
3155
|
-
data: {
|
|
3156
|
-
flipTick: Number(a.flipTick),
|
|
3157
|
-
maker: lc(a.maker),
|
|
3158
|
-
orderId: a.orderId.toString(),
|
|
3159
|
-
side: a.isBid ? 'bid' : 'ask',
|
|
3160
|
-
signer,
|
|
3161
|
-
sourceToken: token(a.amount, a.token),
|
|
3162
|
-
tick: Number(a.tick),
|
|
3163
|
-
},
|
|
3164
|
-
type: 'order-flipped',
|
|
3165
|
-
},
|
|
3166
|
-
];
|
|
3167
|
-
}
|
|
3168
|
-
const pairCreated = findEvent('PairCreated');
|
|
3169
|
-
if (pairCreated) {
|
|
3170
|
-
const a = pairCreated.args;
|
|
3171
|
-
return [
|
|
3172
|
-
{
|
|
3173
|
-
...itemBase(logIndexOfEvent(pairCreated)),
|
|
3174
|
-
data: { base: lc(a.base), key: hx(a.key), quote: lc(a.quote), signer },
|
|
3175
|
-
type: 'pair-created',
|
|
3176
|
-
},
|
|
3177
|
-
];
|
|
3178
|
-
}
|
|
3179
|
-
// TIP-20 token configuration / admin.
|
|
3180
|
-
const pauseStateUpdate = findEvent('PauseStateUpdate');
|
|
3181
|
-
if (pauseStateUpdate) {
|
|
3182
|
-
const a = pauseStateUpdate.args;
|
|
3183
|
-
return [
|
|
3184
|
-
{
|
|
3185
|
-
...itemBase(logIndexOfEvent(pauseStateUpdate)),
|
|
3186
|
-
data: { paused: a.isPaused, signer, updater: lc(a.updater) },
|
|
3187
|
-
type: 'token-pause-set',
|
|
3470
|
+
recognize(named('SpendingLimitUpdated'), (event) => {
|
|
3471
|
+
const args = event.args;
|
|
3472
|
+
return {
|
|
3473
|
+
...itemBase(event.logIndex),
|
|
3474
|
+
data: {
|
|
3475
|
+
account: lc(args.account),
|
|
3476
|
+
publicKey: lc(args.publicKey),
|
|
3477
|
+
signer,
|
|
3478
|
+
sourceToken: token(args.newLimit, args.token),
|
|
3188
3479
|
},
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3480
|
+
type: 'spending-limit-updated',
|
|
3481
|
+
};
|
|
3482
|
+
});
|
|
3483
|
+
// Access-key lifecycle events.
|
|
3484
|
+
recognize((event) => event.eventName === 'KeyAuthorized' &&
|
|
3485
|
+
Address.isEqual(event.address, Addresses.accountKeychain), (event) => {
|
|
3486
|
+
const args = event.args;
|
|
3487
|
+
return {
|
|
3488
|
+
...itemBase(event.logIndex),
|
|
3489
|
+
data: {
|
|
3490
|
+
account: lc(args.account),
|
|
3491
|
+
expiry: Number(args.expiry),
|
|
3492
|
+
publicKey: lc(args.publicKey),
|
|
3493
|
+
signatureType: Number(args.signatureType),
|
|
3199
3494
|
},
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
const
|
|
3205
|
-
return
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3495
|
+
type: 'access-key-created',
|
|
3496
|
+
};
|
|
3497
|
+
});
|
|
3498
|
+
recognize((event) => event.eventName === 'KeyRevoked' && Address.isEqual(event.address, Addresses.accountKeychain), (event) => {
|
|
3499
|
+
const args = event.args;
|
|
3500
|
+
return {
|
|
3501
|
+
...itemBase(event.logIndex),
|
|
3502
|
+
data: {
|
|
3503
|
+
account: lc(args.account),
|
|
3504
|
+
publicKey: lc(args.publicKey),
|
|
3210
3505
|
},
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3506
|
+
type: 'access-key-revoked',
|
|
3507
|
+
};
|
|
3508
|
+
});
|
|
3509
|
+
// Token creation. Factory initialization (role grants, config) precedes the
|
|
3510
|
+
// creation event; claim those, leaving later admin actions on the token as
|
|
3511
|
+
// independent activities.
|
|
3512
|
+
recognize((event) => event.eventName === 'TokenCreated' && Address.isEqual(event.address, Addresses.tip20Factory), (event) => {
|
|
3513
|
+
const args = event.args;
|
|
3514
|
+
unclaimed((e) => e.logIndex < event.logIndex && Address.isEqual(e.address, args.token)).forEach(claim);
|
|
3515
|
+
return {
|
|
3516
|
+
...itemBase(event.logIndex),
|
|
3517
|
+
data: {
|
|
3518
|
+
address: lc(args.token),
|
|
3519
|
+
currency: args.currency,
|
|
3520
|
+
name: args.name,
|
|
3521
|
+
symbol: args.symbol,
|
|
3221
3522
|
},
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3523
|
+
type: 'token-created',
|
|
3524
|
+
};
|
|
3525
|
+
});
|
|
3526
|
+
// Governance / admin / DEX-bookkeeping events. Each maps a single decoded
|
|
3527
|
+
// event to a typed item; every remaining occurrence emits. These carry no
|
|
3528
|
+
// token movement, so the event itself is the whole signal.
|
|
3529
|
+
recognize(named('OrderFilled'), (event) => {
|
|
3530
|
+
const a = event.args;
|
|
3531
|
+
return {
|
|
3532
|
+
...itemBase(event.logIndex),
|
|
3533
|
+
data: {
|
|
3534
|
+
amountFilled: a.amountFilled.toString(),
|
|
3535
|
+
maker: lc(a.maker),
|
|
3536
|
+
orderId: a.orderId.toString(),
|
|
3537
|
+
partialFill: a.partialFill,
|
|
3538
|
+
signer,
|
|
3539
|
+
taker: lc(a.taker),
|
|
3232
3540
|
},
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
const a =
|
|
3238
|
-
return
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3541
|
+
type: 'order-filled',
|
|
3542
|
+
};
|
|
3543
|
+
});
|
|
3544
|
+
recognize(named('OrderFlipped'), (event) => {
|
|
3545
|
+
const a = event.args;
|
|
3546
|
+
return {
|
|
3547
|
+
...itemBase(event.logIndex),
|
|
3548
|
+
data: {
|
|
3549
|
+
flipTick: Number(a.flipTick),
|
|
3550
|
+
maker: lc(a.maker),
|
|
3551
|
+
orderId: a.orderId.toString(),
|
|
3552
|
+
side: a.isBid ? 'bid' : 'ask',
|
|
3553
|
+
signer,
|
|
3554
|
+
sourceToken: token(a.amount, a.token),
|
|
3555
|
+
tick: Number(a.tick),
|
|
3243
3556
|
},
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
const a =
|
|
3249
|
-
return
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3557
|
+
type: 'order-flipped',
|
|
3558
|
+
};
|
|
3559
|
+
});
|
|
3560
|
+
recognize(named('PairCreated'), (event) => {
|
|
3561
|
+
const a = event.args;
|
|
3562
|
+
return {
|
|
3563
|
+
...itemBase(event.logIndex),
|
|
3564
|
+
data: { base: lc(a.base), key: hx(a.key), quote: lc(a.quote), signer },
|
|
3565
|
+
type: 'pair-created',
|
|
3566
|
+
};
|
|
3567
|
+
});
|
|
3568
|
+
// TIP-20 token configuration / admin.
|
|
3569
|
+
recognize(named('PauseStateUpdate'), (event) => {
|
|
3570
|
+
const a = event.args;
|
|
3571
|
+
return {
|
|
3572
|
+
...itemBase(event.logIndex),
|
|
3573
|
+
data: { paused: a.isPaused, signer, updater: lc(a.updater) },
|
|
3574
|
+
type: 'token-pause-set',
|
|
3575
|
+
};
|
|
3576
|
+
});
|
|
3577
|
+
recognize(named('SupplyCapUpdate'), (event) => {
|
|
3578
|
+
const a = event.args;
|
|
3579
|
+
return {
|
|
3580
|
+
...itemBase(event.logIndex),
|
|
3581
|
+
data: { signer, supplyCap: a.newSupplyCap.toString(), updater: lc(a.updater) },
|
|
3582
|
+
type: 'token-supply-cap-set',
|
|
3583
|
+
};
|
|
3584
|
+
});
|
|
3585
|
+
recognize(named('TransferPolicyUpdate'), (event) => {
|
|
3586
|
+
const a = event.args;
|
|
3587
|
+
return {
|
|
3588
|
+
...itemBase(event.logIndex),
|
|
3589
|
+
data: { policyId: a.newPolicyId.toString(), signer, updater: lc(a.updater) },
|
|
3590
|
+
type: 'token-transfer-policy-set',
|
|
3591
|
+
};
|
|
3592
|
+
});
|
|
3593
|
+
recognize(named('QuoteTokenUpdate'), (event) => {
|
|
3594
|
+
const a = event.args;
|
|
3595
|
+
return {
|
|
3596
|
+
...itemBase(event.logIndex),
|
|
3597
|
+
data: { quoteToken: lc(a.newQuoteToken), signer, updater: lc(a.updater) },
|
|
3598
|
+
type: 'token-quote-token-set',
|
|
3599
|
+
};
|
|
3600
|
+
});
|
|
3601
|
+
recognize(named('NextQuoteTokenSet'), (event) => {
|
|
3602
|
+
const a = event.args;
|
|
3603
|
+
return {
|
|
3604
|
+
...itemBase(event.logIndex),
|
|
3605
|
+
data: { nextQuoteToken: lc(a.nextQuoteToken), signer, updater: lc(a.updater) },
|
|
3606
|
+
type: 'token-next-quote-token-set',
|
|
3607
|
+
};
|
|
3608
|
+
});
|
|
3609
|
+
recognize(named('LogoURIUpdated'), (event) => {
|
|
3610
|
+
const a = event.args;
|
|
3611
|
+
return {
|
|
3612
|
+
...itemBase(event.logIndex),
|
|
3613
|
+
data: { logoUri: a.newLogoURI, signer, updater: lc(a.updater) },
|
|
3614
|
+
type: 'token-logo-set',
|
|
3615
|
+
};
|
|
3616
|
+
});
|
|
3617
|
+
recognize(named('RoleMembershipUpdated'), (event) => {
|
|
3618
|
+
const a = event.args;
|
|
3619
|
+
return {
|
|
3620
|
+
...itemBase(event.logIndex),
|
|
3621
|
+
data: {
|
|
3622
|
+
account: lc(a.account),
|
|
3623
|
+
granted: a.hasRole,
|
|
3624
|
+
role: hx(a.role),
|
|
3625
|
+
sender: lc(a.sender),
|
|
3626
|
+
signer,
|
|
3260
3627
|
},
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const a =
|
|
3266
|
-
return
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
signer,
|
|
3274
|
-
},
|
|
3275
|
-
type: 'role-admin-set',
|
|
3628
|
+
type: 'role-membership-set',
|
|
3629
|
+
};
|
|
3630
|
+
});
|
|
3631
|
+
recognize(named('RoleAdminUpdated'), (event) => {
|
|
3632
|
+
const a = event.args;
|
|
3633
|
+
return {
|
|
3634
|
+
...itemBase(event.logIndex),
|
|
3635
|
+
data: {
|
|
3636
|
+
newAdminRole: hx(a.newAdminRole),
|
|
3637
|
+
role: hx(a.role),
|
|
3638
|
+
sender: lc(a.sender),
|
|
3639
|
+
signer,
|
|
3276
3640
|
},
|
|
3277
|
-
|
|
3278
|
-
|
|
3641
|
+
type: 'role-admin-set',
|
|
3642
|
+
};
|
|
3643
|
+
});
|
|
3279
3644
|
// Fee-manager token configuration.
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
{
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
];
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
return
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
signer,
|
|
3316
|
-
},
|
|
3317
|
-
type: 'compound-policy-created',
|
|
3645
|
+
recognize(named('UserTokenSet'), (event) => {
|
|
3646
|
+
const a = event.args;
|
|
3647
|
+
return {
|
|
3648
|
+
...itemBase(event.logIndex),
|
|
3649
|
+
data: { signer, token: lc(a.token), user: lc(a.user) },
|
|
3650
|
+
type: 'fee-user-token-set',
|
|
3651
|
+
};
|
|
3652
|
+
});
|
|
3653
|
+
recognize(named('ValidatorTokenSet'), (event) => {
|
|
3654
|
+
const a = event.args;
|
|
3655
|
+
return {
|
|
3656
|
+
...itemBase(event.logIndex),
|
|
3657
|
+
data: { signer, token: lc(a.token), validator: lc(a.validator) },
|
|
3658
|
+
type: 'fee-validator-token-set',
|
|
3659
|
+
};
|
|
3660
|
+
});
|
|
3661
|
+
// TIP-403 policy registry. Creation events claim the member events they
|
|
3662
|
+
// bundle (which precede the anchor); later member updates stay independent.
|
|
3663
|
+
recognize(named('CompoundPolicyCreated'), (event) => {
|
|
3664
|
+
const a = event.args;
|
|
3665
|
+
const componentIds = new Set([a.mintRecipientPolicyId, a.recipientPolicyId, a.senderPolicyId].map(String));
|
|
3666
|
+
unclaimed((e) => e.logIndex < event.logIndex &&
|
|
3667
|
+
Address.isEqual(e.address, event.address) &&
|
|
3668
|
+
['BlacklistUpdated', 'PolicyAdminUpdated', 'PolicyCreated', 'WhitelistUpdated'].includes(e.eventName) &&
|
|
3669
|
+
'policyId' in e.args &&
|
|
3670
|
+
componentIds.has(String(e.args.policyId))).forEach(claim);
|
|
3671
|
+
return {
|
|
3672
|
+
...itemBase(event.logIndex),
|
|
3673
|
+
data: {
|
|
3674
|
+
creator: lc(a.creator),
|
|
3675
|
+
mintRecipientPolicyId: a.mintRecipientPolicyId.toString(),
|
|
3676
|
+
policyId: a.policyId.toString(),
|
|
3677
|
+
recipientPolicyId: a.recipientPolicyId.toString(),
|
|
3678
|
+
senderPolicyId: a.senderPolicyId.toString(),
|
|
3679
|
+
signer,
|
|
3318
3680
|
},
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
const a =
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3681
|
+
type: 'compound-policy-created',
|
|
3682
|
+
};
|
|
3683
|
+
});
|
|
3684
|
+
recognize(named('PolicyCreated'), (event) => {
|
|
3685
|
+
const a = event.args;
|
|
3686
|
+
unclaimed((e) => e.logIndex < event.logIndex &&
|
|
3687
|
+
Address.isEqual(e.address, event.address) &&
|
|
3688
|
+
['BlacklistUpdated', 'PolicyAdminUpdated', 'WhitelistUpdated'].includes(e.eventName) &&
|
|
3689
|
+
'policyId' in e.args &&
|
|
3690
|
+
e.args.policyId === a.policyId).forEach(claim);
|
|
3691
|
+
return {
|
|
3692
|
+
...itemBase(event.logIndex),
|
|
3693
|
+
data: {
|
|
3694
|
+
policyId: a.policyId.toString(),
|
|
3695
|
+
policyType: Number(a.policyType),
|
|
3696
|
+
signer,
|
|
3697
|
+
updater: lc(a.updater),
|
|
3334
3698
|
},
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
const a =
|
|
3340
|
-
return
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
updater: lc(a.updater),
|
|
3348
|
-
},
|
|
3349
|
-
type: 'policy-admin-set',
|
|
3699
|
+
type: 'policy-created',
|
|
3700
|
+
};
|
|
3701
|
+
});
|
|
3702
|
+
recognize(named('PolicyAdminUpdated'), (event) => {
|
|
3703
|
+
const a = event.args;
|
|
3704
|
+
return {
|
|
3705
|
+
...itemBase(event.logIndex),
|
|
3706
|
+
data: {
|
|
3707
|
+
admin: lc(a.admin),
|
|
3708
|
+
policyId: a.policyId.toString(),
|
|
3709
|
+
signer,
|
|
3710
|
+
updater: lc(a.updater),
|
|
3350
3711
|
},
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
const a =
|
|
3356
|
-
return
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
updater: lc(a.updater),
|
|
3365
|
-
},
|
|
3366
|
-
type: 'whitelist-updated',
|
|
3712
|
+
type: 'policy-admin-set',
|
|
3713
|
+
};
|
|
3714
|
+
});
|
|
3715
|
+
recognize(named('WhitelistUpdated'), (event) => {
|
|
3716
|
+
const a = event.args;
|
|
3717
|
+
return {
|
|
3718
|
+
...itemBase(event.logIndex),
|
|
3719
|
+
data: {
|
|
3720
|
+
account: lc(a.account),
|
|
3721
|
+
allowed: a.allowed,
|
|
3722
|
+
policyId: a.policyId.toString(),
|
|
3723
|
+
signer,
|
|
3724
|
+
updater: lc(a.updater),
|
|
3367
3725
|
},
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
const a =
|
|
3373
|
-
return
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
updater: lc(a.updater),
|
|
3382
|
-
},
|
|
3383
|
-
type: 'blacklist-updated',
|
|
3726
|
+
type: 'whitelist-updated',
|
|
3727
|
+
};
|
|
3728
|
+
});
|
|
3729
|
+
recognize(named('BlacklistUpdated'), (event) => {
|
|
3730
|
+
const a = event.args;
|
|
3731
|
+
return {
|
|
3732
|
+
...itemBase(event.logIndex),
|
|
3733
|
+
data: {
|
|
3734
|
+
account: lc(a.account),
|
|
3735
|
+
policyId: a.policyId.toString(),
|
|
3736
|
+
restricted: a.restricted,
|
|
3737
|
+
signer,
|
|
3738
|
+
updater: lc(a.updater),
|
|
3384
3739
|
},
|
|
3385
|
-
|
|
3386
|
-
|
|
3740
|
+
type: 'blacklist-updated',
|
|
3741
|
+
};
|
|
3742
|
+
});
|
|
3387
3743
|
// Address / nonce registry.
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
{
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
data: {
|
|
3406
|
-
account: lc(a.account),
|
|
3407
|
-
nonce: a.newNonce.toString(),
|
|
3408
|
-
nonceKey: a.nonceKey.toString(),
|
|
3409
|
-
signer,
|
|
3410
|
-
},
|
|
3411
|
-
type: 'nonce-incremented',
|
|
3744
|
+
recognize(named('MasterRegistered'), (event) => {
|
|
3745
|
+
const a = event.args;
|
|
3746
|
+
return {
|
|
3747
|
+
...itemBase(event.logIndex),
|
|
3748
|
+
data: { masterAddress: lc(a.masterAddress), masterId: hx(a.masterId), signer },
|
|
3749
|
+
type: 'master-registered',
|
|
3750
|
+
};
|
|
3751
|
+
});
|
|
3752
|
+
recognize(named('NonceIncremented'), (event) => {
|
|
3753
|
+
const a = event.args;
|
|
3754
|
+
return {
|
|
3755
|
+
...itemBase(event.logIndex),
|
|
3756
|
+
data: {
|
|
3757
|
+
account: lc(a.account),
|
|
3758
|
+
nonce: a.newNonce.toString(),
|
|
3759
|
+
nonceKey: a.nonceKey.toString(),
|
|
3760
|
+
signer,
|
|
3412
3761
|
},
|
|
3413
|
-
|
|
3414
|
-
|
|
3762
|
+
type: 'nonce-incremented',
|
|
3763
|
+
};
|
|
3764
|
+
});
|
|
3415
3765
|
// Access-key authorization witnesses.
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
{
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
},
|
|
3456
|
-
type: 'validator-rotated',
|
|
3457
|
-
},
|
|
3458
|
-
];
|
|
3459
|
-
}
|
|
3460
|
-
const validatorAdded = findEvent('ValidatorAdded');
|
|
3461
|
-
if (validatorAdded) {
|
|
3462
|
-
const a = validatorAdded.args;
|
|
3463
|
-
return [
|
|
3464
|
-
{
|
|
3465
|
-
...itemBase(logIndexOfEvent(validatorAdded)),
|
|
3466
|
-
data: {
|
|
3467
|
-
egress: a.egress,
|
|
3468
|
-
feeRecipient: lc(a.feeRecipient),
|
|
3469
|
-
index: a.index.toString(),
|
|
3470
|
-
ingress: a.ingress,
|
|
3471
|
-
publicKey: hx(a.publicKey),
|
|
3472
|
-
signer,
|
|
3473
|
-
validator: lc(a.validatorAddress),
|
|
3474
|
-
},
|
|
3475
|
-
type: 'validator-added',
|
|
3476
|
-
},
|
|
3477
|
-
];
|
|
3478
|
-
}
|
|
3479
|
-
const validatorDeactivated = findEvent('ValidatorDeactivated');
|
|
3480
|
-
if (validatorDeactivated) {
|
|
3481
|
-
const a = validatorDeactivated.args;
|
|
3482
|
-
return [
|
|
3483
|
-
{
|
|
3484
|
-
...itemBase(logIndexOfEvent(validatorDeactivated)),
|
|
3485
|
-
data: { index: a.index.toString(), signer, validator: lc(a.validatorAddress) },
|
|
3486
|
-
type: 'validator-deactivated',
|
|
3487
|
-
},
|
|
3488
|
-
];
|
|
3489
|
-
}
|
|
3490
|
-
const validatorMigrated = findEvent('ValidatorMigrated');
|
|
3491
|
-
if (validatorMigrated) {
|
|
3492
|
-
const a = validatorMigrated.args;
|
|
3493
|
-
return [
|
|
3494
|
-
{
|
|
3495
|
-
...itemBase(logIndexOfEvent(validatorMigrated)),
|
|
3496
|
-
data: {
|
|
3497
|
-
index: a.index.toString(),
|
|
3498
|
-
publicKey: hx(a.publicKey),
|
|
3499
|
-
signer,
|
|
3500
|
-
validator: lc(a.validatorAddress),
|
|
3501
|
-
},
|
|
3502
|
-
type: 'validator-migrated',
|
|
3766
|
+
recognize(named('KeyAuthorizationWitness'), (event) => {
|
|
3767
|
+
const a = event.args;
|
|
3768
|
+
return {
|
|
3769
|
+
...itemBase(event.logIndex),
|
|
3770
|
+
data: { account: lc(a.account), signer, witness: hx(a.witness) },
|
|
3771
|
+
type: 'key-authorization-witness',
|
|
3772
|
+
};
|
|
3773
|
+
});
|
|
3774
|
+
recognize(named('KeyAuthorizationWitnessBurned'), (event) => {
|
|
3775
|
+
const a = event.args;
|
|
3776
|
+
return {
|
|
3777
|
+
...itemBase(event.logIndex),
|
|
3778
|
+
data: { account: lc(a.account), signer, witness: hx(a.witness) },
|
|
3779
|
+
type: 'key-authorization-witness-burned',
|
|
3780
|
+
};
|
|
3781
|
+
});
|
|
3782
|
+
// Validator configuration. Rotation claims the add/deactivate pair it
|
|
3783
|
+
// bundles, matched by validator index.
|
|
3784
|
+
recognize(named('ValidatorRotated'), (event) => {
|
|
3785
|
+
const a = event.args;
|
|
3786
|
+
unclaimed((e) => {
|
|
3787
|
+
if (!('index' in e.args))
|
|
3788
|
+
return false;
|
|
3789
|
+
const index = e.args.index;
|
|
3790
|
+
return ((e.eventName === 'ValidatorAdded' && index === a.index) ||
|
|
3791
|
+
(e.eventName === 'ValidatorDeactivated' && index === a.deactivatedIndex));
|
|
3792
|
+
}).forEach(claim);
|
|
3793
|
+
return {
|
|
3794
|
+
...itemBase(event.logIndex),
|
|
3795
|
+
data: {
|
|
3796
|
+
caller: lc(a.caller),
|
|
3797
|
+
deactivatedIndex: a.deactivatedIndex.toString(),
|
|
3798
|
+
egress: a.egress,
|
|
3799
|
+
index: a.index.toString(),
|
|
3800
|
+
ingress: a.ingress,
|
|
3801
|
+
newPublicKey: hx(a.newPublicKey),
|
|
3802
|
+
oldPublicKey: hx(a.oldPublicKey),
|
|
3803
|
+
signer,
|
|
3804
|
+
validator: lc(a.validatorAddress),
|
|
3503
3805
|
},
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
const a =
|
|
3509
|
-
return
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3806
|
+
type: 'validator-rotated',
|
|
3807
|
+
};
|
|
3808
|
+
});
|
|
3809
|
+
recognize(named('ValidatorAdded'), (event) => {
|
|
3810
|
+
const a = event.args;
|
|
3811
|
+
return {
|
|
3812
|
+
...itemBase(event.logIndex),
|
|
3813
|
+
data: {
|
|
3814
|
+
egress: a.egress,
|
|
3815
|
+
feeRecipient: lc(a.feeRecipient),
|
|
3816
|
+
index: a.index.toString(),
|
|
3817
|
+
ingress: a.ingress,
|
|
3818
|
+
publicKey: hx(a.publicKey),
|
|
3819
|
+
signer,
|
|
3820
|
+
validator: lc(a.validatorAddress),
|
|
3519
3821
|
},
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
const a =
|
|
3525
|
-
return
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3822
|
+
type: 'validator-added',
|
|
3823
|
+
};
|
|
3824
|
+
});
|
|
3825
|
+
recognize(named('ValidatorDeactivated'), (event) => {
|
|
3826
|
+
const a = event.args;
|
|
3827
|
+
return {
|
|
3828
|
+
...itemBase(event.logIndex),
|
|
3829
|
+
data: { index: a.index.toString(), signer, validator: lc(a.validatorAddress) },
|
|
3830
|
+
type: 'validator-deactivated',
|
|
3831
|
+
};
|
|
3832
|
+
});
|
|
3833
|
+
recognize(named('ValidatorMigrated'), (event) => {
|
|
3834
|
+
const a = event.args;
|
|
3835
|
+
return {
|
|
3836
|
+
...itemBase(event.logIndex),
|
|
3837
|
+
data: {
|
|
3838
|
+
index: a.index.toString(),
|
|
3839
|
+
publicKey: hx(a.publicKey),
|
|
3840
|
+
signer,
|
|
3841
|
+
validator: lc(a.validatorAddress),
|
|
3535
3842
|
},
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
const a =
|
|
3541
|
-
return
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
ingress: a.ingress,
|
|
3549
|
-
signer,
|
|
3550
|
-
},
|
|
3551
|
-
type: 'validator-ip-set',
|
|
3843
|
+
type: 'validator-migrated',
|
|
3844
|
+
};
|
|
3845
|
+
});
|
|
3846
|
+
recognize(named('SkippedValidatorMigration'), (event) => {
|
|
3847
|
+
const a = event.args;
|
|
3848
|
+
return {
|
|
3849
|
+
...itemBase(event.logIndex),
|
|
3850
|
+
data: {
|
|
3851
|
+
index: a.index.toString(),
|
|
3852
|
+
publicKey: hx(a.publicKey),
|
|
3853
|
+
signer,
|
|
3854
|
+
validator: lc(a.validatorAddress),
|
|
3552
3855
|
},
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
const a =
|
|
3558
|
-
return
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
oldAddress: lc(a.oldAddress),
|
|
3566
|
-
signer,
|
|
3567
|
-
},
|
|
3568
|
-
type: 'validator-ownership-transferred',
|
|
3856
|
+
type: 'validator-migration-skipped',
|
|
3857
|
+
};
|
|
3858
|
+
});
|
|
3859
|
+
recognize(named('FeeRecipientUpdated'), (event) => {
|
|
3860
|
+
const a = event.args;
|
|
3861
|
+
return {
|
|
3862
|
+
...itemBase(event.logIndex),
|
|
3863
|
+
data: {
|
|
3864
|
+
caller: lc(a.caller),
|
|
3865
|
+
feeRecipient: lc(a.feeRecipient),
|
|
3866
|
+
index: a.index.toString(),
|
|
3867
|
+
signer,
|
|
3569
3868
|
},
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
const a =
|
|
3575
|
-
return
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3869
|
+
type: 'validator-fee-recipient-set',
|
|
3870
|
+
};
|
|
3871
|
+
});
|
|
3872
|
+
recognize(named('IpAddressesUpdated'), (event) => {
|
|
3873
|
+
const a = event.args;
|
|
3874
|
+
return {
|
|
3875
|
+
...itemBase(event.logIndex),
|
|
3876
|
+
data: {
|
|
3877
|
+
caller: lc(a.caller),
|
|
3878
|
+
egress: a.egress,
|
|
3879
|
+
index: a.index.toString(),
|
|
3880
|
+
ingress: a.ingress,
|
|
3881
|
+
signer,
|
|
3580
3882
|
},
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
const a =
|
|
3586
|
-
return
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
type: 'network-identity-rotation-epoch-set',
|
|
3883
|
+
type: 'validator-ip-set',
|
|
3884
|
+
};
|
|
3885
|
+
});
|
|
3886
|
+
recognize(named('ValidatorOwnershipTransferred'), (event) => {
|
|
3887
|
+
const a = event.args;
|
|
3888
|
+
return {
|
|
3889
|
+
...itemBase(event.logIndex),
|
|
3890
|
+
data: {
|
|
3891
|
+
caller: lc(a.caller),
|
|
3892
|
+
index: a.index.toString(),
|
|
3893
|
+
newAddress: lc(a.newAddress),
|
|
3894
|
+
oldAddress: lc(a.oldAddress),
|
|
3895
|
+
signer,
|
|
3595
3896
|
},
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
const a =
|
|
3601
|
-
return
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3897
|
+
type: 'validator-ownership-transferred',
|
|
3898
|
+
};
|
|
3899
|
+
});
|
|
3900
|
+
recognize(named('OwnershipTransferred'), (event) => {
|
|
3901
|
+
const a = event.args;
|
|
3902
|
+
return {
|
|
3903
|
+
...itemBase(event.logIndex),
|
|
3904
|
+
data: { newOwner: lc(a.newOwner), oldOwner: lc(a.oldOwner), signer },
|
|
3905
|
+
type: 'ownership-transferred',
|
|
3906
|
+
};
|
|
3907
|
+
});
|
|
3908
|
+
recognize(named('NetworkIdentityRotationEpochSet'), (event) => {
|
|
3909
|
+
const a = event.args;
|
|
3910
|
+
return {
|
|
3911
|
+
...itemBase(event.logIndex),
|
|
3912
|
+
data: {
|
|
3913
|
+
nextEpoch: a.nextEpoch.toString(),
|
|
3914
|
+
previousEpoch: a.previousEpoch.toString(),
|
|
3915
|
+
signer,
|
|
3606
3916
|
},
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3917
|
+
type: 'network-identity-rotation-epoch-set',
|
|
3918
|
+
};
|
|
3919
|
+
});
|
|
3920
|
+
recognize(named('Initialized'), (event) => {
|
|
3921
|
+
const a = event.args;
|
|
3922
|
+
return {
|
|
3923
|
+
...itemBase(event.logIndex),
|
|
3924
|
+
data: { height: a.height.toString(), signer },
|
|
3925
|
+
type: 'initialized',
|
|
3926
|
+
};
|
|
3927
|
+
});
|
|
3928
|
+
// Plain transfers: every remaining viewer-party transfer is its own
|
|
3929
|
+
// activity. Non-viewer transfers are internal legs of what classified
|
|
3930
|
+
// above and surface only via the fallback below.
|
|
3931
|
+
const buildTransfer = (event) => {
|
|
3932
|
+
const args = transferArgs(event);
|
|
3933
|
+
const to = lc(args.to);
|
|
3934
|
+
const from = lc(args.from);
|
|
3935
|
+
const rawMemo = event.eventName === 'TransferWithMemo' ? args.memo : undefined;
|
|
3617
3936
|
// The raw memo (not the suppressed display string) carries the MPP
|
|
3618
3937
|
// attribution fingerprint, so resolve the service name from it.
|
|
3619
3938
|
const memo = rawMemo ? Transfers.memoToString(rawMemo) : undefined;
|
|
3620
3939
|
const attribution = Mpp.resolve({ fingerprintMap, memo: rawMemo });
|
|
3621
|
-
const sourceToken = token(args.amount,
|
|
3940
|
+
const sourceToken = token(args.amount, event.address);
|
|
3622
3941
|
if (Address.isEqual(to, viewer))
|
|
3623
|
-
return
|
|
3624
|
-
transfer(itemBase(logIndexOfEvent(t)), blockNum, {
|
|
3625
|
-
attribution,
|
|
3626
|
-
direction: 'in',
|
|
3627
|
-
memo,
|
|
3628
|
-
recipient: viewer,
|
|
3629
|
-
sender: from,
|
|
3630
|
-
sourceToken,
|
|
3631
|
-
}),
|
|
3632
|
-
...accessKeyItems,
|
|
3633
|
-
];
|
|
3634
|
-
return [
|
|
3635
|
-
transfer(itemBase(logIndexOfEvent(t)), blockNum, {
|
|
3942
|
+
return transfer(itemBase(event.logIndex), blockNum, {
|
|
3636
3943
|
attribution,
|
|
3637
|
-
direction: '
|
|
3944
|
+
direction: 'in',
|
|
3638
3945
|
memo,
|
|
3639
|
-
recipient:
|
|
3946
|
+
recipient: viewer,
|
|
3640
3947
|
sender: from,
|
|
3641
|
-
signer,
|
|
3642
3948
|
sourceToken,
|
|
3643
|
-
})
|
|
3644
|
-
|
|
3645
|
-
|
|
3949
|
+
});
|
|
3950
|
+
return transfer(itemBase(event.logIndex), blockNum, {
|
|
3951
|
+
attribution,
|
|
3952
|
+
direction: 'out',
|
|
3953
|
+
memo,
|
|
3954
|
+
recipient: to,
|
|
3955
|
+
sender: from,
|
|
3956
|
+
signer,
|
|
3957
|
+
sourceToken,
|
|
3958
|
+
});
|
|
3959
|
+
};
|
|
3960
|
+
const isUserTransfer = (event) => isTransfer(event) && !Address.isEqual(transferArgs(event).to, Addresses.feeManager);
|
|
3961
|
+
recognize((event) => isUserTransfer(event) &&
|
|
3962
|
+
(Address.isEqual(transferArgs(event).from, viewer) ||
|
|
3963
|
+
Address.isEqual(transferArgs(event).to, viewer)), buildTransfer);
|
|
3964
|
+
// Fallback when nothing viewer-facing classified: surface fold-only vault
|
|
3965
|
+
// items, third-party approvals, and transfers (e.g. a sponsored batch)
|
|
3966
|
+
// instead of reporting `unknown`.
|
|
3967
|
+
if (items.length === 0) {
|
|
3968
|
+
items.push(...suppressedVaultItems);
|
|
3969
|
+
recognize(isErc20Approval, approvalItem);
|
|
3970
|
+
recognize(isUserTransfer, buildTransfer);
|
|
3646
3971
|
}
|
|
3647
|
-
//
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
return [unknown(itemBase(minLogIndex), { events: buildTxEvents(rows, events), signer })];
|
|
3654
|
-
}
|
|
3655
|
-
/** Extracts the block-wide log index from a decoded viem event. */
|
|
3656
|
-
function logIndexOfEvent(event) {
|
|
3657
|
-
return event.logIndex;
|
|
3972
|
+
// Unclassified: a tx the address sent or paid for whose logs matched no
|
|
3973
|
+
// known classifier. Surface it honestly as an `unknown` item carrying every
|
|
3974
|
+
// log (decoded when possible), rather than fabricating a zero transfer.
|
|
3975
|
+
if (items.length === 0)
|
|
3976
|
+
return [unknown(itemBase(minLogIndex), { events: buildTxEvents(rows, events), signer })];
|
|
3977
|
+
return items.sort((a, b) => a.logIndex - b.logIndex);
|
|
3658
3978
|
}
|
|
3659
3979
|
/**
|
|
3660
3980
|
* Builds a `transfer` item whose `data` is the `Transfer` API resource plus the
|
|
@@ -3693,29 +4013,31 @@ function unknown(base, data) {
|
|
|
3693
4013
|
}
|
|
3694
4014
|
/** Decodes a group's raw rows into `viem/tempo` events via `parseEventLogs` (non-strict). */
|
|
3695
4015
|
function decodeRows(rows) {
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
4016
|
+
const logs = rows.map((r) => ({
|
|
4017
|
+
address: r.logAddress,
|
|
4018
|
+
blockHash: '0x0',
|
|
4019
|
+
blockNumber: BigInt(r.blockNum),
|
|
4020
|
+
data: (r.data ?? '0x'),
|
|
4021
|
+
logIndex: r.logIdx,
|
|
4022
|
+
removed: false,
|
|
4023
|
+
topics: [r.selector, r.topic1, r.topic2, r.topic3].filter(Boolean),
|
|
4024
|
+
transactionHash: r.txHash,
|
|
4025
|
+
transactionIndex: r.txIdx,
|
|
4026
|
+
}));
|
|
4027
|
+
try {
|
|
4028
|
+
// One batched call: viem re-processes the merged ABI per invocation, so
|
|
4029
|
+
// per-row calls cost ~55x more. Non-strict decoding skips undecodable
|
|
4030
|
+
// logs per log and can yield `args: undefined`; normalize so `in`-guards
|
|
4031
|
+
// never throw.
|
|
4032
|
+
return parseEventLogs({
|
|
4033
|
+
abi: [...Abis.abis, ...Abis.earnRouter, ...Abis.earnVault, ...erc4626Events],
|
|
4034
|
+
logs,
|
|
4035
|
+
strict: false,
|
|
4036
|
+
}).map((event) => ({ ...event, args: event.args ?? {} }));
|
|
4037
|
+
}
|
|
4038
|
+
catch {
|
|
4039
|
+
return [];
|
|
4040
|
+
}
|
|
3719
4041
|
}
|
|
3720
4042
|
/**
|
|
3721
4043
|
* Builds the full per-transaction `events` capture: one entry per raw log,
|
|
@@ -3873,8 +4195,8 @@ async function enrichTokens(c, options) {
|
|
|
3873
4195
|
rates,
|
|
3874
4196
|
};
|
|
3875
4197
|
}
|
|
3876
|
-
/** Resolves token metadata on each item's source
|
|
3877
|
-
async function
|
|
4198
|
+
/** Resolves token metadata on each item's source chain without changing feed order. */
|
|
4199
|
+
async function enrichChainTokens(c, options) {
|
|
3878
4200
|
const byChain = new Map();
|
|
3879
4201
|
for (const item of options.items) {
|
|
3880
4202
|
const items = byChain.get(item.chainId) ?? [];
|