tapimo 0.5.2 → 0.6.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 +13987 -5755
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +910 -412
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +4 -0
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/early-access.d.ts +148 -0
- package/dist/admin/apps/early-access.d.ts.map +1 -0
- package/dist/admin/apps/early-access.js +133 -0
- package/dist/admin/apps/early-access.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +643 -118
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +214 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- 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 +45 -33
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +3733 -2428
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +11 -6
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +193 -2
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +71 -0
- package/dist/apps/data/FxOracle.d.ts.map +1 -0
- package/dist/apps/data/FxOracle.js +97 -0
- package/dist/apps/data/FxOracle.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -0
- package/dist/apps/data/index.d.ts.map +1 -1
- package/dist/apps/data/index.js +1 -0
- package/dist/apps/data/index.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +3575 -1358
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +230 -88
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +963 -342
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/addresses.js +11 -9
- package/dist/apps/data/routes/addresses.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +108 -57
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +88 -45
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +169 -169
- package/dist/apps/data/routes/exchanges.d.ts +952 -201
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +518 -76
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +24 -24
- package/dist/apps/data/routes/funding.d.ts +8 -2
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +32 -6
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +5 -5
- package/dist/apps/data/routes/receipts.d.ts +392 -315
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +152 -77
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +166 -173
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +14 -12
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +277 -536
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +96 -77
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +82 -17
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +70 -13
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +307 -0
- package/dist/apps/data/routes/valuation.d.ts.map +1 -0
- package/dist/apps/data/routes/valuation.js +292 -0
- package/dist/apps/data/routes/valuation.js.map +1 -0
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +7 -5
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +459 -479
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +124 -25
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +8606 -1681
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +12 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +23 -0
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +5 -3
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +88 -48
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +10 -2
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +218 -0
- package/dist/apps/management/routes/faucet.d.ts.map +1 -0
- package/dist/apps/management/routes/faucet.js +139 -0
- package/dist/apps/management/routes/faucet.js.map +1 -0
- package/dist/apps/management/routes/invitations.d.ts +121 -121
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +3 -0
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +197 -168
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +26 -1
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +2 -0
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +4 -0
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +49 -49
- package/dist/apps/management/routes/orgs.d.ts +91 -91
- package/dist/apps/management/routes/projects.d.ts +115 -115
- package/dist/apps/management/routes/usage.d.ts +35 -32
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +4 -0
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
- package/dist/apps/management/routes/verified-token-requests.js +271 -0
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
- package/dist/apps/management/routes/webhooks.d.ts +6977 -0
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
- package/dist/apps/management/routes/webhooks.js +573 -0
- package/dist/apps/management/routes/webhooks.js.map +1 -0
- package/dist/apps/mcp.d.ts +9 -2
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +9 -4
- package/dist/apps/mcp.js.map +1 -1
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +7 -2
- package/dist/cli/Cli.js.map +1 -1
- package/dist/db/Db.d.ts +4 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +83 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +41 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +45 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earlyAccess.d.ts +50 -0
- package/dist/db/tables/earlyAccess.d.ts.map +1 -0
- package/dist/db/tables/earlyAccess.js +63 -0
- package/dist/db/tables/earlyAccess.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +12 -1
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +23 -0
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/inviteLinks.d.ts +10 -2
- package/dist/db/tables/inviteLinks.d.ts.map +1 -1
- package/dist/db/tables/inviteLinks.js +10 -1
- package/dist/db/tables/inviteLinks.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +2 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
- package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
- package/dist/db/tables/verifiedTokenRequests.js +120 -0
- package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +16 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +43 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +60 -45
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +69 -15
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +74 -25
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +19 -1
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +2 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +9 -0
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +2 -2
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +3 -0
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Schema.d.ts +14 -5
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +23 -5
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Value.d.ts +18 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +11 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +10 -1
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Viem.d.ts +1 -1
- package/dist/internal/Webhooks.d.ts +24 -3
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +34 -8
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +2 -2
- package/src/App.ts +9 -1
- package/src/Client.test-d.ts +638 -6
- package/src/admin/App.ts +4 -0
- package/src/admin/apps/early-access.test.ts +125 -0
- package/src/admin/apps/early-access.ts +175 -0
- package/src/admin/apps/verified-tokens.test.ts +201 -0
- package/src/admin/apps/verified-tokens.ts +532 -286
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/lib/early-access.ts +63 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
- package/src/admin/ui/src/routeTree.gen.ts +33 -2
- package/src/admin/ui/src/routes/__root.tsx +2 -1
- package/src/admin/ui/src/routes/early-access.tsx +157 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
- package/src/admin/ui.generated.ts +48 -33
- package/src/apps/data/App.ts +22 -14
- package/src/apps/data/FundingQuoteProviders.test.ts +298 -0
- package/src/apps/data/FundingQuoteProviders.ts +257 -4
- package/src/apps/data/FxOracle.test.ts +118 -0
- package/src/apps/data/FxOracle.ts +153 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/activities.test.ts +173 -3
- package/src/apps/data/routes/activities.ts +403 -132
- package/src/apps/data/routes/addresses.ts +19 -9
- package/src/apps/data/routes/balances.test.ts +144 -11
- package/src/apps/data/routes/balances.ts +140 -51
- package/src/apps/data/routes/exchanges.test.ts +540 -9
- package/src/apps/data/routes/exchanges.ts +690 -103
- package/src/apps/data/routes/funding.test.ts +84 -0
- package/src/apps/data/routes/funding.ts +38 -6
- package/src/apps/data/routes/receipts.test.ts +125 -23
- package/src/apps/data/routes/receipts.ts +205 -100
- package/src/apps/data/routes/tokens.test.ts +43 -0
- package/src/apps/data/routes/tokens.ts +27 -12
- package/src/apps/data/routes/transactions.test.ts +72 -19
- package/src/apps/data/routes/transactions.ts +162 -92
- package/src/apps/data/routes/transfers.test.ts +146 -4
- package/src/apps/data/routes/transfers.ts +91 -13
- package/src/apps/data/routes/valuation.test.ts +422 -0
- package/src/apps/data/routes/valuation.ts +423 -0
- package/src/apps/data/routes/verified-tokens.ts +14 -2
- package/src/apps/data/routes/webhooks.test.ts +56 -8
- package/src/apps/data/routes/webhooks.ts +144 -27
- package/src/apps/index.ts +1 -1
- package/src/apps/management/App.ts +13 -0
- package/src/apps/management/routes/api-keys.test.ts +87 -0
- package/src/apps/management/routes/api-keys.ts +4 -3
- package/src/apps/management/routes/billing.test.ts +112 -1
- package/src/apps/management/routes/billing.ts +7 -2
- package/src/apps/management/routes/faucet.test.ts +79 -0
- package/src/apps/management/routes/faucet.ts +185 -0
- package/src/apps/management/routes/invitations.test.ts +172 -0
- package/src/apps/management/routes/invitations.ts +3 -0
- package/src/apps/management/routes/invite-links.test.ts +117 -0
- package/src/apps/management/routes/invite-links.ts +43 -1
- package/src/apps/management/routes/me.test.ts +140 -88
- package/src/apps/management/routes/me.ts +6 -0
- package/src/apps/management/routes/usage.test.ts +6 -0
- package/src/apps/management/routes/usage.ts +7 -0
- package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
- package/src/apps/management/routes/verified-token-requests.ts +397 -0
- package/src/apps/management/routes/webhooks.test.ts +426 -0
- package/src/apps/management/routes/webhooks.ts +672 -0
- package/src/apps/mcp.test.ts +13 -0
- package/src/apps/mcp.ts +18 -5
- package/src/cli/Cli.test.ts +36 -0
- package/src/cli/Cli.ts +7 -2
- package/src/db/Db.test.ts +19 -0
- package/src/db/Db.ts +95 -0
- package/src/db/Schema.ts +53 -0
- package/src/db/tables/earlyAccess.ts +86 -0
- package/src/db/tables/invitations.ts +26 -1
- package/src/db/tables/inviteLinks.ts +22 -3
- package/src/db/tables/sponsoredTransactions.ts +4 -0
- package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
- package/src/db/tables/verifiedTokenRequests.ts +182 -0
- package/src/db/tables/webhookSubscriptions.ts +31 -0
- package/src/internal/Auth.ts +73 -45
- package/src/internal/FundingQuotes.test.ts +143 -1
- package/src/internal/FundingQuotes.ts +95 -28
- package/src/internal/Log.test-d.ts +21 -0
- package/src/internal/Log.test.ts +50 -0
- package/src/internal/Log.ts +24 -1
- package/src/internal/MetricSink.test.ts +160 -0
- package/src/internal/MetricSink.ts +10 -0
- package/src/internal/OpenApi.ts +3 -0
- package/src/internal/Schema.ts +50 -5
- package/src/internal/Value.test.ts +20 -0
- package/src/internal/Value.ts +24 -1
- package/src/internal/VerifiedTokens.ts +9 -1
- package/src/internal/Webhooks.test.ts +140 -0
- package/src/internal/Webhooks.ts +53 -9
|
@@ -17,12 +17,17 @@ import type * as Tidx from '../../../internal/Tidx.js'
|
|
|
17
17
|
import * as Timing from '../../../internal/Timing.js'
|
|
18
18
|
import * as Ttl from '../../../internal/Ttl.js'
|
|
19
19
|
import * as Value from '../../../internal/Value.js'
|
|
20
|
+
import * as VerifiedTokens from '../../../internal/VerifiedTokens.js'
|
|
21
|
+
import * as FxOracle from '../FxOracle.js'
|
|
20
22
|
import * as Tokens from './tokens.js'
|
|
21
23
|
import * as Transfers from './transfers.js'
|
|
24
|
+
import * as Valuation from './valuation.js'
|
|
22
25
|
|
|
23
26
|
/** Example transaction hash surfaced in OpenAPI docs. */
|
|
24
27
|
const exampleHash = '0x4845ae2098724ab26a5d89370dce5124d044be6a5acc164afc348662de67d474'
|
|
25
28
|
|
|
29
|
+
const tokenFields = ['token.logoUri', 'token.verified'] as const
|
|
30
|
+
|
|
26
31
|
/** Zod schemas owned by the activity handlers. */
|
|
27
32
|
export namespace schema {
|
|
28
33
|
/** A signer: the literal `self`, or the access-key address that signed. */
|
|
@@ -32,20 +37,50 @@ export namespace schema {
|
|
|
32
37
|
z.describe('Who signed the activity: `self`, or the access-key address used to sign it.'),
|
|
33
38
|
)
|
|
34
39
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/
|
|
41
|
-
export const Token = Transfers.schema.SourceToken
|
|
40
|
+
/** A token with required RPC metadata and optional curated fields. */
|
|
41
|
+
export const Token = Schema.describe(
|
|
42
|
+
Tokens.schema.TokenReference,
|
|
43
|
+
'A token referenced by an activity item.',
|
|
44
|
+
)
|
|
42
45
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
/** A token amount carrying its nominal value in the requested denomination. */
|
|
47
|
+
const ValuedAmount = z.extend(Schema.TokenAmount, {
|
|
48
|
+
valuation: z
|
|
49
|
+
.optional(z.nullable(Valuation.schema.Value))
|
|
50
|
+
.check(
|
|
51
|
+
z.describe(
|
|
52
|
+
'The amount’s nominal value in the requested `denomination`. `null` when the token is ' +
|
|
53
|
+
'unverified, its display currency has no rate, or rates were unavailable.',
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/** Self-contained amount for a destination token. */
|
|
59
|
+
const DestinationAmount = ValuedAmount.check(
|
|
60
|
+
z.describe('Amount associated with `destinationToken`.'),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/** Self-contained amount for a refunded token. */
|
|
64
|
+
const RefundAmount = ValuedAmount.check(z.describe('Amount associated with `refund`.'))
|
|
65
|
+
|
|
66
|
+
/** Self-contained amount for a source token. */
|
|
67
|
+
const SourceAmount = ValuedAmount.check(z.describe('Amount associated with `sourceToken`.'))
|
|
68
|
+
|
|
69
|
+
/** Page-level resources: valuation rate provenance. */
|
|
70
|
+
export const Meta = z
|
|
71
|
+
.object({
|
|
72
|
+
pricing: z
|
|
73
|
+
.optional(Valuation.schema.Pricing)
|
|
74
|
+
.check(
|
|
75
|
+
z.describe(
|
|
76
|
+
'Rate provenance for amount valuations. Present when conversion rates were ' +
|
|
77
|
+
'consulted; absent when every value was identity-valued or rates were unavailable.',
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
})
|
|
81
|
+
.check(z.describe('Page-level resources attached to this response.'))
|
|
82
|
+
|
|
83
|
+
/** A token transfer with subject-relative direction and signer fields. */
|
|
49
84
|
export const Transfer = z
|
|
50
85
|
.object({
|
|
51
86
|
attribution: z
|
|
@@ -64,11 +99,18 @@ export namespace schema {
|
|
|
64
99
|
z.describe('Block number where this transfer was included.'),
|
|
65
100
|
z.meta({ examples: [12345678] }),
|
|
66
101
|
),
|
|
102
|
+
destinationAmount: z
|
|
103
|
+
.optional(DestinationAmount)
|
|
104
|
+
.check(
|
|
105
|
+
z.describe(
|
|
106
|
+
'Amount delivered to the recipient for a cross-token transfer. Omitted with `destinationToken` for a same-token transfer.',
|
|
107
|
+
),
|
|
108
|
+
),
|
|
67
109
|
destinationToken: z
|
|
68
110
|
.optional(Token)
|
|
69
111
|
.check(
|
|
70
112
|
z.describe(
|
|
71
|
-
'Token
|
|
113
|
+
'Token delivered to the recipient when it differs from the source token. Omitted for a same-token transfer.',
|
|
72
114
|
),
|
|
73
115
|
),
|
|
74
116
|
direction: z
|
|
@@ -90,7 +132,8 @@ export namespace schema {
|
|
|
90
132
|
'For outbound transfers, who signed it: `self` or the access-key address. Omitted for inbound transfers.',
|
|
91
133
|
),
|
|
92
134
|
),
|
|
93
|
-
|
|
135
|
+
sourceAmount: SourceAmount,
|
|
136
|
+
sourceToken: Token.check(z.describe('Token that moved in the transfer.')),
|
|
94
137
|
timestamp: z
|
|
95
138
|
.iso.datetime()
|
|
96
139
|
.check(z.describe('Block timestamp for the transfer.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })), // prettier-ignore
|
|
@@ -98,7 +141,16 @@ export namespace schema {
|
|
|
98
141
|
z.describe('Transaction hash that contains this transfer.'),
|
|
99
142
|
),
|
|
100
143
|
})
|
|
101
|
-
.check(
|
|
144
|
+
.check(
|
|
145
|
+
z.refine(
|
|
146
|
+
(data) => (data.destinationToken === undefined) === (data.destinationAmount === undefined),
|
|
147
|
+
{
|
|
148
|
+
error: '`destinationAmount` and `destinationToken` must be returned together.',
|
|
149
|
+
path: ['destinationAmount'],
|
|
150
|
+
},
|
|
151
|
+
),
|
|
152
|
+
z.describe('One token transfer involving the requested address.'),
|
|
153
|
+
)
|
|
102
154
|
|
|
103
155
|
/**
|
|
104
156
|
* One transaction log, decoded into a known Tempo event when possible. The
|
|
@@ -193,9 +245,7 @@ export namespace schema {
|
|
|
193
245
|
.object({
|
|
194
246
|
...base,
|
|
195
247
|
data: Transfer.check(
|
|
196
|
-
z.describe(
|
|
197
|
-
'Transfer details, using the `/transfers` shape plus feed-only fields such as direction and signer.',
|
|
198
|
-
),
|
|
248
|
+
z.describe('Transfer details with feed-relative direction and signer fields.'),
|
|
199
249
|
),
|
|
200
250
|
type: z
|
|
201
251
|
.literal('transfer')
|
|
@@ -208,7 +258,8 @@ export namespace schema {
|
|
|
208
258
|
data: z
|
|
209
259
|
.object({
|
|
210
260
|
recipient: Schema.Address.check(z.describe('Address that received the transfer.')),
|
|
211
|
-
|
|
261
|
+
sourceAmount: SourceAmount,
|
|
262
|
+
sourceToken: Token.check(z.describe('Token that was minted.')),
|
|
212
263
|
})
|
|
213
264
|
.check(z.describe('A token mint activity.')),
|
|
214
265
|
type: z
|
|
@@ -223,7 +274,8 @@ export namespace schema {
|
|
|
223
274
|
.object({
|
|
224
275
|
sender: Schema.Address.check(z.describe('Address whose token balance was burned.')),
|
|
225
276
|
signer: Signer,
|
|
226
|
-
|
|
277
|
+
sourceAmount: SourceAmount,
|
|
278
|
+
sourceToken: Token.check(z.describe('Token that was burned.')),
|
|
227
279
|
})
|
|
228
280
|
.check(z.describe('A token burn activity.')),
|
|
229
281
|
type: z
|
|
@@ -236,8 +288,10 @@ export namespace schema {
|
|
|
236
288
|
...base,
|
|
237
289
|
data: z
|
|
238
290
|
.object({
|
|
291
|
+
destinationAmount: DestinationAmount,
|
|
239
292
|
destinationToken: Token.check(z.describe('Token the address received in the swap.')),
|
|
240
293
|
signer: Signer,
|
|
294
|
+
sourceAmount: SourceAmount,
|
|
241
295
|
sourceToken: Token.check(z.describe('Token the address sent into the swap.')),
|
|
242
296
|
})
|
|
243
297
|
.check(
|
|
@@ -256,9 +310,8 @@ export namespace schema {
|
|
|
256
310
|
data: z
|
|
257
311
|
.object({
|
|
258
312
|
signer: Signer,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
),
|
|
313
|
+
sourceAmount: SourceAmount,
|
|
314
|
+
sourceToken: Token.check(z.describe('Token approved for spending.')),
|
|
262
315
|
spender: Schema.Address.check(
|
|
263
316
|
z.describe('Address allowed to spend the approved amount.'),
|
|
264
317
|
),
|
|
@@ -277,9 +330,18 @@ export namespace schema {
|
|
|
277
330
|
refund: z
|
|
278
331
|
.optional(Token)
|
|
279
332
|
.check(z.describe('Token refunded to the address when the session closed, if any.')),
|
|
333
|
+
refundAmount: z
|
|
334
|
+
.optional(RefundAmount)
|
|
335
|
+
.check(z.describe('Amount refunded to the address when the session closed, if any.')),
|
|
280
336
|
signer: Signer,
|
|
281
337
|
})
|
|
282
|
-
.check(
|
|
338
|
+
.check(
|
|
339
|
+
z.refine((data) => (data.refund === undefined) === (data.refundAmount === undefined), {
|
|
340
|
+
error: '`refundAmount` and `refund` must be returned together.',
|
|
341
|
+
path: ['refundAmount'],
|
|
342
|
+
}),
|
|
343
|
+
z.describe('A payment-channel session close activity.'),
|
|
344
|
+
),
|
|
283
345
|
type: z
|
|
284
346
|
.literal('session-closed')
|
|
285
347
|
.check(z.describe('Activity type that tells you which `data` shape this entry uses.')),
|
|
@@ -354,7 +416,8 @@ export namespace schema {
|
|
|
354
416
|
z.describe('Address whose blocked balance was burned by policy.'),
|
|
355
417
|
),
|
|
356
418
|
signer: Signer,
|
|
357
|
-
|
|
419
|
+
sourceAmount: SourceAmount,
|
|
420
|
+
sourceToken: Token.check(z.describe('Token that was burned.')),
|
|
358
421
|
})
|
|
359
422
|
.check(
|
|
360
423
|
z.describe(
|
|
@@ -377,9 +440,8 @@ export namespace schema {
|
|
|
377
440
|
),
|
|
378
441
|
payer: Schema.Address.check(z.describe('Address that funds the channel.')),
|
|
379
442
|
signer: Signer,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
),
|
|
443
|
+
sourceAmount: SourceAmount,
|
|
444
|
+
sourceToken: Token.check(z.describe('Token locked into the channel.')),
|
|
383
445
|
})
|
|
384
446
|
.check(z.describe('A payment channel was opened and funded.')),
|
|
385
447
|
type: z
|
|
@@ -394,7 +456,8 @@ export namespace schema {
|
|
|
394
456
|
.object({
|
|
395
457
|
channelId: Schema.Hex.check(z.describe('Identifier of the payment channel.')),
|
|
396
458
|
signer: Signer,
|
|
397
|
-
|
|
459
|
+
sourceAmount: SourceAmount,
|
|
460
|
+
sourceToken: Token.check(z.describe('Token added to the channel deposit.')),
|
|
398
461
|
})
|
|
399
462
|
.check(z.describe('A payment channel deposit was topped up.')),
|
|
400
463
|
type: z
|
|
@@ -411,7 +474,8 @@ export namespace schema {
|
|
|
411
474
|
payee: Schema.Address.check(z.describe('Address that received the settled payment.')),
|
|
412
475
|
payer: Schema.Address.check(z.describe('Address that funded the channel.')),
|
|
413
476
|
signer: Signer,
|
|
414
|
-
|
|
477
|
+
sourceAmount: SourceAmount,
|
|
478
|
+
sourceToken: Token.check(z.describe('Token paid out in this settlement.')),
|
|
415
479
|
})
|
|
416
480
|
.check(z.describe('An incremental payment-channel settlement to the payee.')),
|
|
417
481
|
type: z
|
|
@@ -431,13 +495,30 @@ export namespace schema {
|
|
|
431
495
|
payer: Schema.Address.check(z.describe('Address that funded the channel.')),
|
|
432
496
|
refund: z
|
|
433
497
|
.optional(Token)
|
|
434
|
-
.check(z.describe('Token
|
|
498
|
+
.check(z.describe('Token refunded to the payer on close, if any.')),
|
|
499
|
+
refundAmount: z
|
|
500
|
+
.optional(RefundAmount)
|
|
501
|
+
.check(z.describe('Amount refunded to the payer on close, if any.')),
|
|
435
502
|
signer: Signer,
|
|
503
|
+
sourceAmount: z
|
|
504
|
+
.optional(SourceAmount)
|
|
505
|
+
.check(z.describe('Final amount paid to the payee on close, if any.')),
|
|
436
506
|
sourceToken: z
|
|
437
507
|
.optional(Token)
|
|
438
|
-
.check(z.describe('Token
|
|
508
|
+
.check(z.describe('Token paid out to the payee on close, if any.')),
|
|
439
509
|
})
|
|
440
510
|
.check(
|
|
511
|
+
z.refine((data) => (data.refund === undefined) === (data.refundAmount === undefined), {
|
|
512
|
+
error: '`refundAmount` and `refund` must be returned together.',
|
|
513
|
+
path: ['refundAmount'],
|
|
514
|
+
}),
|
|
515
|
+
z.refine(
|
|
516
|
+
(data) => (data.sourceToken === undefined) === (data.sourceAmount === undefined),
|
|
517
|
+
{
|
|
518
|
+
error: '`sourceAmount` and `sourceToken` must be returned together.',
|
|
519
|
+
path: ['sourceAmount'],
|
|
520
|
+
},
|
|
521
|
+
),
|
|
441
522
|
z.describe(
|
|
442
523
|
'A payment channel was closed, paying out the payee and refunding the payer.',
|
|
443
524
|
),
|
|
@@ -475,7 +556,8 @@ export namespace schema {
|
|
|
475
556
|
.enum(['bid', 'ask'])
|
|
476
557
|
.check(z.describe('Order side: `bid` buys the base token, `ask` sells it.'), z.meta({ examples: ['bid'] })), // prettier-ignore
|
|
477
558
|
signer: Signer,
|
|
478
|
-
|
|
559
|
+
sourceAmount: SourceAmount,
|
|
560
|
+
sourceToken: Token.check(z.describe('Token escrowed for the order.')),
|
|
479
561
|
tick: z
|
|
480
562
|
.number()
|
|
481
563
|
.check(
|
|
@@ -512,7 +594,8 @@ export namespace schema {
|
|
|
512
594
|
data: z
|
|
513
595
|
.object({
|
|
514
596
|
signer: Signer,
|
|
515
|
-
|
|
597
|
+
sourceAmount: SourceAmount,
|
|
598
|
+
sourceToken: Token.check(z.describe('Token used for the distributed fees.')),
|
|
516
599
|
validator: Schema.Address.check(
|
|
517
600
|
z.describe('Validator that received the distributed fees.'),
|
|
518
601
|
),
|
|
@@ -528,11 +611,11 @@ export namespace schema {
|
|
|
528
611
|
...base,
|
|
529
612
|
data: z
|
|
530
613
|
.object({
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
),
|
|
614
|
+
destinationAmount: DestinationAmount,
|
|
615
|
+
destinationToken: Token.check(z.describe('Token received from the fee AMM.')),
|
|
534
616
|
signer: Signer,
|
|
535
|
-
|
|
617
|
+
sourceAmount: SourceAmount,
|
|
618
|
+
sourceToken: Token.check(z.describe('Token sent into the fee AMM.')),
|
|
536
619
|
swapper: Schema.Address.check(z.describe('Address that performed the rebalance swap.')),
|
|
537
620
|
})
|
|
538
621
|
.check(z.describe('A fee-AMM rebalance swap between a validator and user token.')),
|
|
@@ -548,7 +631,8 @@ export namespace schema {
|
|
|
548
631
|
.object({
|
|
549
632
|
funder: Schema.Address.check(z.describe('Address that funded the reward pool.')),
|
|
550
633
|
signer: Signer,
|
|
551
|
-
|
|
634
|
+
sourceAmount: SourceAmount,
|
|
635
|
+
sourceToken: Token.check(z.describe('Token added to the reward pool.')),
|
|
552
636
|
})
|
|
553
637
|
.check(z.describe('A token reward pool was funded.')),
|
|
554
638
|
type: z
|
|
@@ -583,9 +667,8 @@ export namespace schema {
|
|
|
583
667
|
z.describe('Public key of the access key whose limit changed.'),
|
|
584
668
|
),
|
|
585
669
|
signer: Signer,
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
),
|
|
670
|
+
sourceAmount: SourceAmount,
|
|
671
|
+
sourceToken: Token.check(z.describe('Token whose spending limit was updated.')),
|
|
589
672
|
})
|
|
590
673
|
.check(z.describe('An access key’s per-token spending limit was updated.')),
|
|
591
674
|
type: z
|
|
@@ -636,7 +719,8 @@ export namespace schema {
|
|
|
636
719
|
.enum(['bid', 'ask'])
|
|
637
720
|
.check(z.describe('Side of the flipped order: `bid` buys the base token, `ask` sells it.'), z.meta({ examples: ['bid'] })), // prettier-ignore
|
|
638
721
|
signer: Signer,
|
|
639
|
-
|
|
722
|
+
sourceAmount: SourceAmount,
|
|
723
|
+
sourceToken: Token.check(z.describe('Token the flipped order rests with.')),
|
|
640
724
|
tick: z
|
|
641
725
|
.number()
|
|
642
726
|
.check(
|
|
@@ -1292,6 +1376,17 @@ export namespace schema {
|
|
|
1292
1376
|
.union([...items, Group])
|
|
1293
1377
|
.check(z.describe('Either one activity item, or a grouped set of access-key activity items.'))
|
|
1294
1378
|
|
|
1379
|
+
/** Optional token fields that require sources beyond RPC metadata. */
|
|
1380
|
+
export const Include = z
|
|
1381
|
+
.enum(tokenFields)
|
|
1382
|
+
.check(z.describe('Additional token fields to include in activity responses.'))
|
|
1383
|
+
|
|
1384
|
+
/** Parses comma-separated optional token fields. */
|
|
1385
|
+
export const includeQuery = Schema.includeQuery(
|
|
1386
|
+
Include,
|
|
1387
|
+
'Comma-separated token fields to include, such as `token.logoUri,token.verified`.',
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1295
1390
|
/** Schemas for the getAddressActivities operation. */
|
|
1296
1391
|
export namespace getAddressActivities {
|
|
1297
1392
|
/** Path parameters for address activity requests. */
|
|
@@ -1308,6 +1403,7 @@ export namespace schema {
|
|
|
1308
1403
|
.strictObject({
|
|
1309
1404
|
chainId: Schema.ChainIdQuery,
|
|
1310
1405
|
cursor: Schema.Cursor,
|
|
1406
|
+
denomination: Schema.Denomination,
|
|
1311
1407
|
// Folding is opt-in: by default every item is returned at the root.
|
|
1312
1408
|
group: z
|
|
1313
1409
|
._default(Schema.booleanQuery(), false)
|
|
@@ -1317,6 +1413,7 @@ export namespace schema {
|
|
|
1317
1413
|
),
|
|
1318
1414
|
z.meta({ examples: [true] }),
|
|
1319
1415
|
),
|
|
1416
|
+
include: includeQuery,
|
|
1320
1417
|
limit: Schema.Limit,
|
|
1321
1418
|
// Raw-log capture is opt-in: it can add a large `events` array to every
|
|
1322
1419
|
// item, so the default feed stays lean.
|
|
@@ -1344,6 +1441,9 @@ export namespace schema {
|
|
|
1344
1441
|
export const Response = z
|
|
1345
1442
|
.object({
|
|
1346
1443
|
data: z.array(Entry).check(z.describe('Activity feed entries for the address.')),
|
|
1444
|
+
meta: z
|
|
1445
|
+
.optional(schema.Meta)
|
|
1446
|
+
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1347
1447
|
nextCursor: Schema.NextCursor,
|
|
1348
1448
|
})
|
|
1349
1449
|
.check(z.describe('A page of human-readable activity for the address.'))
|
|
@@ -1366,6 +1466,8 @@ export namespace schema {
|
|
|
1366
1466
|
export const Query = z
|
|
1367
1467
|
.strictObject({
|
|
1368
1468
|
chainId: Schema.ChainIdQuery,
|
|
1469
|
+
denomination: Schema.Denomination,
|
|
1470
|
+
include: includeQuery,
|
|
1369
1471
|
// Raw-log capture is opt-in: it can add a large `events` array to every
|
|
1370
1472
|
// item, so the default response stays lean.
|
|
1371
1473
|
logs: z
|
|
@@ -1388,12 +1490,15 @@ export namespace schema {
|
|
|
1388
1490
|
export const Response = z
|
|
1389
1491
|
.object({
|
|
1390
1492
|
data: z.array(Item).check(z.describe('Activity items classified from the transaction.')),
|
|
1493
|
+
meta: z
|
|
1494
|
+
.optional(schema.Meta)
|
|
1495
|
+
.check(z.describe('Page-level resources, such as valuation rate provenance.')),
|
|
1391
1496
|
})
|
|
1392
1497
|
.check(z.describe('The human-readable activity that happened on a transaction.'))
|
|
1393
1498
|
}
|
|
1394
1499
|
}
|
|
1395
1500
|
|
|
1396
|
-
/** A token
|
|
1501
|
+
/** A token in the public resource shape (derived from {@link schema.Token}). */
|
|
1397
1502
|
export type Token = z.output<typeof schema.Token>
|
|
1398
1503
|
|
|
1399
1504
|
/** A single classified activity item (derived from {@link schema.Item}). */
|
|
@@ -1402,14 +1507,36 @@ export type Item = z.output<typeof schema.Item>
|
|
|
1402
1507
|
/** A single activity item, or a folded access-key group (derived from {@link schema.Entry}). */
|
|
1403
1508
|
export type Entry = z.output<typeof schema.Entry>
|
|
1404
1509
|
|
|
1510
|
+
type AmountKey = 'destinationAmount' | 'refundAmount' | 'sourceAmount'
|
|
1511
|
+
type TokenKey = 'destinationToken' | 'refund' | 'sourceToken'
|
|
1512
|
+
|
|
1513
|
+
/** Token fields available before RPC metadata resolution. */
|
|
1514
|
+
type UnenrichedToken = {
|
|
1515
|
+
address: Token['address']
|
|
1516
|
+
amount: z.output<typeof Schema.DecimalString>
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
type UnenrichedData<data> = {
|
|
1520
|
+
[key in keyof data as key extends AmountKey ? never : key]: key extends TokenKey
|
|
1521
|
+
? UnenrichedToken | Extract<data[key], undefined>
|
|
1522
|
+
: data[key]
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/** A classified item whose token stubs have not been enriched with structured amounts. */
|
|
1526
|
+
type UnenrichedItem = {
|
|
1527
|
+
[type in Item['type']]: Omit<Extract<Item, { type: type }>, 'data'> & {
|
|
1528
|
+
data: UnenrichedData<Extract<Item, { type: type }>['data']>
|
|
1529
|
+
}
|
|
1530
|
+
}[Item['type']]
|
|
1531
|
+
|
|
1405
1532
|
/**
|
|
1406
1533
|
* A classified item before {@link list} stamps its display {@link Item.title}.
|
|
1407
1534
|
* Internal classifiers build these; `list` derives `title` from `type` via
|
|
1408
1535
|
* {@link titleByType}, so each branch never repeats the human label.
|
|
1409
1536
|
*/
|
|
1410
1537
|
type RawItem = {
|
|
1411
|
-
[type in
|
|
1412
|
-
}[
|
|
1538
|
+
[type in UnenrichedItem['type']]: Omit<Extract<UnenrichedItem, { type: type }>, 'title'>
|
|
1539
|
+
}[UnenrichedItem['type']]
|
|
1413
1540
|
|
|
1414
1541
|
/**
|
|
1415
1542
|
* Human-readable label for each activity {@link Item.type}, mirroring the per-variant
|
|
@@ -1572,6 +1699,12 @@ const maxGroupSize = 50
|
|
|
1572
1699
|
/** Token-bearing keys on an item's `data` payload, walked for enrichment. */
|
|
1573
1700
|
const tokenKeys = ['destinationToken', 'refund', 'sourceToken'] as const
|
|
1574
1701
|
|
|
1702
|
+
const amountKeyByTokenKey = {
|
|
1703
|
+
destinationToken: 'destinationAmount',
|
|
1704
|
+
refund: 'refundAmount',
|
|
1705
|
+
sourceToken: 'sourceAmount',
|
|
1706
|
+
} as const
|
|
1707
|
+
|
|
1575
1708
|
/** The base fields ({@link schema.base}) every activity item carries. */
|
|
1576
1709
|
type Base = {
|
|
1577
1710
|
id: string
|
|
@@ -1665,7 +1798,8 @@ type TxGroup = {
|
|
|
1665
1798
|
* consecutive access-key-signed items into `group` entries. Pagination uses the
|
|
1666
1799
|
* same `(block_num, tx_idx)` keyset cursor the pipeline emits.
|
|
1667
1800
|
*/
|
|
1668
|
-
export function addresses() {
|
|
1801
|
+
export function addresses(options: addresses.Options = {}) {
|
|
1802
|
+
const oracle = options.fx?.oracle ?? FxOracle.ecb()
|
|
1669
1803
|
return new Hono<App.Environment>().get(
|
|
1670
1804
|
'/v1/addresses/:address/activities',
|
|
1671
1805
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -1682,7 +1816,7 @@ export function addresses() {
|
|
|
1682
1816
|
'Get a human-readable feed of what an address has been doing onchain, including payments sent and received, swaps, approvals, and more.',
|
|
1683
1817
|
operationId: 'getAddressActivities',
|
|
1684
1818
|
responses: OpenApi.responses({
|
|
1685
|
-
errors: { 502: 'Tempo could not read activity data from
|
|
1819
|
+
errors: { 502: 'Tempo could not read activity or token data from an upstream service.' },
|
|
1686
1820
|
success: {
|
|
1687
1821
|
description: 'A page of address activity entries.',
|
|
1688
1822
|
schema: schema.getAddressActivities.Response,
|
|
@@ -1712,38 +1846,42 @@ export function addresses() {
|
|
|
1712
1846
|
const { address } = c.req.valid('param')
|
|
1713
1847
|
const query = c.req.valid('query')
|
|
1714
1848
|
|
|
1715
|
-
// Cursor pages
|
|
1716
|
-
|
|
1717
|
-
// default.
|
|
1718
|
-
if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.immutable)
|
|
1849
|
+
// Cursor pages carry current valuations and curated verification data.
|
|
1850
|
+
if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.metadata)
|
|
1719
1851
|
|
|
1720
1852
|
try {
|
|
1721
1853
|
const chainId = query.chainId ?? c.get('chainId')
|
|
1722
1854
|
const provider = tidx({ chainId, tidx: c.get('getTidx')(chainId) })
|
|
1723
|
-
const { items, nextCursor } = await Timing.time(
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1855
|
+
const { items: unenrichedItems, nextCursor } = await Timing.time(
|
|
1856
|
+
c,
|
|
1857
|
+
'address_activities',
|
|
1858
|
+
() =>
|
|
1859
|
+
Store.memoize(
|
|
1860
|
+
() =>
|
|
1861
|
+
list(provider, {
|
|
1862
|
+
address: address as Hex.Hex,
|
|
1863
|
+
cursor: query.cursor,
|
|
1864
|
+
group: query.group,
|
|
1865
|
+
limit: query.limit,
|
|
1866
|
+
logs: query.logs,
|
|
1867
|
+
store: c.get('store'),
|
|
1868
|
+
}),
|
|
1869
|
+
{
|
|
1870
|
+
key: `activities:v1:${chainId}:${address.toLowerCase()}:${query.group ?? false}:${query.logs ?? false}:${query.cursor ?? 'head'}:${query.limit}`,
|
|
1732
1871
|
store: c.get('store'),
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
store: c.get('store'),
|
|
1737
|
-
ttl: Ttl.seconds(15),
|
|
1738
|
-
},
|
|
1739
|
-
),
|
|
1872
|
+
ttl: Ttl.seconds(15),
|
|
1873
|
+
},
|
|
1874
|
+
),
|
|
1740
1875
|
)
|
|
1741
1876
|
|
|
1742
|
-
//
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1877
|
+
// Build immutable token and structured amount copies from RPC metadata.
|
|
1878
|
+
const { items, rates } = await enrichTokens(c, {
|
|
1879
|
+
chainId,
|
|
1880
|
+
denomination: query.denomination ?? 'USD',
|
|
1881
|
+
include: query.include,
|
|
1882
|
+
items: unenrichedItems,
|
|
1883
|
+
oracle,
|
|
1884
|
+
})
|
|
1747
1885
|
|
|
1748
1886
|
// Fold consecutive access-key-signed items into `group` entries only
|
|
1749
1887
|
// when requested; otherwise every item is returned at the root.
|
|
@@ -1752,17 +1890,28 @@ export function addresses() {
|
|
|
1752
1890
|
return c.json(
|
|
1753
1891
|
Response.validated(schema.getAddressActivities.Response, {
|
|
1754
1892
|
data,
|
|
1893
|
+
...(rates ? { meta: { pricing: Valuation.pricing(rates, oracle) } } : {}),
|
|
1755
1894
|
nextCursor,
|
|
1756
1895
|
}),
|
|
1757
1896
|
200,
|
|
1758
1897
|
)
|
|
1759
1898
|
} catch (cause) {
|
|
1899
|
+
if (cause instanceof Valuation.UnsupportedDenominationError)
|
|
1900
|
+
return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
|
|
1760
1901
|
return Response.upstream(c, cause)
|
|
1761
1902
|
}
|
|
1762
1903
|
},
|
|
1763
1904
|
)
|
|
1764
1905
|
}
|
|
1765
1906
|
|
|
1907
|
+
export declare namespace addresses {
|
|
1908
|
+
/** Options for the address-scoped activity handlers. */
|
|
1909
|
+
type Options = {
|
|
1910
|
+
/** FX configuration backing amount valuation. */
|
|
1911
|
+
fx?: Valuation.addresses.Fx | undefined
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1766
1915
|
/**
|
|
1767
1916
|
* Creates transaction-scoped activity handlers, mounted under the
|
|
1768
1917
|
* `/transactions` composer. Exposes `GET /:transactionHash/activities`.
|
|
@@ -1773,7 +1922,8 @@ export function addresses() {
|
|
|
1773
1922
|
* classified item(s) — usually one headline activity — with the full per-tx
|
|
1774
1923
|
* event set available via `logs=true`.
|
|
1775
1924
|
*/
|
|
1776
|
-
export function transactions() {
|
|
1925
|
+
export function transactions(options: transactions.Options = {}) {
|
|
1926
|
+
const oracle = options.fx?.oracle ?? FxOracle.ecb()
|
|
1777
1927
|
return new Hono<App.Environment>().get(
|
|
1778
1928
|
'/v1/transactions/:transactionHash/activities',
|
|
1779
1929
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -1795,7 +1945,7 @@ export function transactions() {
|
|
|
1795
1945
|
description: 'No mined transaction was found for that hash.',
|
|
1796
1946
|
codes: ['transaction_not_found'],
|
|
1797
1947
|
},
|
|
1798
|
-
502: 'Tempo could not read activity data from
|
|
1948
|
+
502: 'Tempo could not read activity or token data from an upstream service.',
|
|
1799
1949
|
},
|
|
1800
1950
|
success: {
|
|
1801
1951
|
description: 'The classified activity for the transaction.',
|
|
@@ -1806,10 +1956,8 @@ export function transactions() {
|
|
|
1806
1956
|
tags: ['Activities'],
|
|
1807
1957
|
}),
|
|
1808
1958
|
Cache.response<App.Environment>({
|
|
1809
|
-
//
|
|
1810
|
-
//
|
|
1811
|
-
// is not cached with a long header that would mask the eventual result;
|
|
1812
|
-
// the handler upgrades the 200 to `immutable` below.
|
|
1959
|
+
// Default to no-store so a 404 does not mask the eventual mined result.
|
|
1960
|
+
// Successful responses use the metadata policy for current valuations.
|
|
1813
1961
|
cacheControl: Cache.policies.noStore,
|
|
1814
1962
|
name: 'tempo-api:transactions:v1',
|
|
1815
1963
|
key: (c) => Cache.urlKey(c, schema.getTransactionActivities.Query),
|
|
@@ -1832,7 +1980,7 @@ export function transactions() {
|
|
|
1832
1980
|
const chainId = query.chainId ?? c.get('chainId')
|
|
1833
1981
|
|
|
1834
1982
|
try {
|
|
1835
|
-
const
|
|
1983
|
+
const unenrichedItems = await Timing.time(c, 'transaction_activities', () =>
|
|
1836
1984
|
Store.memoize(
|
|
1837
1985
|
() =>
|
|
1838
1986
|
getTransactionActivities({
|
|
@@ -1850,31 +1998,49 @@ export function transactions() {
|
|
|
1850
1998
|
),
|
|
1851
1999
|
)
|
|
1852
2000
|
|
|
1853
|
-
if (
|
|
2001
|
+
if (unenrichedItems === null)
|
|
1854
2002
|
return Response.error(c, {
|
|
1855
2003
|
code: 'transaction_not_found',
|
|
1856
2004
|
message: 'Transaction not found',
|
|
1857
2005
|
status: 404,
|
|
1858
2006
|
})
|
|
1859
2007
|
|
|
1860
|
-
//
|
|
1861
|
-
Cache.setPolicy(c, Cache.policies.
|
|
2008
|
+
// Mined activity still carries current valuations and verification data.
|
|
2009
|
+
Cache.setPolicy(c, Cache.policies.metadata)
|
|
1862
2010
|
|
|
1863
|
-
//
|
|
1864
|
-
|
|
1865
|
-
|
|
2011
|
+
// Build immutable token and structured amount copies from RPC metadata.
|
|
2012
|
+
const { items, rates } = await enrichTokens(c, {
|
|
2013
|
+
chainId,
|
|
2014
|
+
denomination: query.denomination ?? 'USD',
|
|
2015
|
+
include: query.include,
|
|
2016
|
+
items: unenrichedItems,
|
|
2017
|
+
oracle,
|
|
2018
|
+
})
|
|
1866
2019
|
|
|
1867
2020
|
return c.json(
|
|
1868
|
-
Response.validated(schema.getTransactionActivities.Response, {
|
|
2021
|
+
Response.validated(schema.getTransactionActivities.Response, {
|
|
2022
|
+
data: items,
|
|
2023
|
+
...(rates ? { meta: { pricing: Valuation.pricing(rates, oracle) } } : {}),
|
|
2024
|
+
}),
|
|
1869
2025
|
200,
|
|
1870
2026
|
)
|
|
1871
2027
|
} catch (cause) {
|
|
2028
|
+
if (cause instanceof Valuation.UnsupportedDenominationError)
|
|
2029
|
+
return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
|
|
1872
2030
|
return Response.upstream(c, cause)
|
|
1873
2031
|
}
|
|
1874
2032
|
},
|
|
1875
2033
|
)
|
|
1876
2034
|
}
|
|
1877
2035
|
|
|
2036
|
+
export declare namespace transactions {
|
|
2037
|
+
/** Options for the transaction-scoped activity handlers. */
|
|
2038
|
+
type Options = {
|
|
2039
|
+
/** FX configuration backing amount valuation. */
|
|
2040
|
+
fx?: Valuation.addresses.Fx | undefined
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
1878
2044
|
/**
|
|
1879
2045
|
* Classifies the activity for a single transaction. Fetches the transaction's
|
|
1880
2046
|
* receipt (for its block position, sender, and fee payer) and its logs, then
|
|
@@ -1887,7 +2053,7 @@ export function transactions() {
|
|
|
1887
2053
|
*/
|
|
1888
2054
|
export async function getTransactionActivities(
|
|
1889
2055
|
options: getTransactionActivities.Options,
|
|
1890
|
-
): Promise<
|
|
2056
|
+
): Promise<UnenrichedItem[] | null> {
|
|
1891
2057
|
const { chainId, logs = false, tidx: tidxClient } = options
|
|
1892
2058
|
const hash = options.transactionHash.toLowerCase()
|
|
1893
2059
|
|
|
@@ -1979,7 +2145,7 @@ export async function list(provider: Provider, options: list.Options): Promise<l
|
|
|
1979
2145
|
const fingerprintMap = await Mpp.fingerprintMap({ store: options.store }).catch(() => ({}))
|
|
1980
2146
|
|
|
1981
2147
|
let currentCursor: string | undefined = cursor
|
|
1982
|
-
const allItems:
|
|
2148
|
+
const allItems: UnenrichedItem[] = []
|
|
1983
2149
|
let hasMore = false
|
|
1984
2150
|
let lastGroup: TxGroup | undefined
|
|
1985
2151
|
|
|
@@ -2005,7 +2171,7 @@ export async function list(provider: Provider, options: list.Options): Promise<l
|
|
|
2005
2171
|
lastGroup = page[page.length - 1]
|
|
2006
2172
|
|
|
2007
2173
|
const enough = group
|
|
2008
|
-
?
|
|
2174
|
+
? groupByAccessKeyItems(allItems).length >= minGroupEntries
|
|
2009
2175
|
: allItems.length >= limit
|
|
2010
2176
|
if (enough || !hasMore) break
|
|
2011
2177
|
|
|
@@ -2015,8 +2181,8 @@ export async function list(provider: Provider, options: list.Options): Promise<l
|
|
|
2015
2181
|
let nextCursor: string | null = null
|
|
2016
2182
|
if (hasMore && lastGroup) nextCursor = provider.cursorForGroup(lastGroup)
|
|
2017
2183
|
|
|
2018
|
-
// Items carry
|
|
2019
|
-
//
|
|
2184
|
+
// Items carry private `address`/`amount` stubs; the caller splits them into
|
|
2185
|
+
// public token references and structured amounts via {@link enrichTokens}.
|
|
2020
2186
|
return { items: allItems, nextCursor }
|
|
2021
2187
|
}
|
|
2022
2188
|
|
|
@@ -2038,7 +2204,7 @@ export declare namespace list {
|
|
|
2038
2204
|
}
|
|
2039
2205
|
/** Result of {@link list}. */
|
|
2040
2206
|
type ReturnType = {
|
|
2041
|
-
items:
|
|
2207
|
+
items: UnenrichedItem[]
|
|
2042
2208
|
nextCursor: string | null
|
|
2043
2209
|
}
|
|
2044
2210
|
}
|
|
@@ -3661,13 +3827,13 @@ function transfer(
|
|
|
3661
3827
|
blockNumber: number,
|
|
3662
3828
|
data: {
|
|
3663
3829
|
attribution?: string | undefined
|
|
3664
|
-
destinationToken?:
|
|
3830
|
+
destinationToken?: UnenrichedToken | undefined
|
|
3665
3831
|
direction: 'in' | 'out'
|
|
3666
3832
|
memo?: string | undefined
|
|
3667
3833
|
recipient: Address.Address
|
|
3668
3834
|
sender: Address.Address
|
|
3669
3835
|
signer?: 'self' | Address.Address | undefined
|
|
3670
|
-
sourceToken:
|
|
3836
|
+
sourceToken: UnenrichedToken
|
|
3671
3837
|
},
|
|
3672
3838
|
): Extract<RawItem, { type: 'transfer' }> {
|
|
3673
3839
|
return {
|
|
@@ -3792,75 +3958,154 @@ function lowerOrNull(value: unknown): Address.Address | null {
|
|
|
3792
3958
|
return text ? (text.toLowerCase() as Address.Address) : null
|
|
3793
3959
|
}
|
|
3794
3960
|
|
|
3795
|
-
/** Builds a stub
|
|
3796
|
-
function token(amount: bigint | undefined, address: Address.Address):
|
|
3961
|
+
/** Builds a token stub whose metadata is filled by {@link enrichItem}. */
|
|
3962
|
+
function token(amount: bigint | undefined, address: Address.Address): UnenrichedToken {
|
|
3797
3963
|
return { address: address.toLowerCase() as Address.Address, amount: (amount ?? 0n).toString() }
|
|
3798
3964
|
}
|
|
3799
3965
|
|
|
3800
3966
|
/** Collects every token address referenced by an item's `data`. */
|
|
3801
|
-
function collectTokens(item:
|
|
3802
|
-
const data = item.data as Partial<Record<(typeof tokenKeys)[number],
|
|
3967
|
+
function collectTokens(item: UnenrichedItem, set: Set<Address.Address>) {
|
|
3968
|
+
const data = item.data as Partial<Record<(typeof tokenKeys)[number], UnenrichedToken>>
|
|
3803
3969
|
for (const key of tokenKeys) {
|
|
3804
3970
|
const t = data[key]
|
|
3805
3971
|
if (t) set.add(t.address.toLowerCase() as Address.Address)
|
|
3806
3972
|
}
|
|
3807
3973
|
}
|
|
3808
3974
|
|
|
3809
|
-
/** Resolved token
|
|
3810
|
-
type ResolvedToken =
|
|
3975
|
+
/** Resolved token fields keyed by lowercased address. */
|
|
3976
|
+
type ResolvedToken = {
|
|
3977
|
+
currency: string
|
|
3978
|
+
decimals: number
|
|
3979
|
+
logoUri?: string | undefined
|
|
3980
|
+
name: string
|
|
3981
|
+
symbol: string
|
|
3982
|
+
verified?: boolean | undefined
|
|
3983
|
+
}
|
|
3811
3984
|
|
|
3812
|
-
/**
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
* whose metadata is unavailable keeps just `address`/`amount`.
|
|
3819
|
-
*/
|
|
3820
|
-
async function enrichTokens(c: Context<App.Environment>, options: enrichTokens.Options) {
|
|
3821
|
-
const { chainId, items } = options
|
|
3985
|
+
/** Resolves core metadata into immutable items. Optional logo and verification sources are best-effort. */
|
|
3986
|
+
async function enrichTokens(
|
|
3987
|
+
c: Context<App.Environment>,
|
|
3988
|
+
options: enrichTokens.Options,
|
|
3989
|
+
): Promise<{ items: Item[]; rates: FxOracle.RateSet | undefined }> {
|
|
3990
|
+
const { chainId, denomination, include, items, oracle } = options
|
|
3822
3991
|
const addresses = new Set<Address.Address>()
|
|
3823
3992
|
for (const item of items) collectTokens(item, addresses)
|
|
3824
|
-
|
|
3993
|
+
// Valuation keys off the curated snapshot; a snapshot failure degrades the
|
|
3994
|
+
// values to `null` rather than failing the feed.
|
|
3995
|
+
const snapshot =
|
|
3996
|
+
addresses.size > 0
|
|
3997
|
+
? await VerifiedTokens.snapshot(c, chainId).catch(() => undefined)
|
|
3998
|
+
: undefined
|
|
3999
|
+
const rates = await Valuation.ratesFor(c, {
|
|
4000
|
+
currencies: snapshot
|
|
4001
|
+
? [...addresses].flatMap((address) => {
|
|
4002
|
+
const held = snapshot.byAddress.get(address)?.currency
|
|
4003
|
+
return held === undefined ? [] : [held]
|
|
4004
|
+
})
|
|
4005
|
+
: [],
|
|
4006
|
+
denomination,
|
|
4007
|
+
oracle,
|
|
4008
|
+
})
|
|
4009
|
+
if (addresses.size === 0) return { items: items as Item[], rates }
|
|
4010
|
+
|
|
4011
|
+
const includeLogoUri = include.includes('token.logoUri')
|
|
4012
|
+
const includeVerified = include.includes('token.verified')
|
|
3825
4013
|
|
|
3826
4014
|
const resolved = await Timing.time(c, 'tokens', () =>
|
|
3827
4015
|
Promise.all(
|
|
3828
|
-
[...addresses].map((address) =>
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
4016
|
+
[...addresses].map(async (address) => {
|
|
4017
|
+
const [logoUri, metadata] = await Promise.all([
|
|
4018
|
+
includeLogoUri
|
|
4019
|
+
? Tokens.getTokenLogo(c, { address, chainId }).catch(() => undefined)
|
|
4020
|
+
: undefined,
|
|
4021
|
+
Tokens.getTokenMetadata(c, { address, chainId }),
|
|
4022
|
+
])
|
|
4023
|
+
return [
|
|
4024
|
+
address.toLowerCase(),
|
|
4025
|
+
{
|
|
4026
|
+
currency: metadata.currency,
|
|
4027
|
+
decimals: metadata.decimals,
|
|
4028
|
+
logoUri: includeLogoUri
|
|
4029
|
+
? (logoUri ?? snapshot?.byAddress.get(address)?.logoUri ?? metadata.logoUri)
|
|
4030
|
+
: undefined,
|
|
4031
|
+
name: metadata.name,
|
|
4032
|
+
symbol: metadata.symbol,
|
|
4033
|
+
verified: includeVerified && snapshot ? snapshot.byAddress.has(address) : undefined,
|
|
4034
|
+
},
|
|
4035
|
+
] as const
|
|
4036
|
+
}),
|
|
3833
4037
|
),
|
|
3834
4038
|
)
|
|
3835
4039
|
const byAddress = new Map(resolved)
|
|
3836
|
-
|
|
4040
|
+
return {
|
|
4041
|
+
items: items.map((item) => enrichItem(item, byAddress, { denomination, rates, snapshot })),
|
|
4042
|
+
rates,
|
|
4043
|
+
}
|
|
3837
4044
|
}
|
|
3838
4045
|
|
|
3839
4046
|
declare namespace enrichTokens {
|
|
3840
4047
|
type Options = {
|
|
3841
4048
|
chainId: z.output<typeof Schema.ChainId>
|
|
3842
|
-
|
|
4049
|
+
/** Currency the amount valuations are denominated in (uppercase). */
|
|
4050
|
+
denomination: string
|
|
4051
|
+
include: readonly z.output<typeof schema.Include>[]
|
|
4052
|
+
items: readonly UnenrichedItem[]
|
|
4053
|
+
/** FX rate oracle backing amount valuation. */
|
|
4054
|
+
oracle: FxOracle.Oracle
|
|
3843
4055
|
}
|
|
3844
4056
|
}
|
|
3845
4057
|
|
|
3846
|
-
/**
|
|
3847
|
-
function enrichItem(
|
|
3848
|
-
|
|
4058
|
+
/** Returns an enriched copy of an item without mutating its cached token stubs. */
|
|
4059
|
+
function enrichItem(
|
|
4060
|
+
item: UnenrichedItem,
|
|
4061
|
+
byAddress: Map<string, ResolvedToken>,
|
|
4062
|
+
valuation: enrichItem.Valuation,
|
|
4063
|
+
): Item {
|
|
4064
|
+
const source = item.data as Partial<Record<(typeof tokenKeys)[number], UnenrichedToken>>
|
|
4065
|
+
const data: Record<string, unknown> = { ...item.data }
|
|
3849
4066
|
for (const key of tokenKeys) {
|
|
3850
|
-
const t =
|
|
4067
|
+
const t = source[key]
|
|
3851
4068
|
if (!t) continue
|
|
3852
4069
|
const meta = byAddress.get(t.address.toLowerCase())
|
|
3853
|
-
if (!meta)
|
|
3854
|
-
|
|
3855
|
-
|
|
4070
|
+
if (!meta) throw new Error(`Token metadata unavailable for ${t.address}`)
|
|
4071
|
+
|
|
4072
|
+
data[amountKeyByTokenKey[key]] = {
|
|
4073
|
+
...Value.tokenAmount({
|
|
4074
|
+
baseUnits: t.amount,
|
|
4075
|
+
currency: meta.currency,
|
|
4076
|
+
decimals: meta.decimals,
|
|
4077
|
+
}),
|
|
4078
|
+
// Valued via the curated display currency, never the on-chain metadata.
|
|
4079
|
+
valuation: Valuation.valuationFor({
|
|
4080
|
+
amount: BigInt(t.amount),
|
|
4081
|
+
denomination: valuation.denomination,
|
|
4082
|
+
rates: valuation.rates,
|
|
4083
|
+
token: valuation.snapshot?.byAddress.get(t.address.toLowerCase()),
|
|
4084
|
+
}),
|
|
4085
|
+
}
|
|
3856
4086
|
data[key] = {
|
|
3857
|
-
|
|
4087
|
+
address: t.address,
|
|
3858
4088
|
currency: meta.currency,
|
|
3859
4089
|
decimals: meta.decimals,
|
|
4090
|
+
...(meta.logoUri !== undefined ? { logoUri: meta.logoUri } : {}),
|
|
3860
4091
|
name: meta.name,
|
|
3861
4092
|
symbol: meta.symbol,
|
|
4093
|
+
...(meta.verified !== undefined ? { verified: meta.verified } : {}),
|
|
3862
4094
|
}
|
|
3863
4095
|
}
|
|
4096
|
+
return { ...item, data } as Item
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
declare namespace enrichItem {
|
|
4100
|
+
/** Valuation inputs threaded from {@link enrichTokens}. */
|
|
4101
|
+
type Valuation = {
|
|
4102
|
+
/** Currency the amount valuations are denominated in (uppercase). */
|
|
4103
|
+
denomination: string
|
|
4104
|
+
/** Oracle rate set, when loaded. */
|
|
4105
|
+
rates: FxOracle.RateSet | undefined
|
|
4106
|
+
/** Compiled verified-token snapshot, when available. */
|
|
4107
|
+
snapshot: VerifiedTokens.Snapshot | undefined
|
|
4108
|
+
}
|
|
3864
4109
|
}
|
|
3865
4110
|
|
|
3866
4111
|
/**
|
|
@@ -3869,8 +4114,31 @@ function enrichItem(item: Item, byAddress: Map<string, ResolvedToken | undefined
|
|
|
3869
4114
|
* Must be called once over the full, timestamp-sorted item list.
|
|
3870
4115
|
*/
|
|
3871
4116
|
export function groupByAccessKey(items: Item[]): Entry[] {
|
|
3872
|
-
|
|
3873
|
-
|
|
4117
|
+
return groupByAccessKeyItems(items)
|
|
4118
|
+
}
|
|
4119
|
+
|
|
4120
|
+
type GroupableItem = {
|
|
4121
|
+
data: object
|
|
4122
|
+
id: string
|
|
4123
|
+
perspective: 'incoming' | 'outgoing'
|
|
4124
|
+
timestamp: string
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
type GroupedItem<item extends GroupableItem> =
|
|
4128
|
+
| item
|
|
4129
|
+
| {
|
|
4130
|
+
data: { items: item[]; signer: Address.Address }
|
|
4131
|
+
id: string
|
|
4132
|
+
perspective: 'incoming' | 'outgoing'
|
|
4133
|
+
timestamp: string
|
|
4134
|
+
title: 'Group'
|
|
4135
|
+
type: 'group'
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
/** Groups items while preserving whether their token data has been enriched. */
|
|
4139
|
+
function groupByAccessKeyItems<item extends GroupableItem>(items: item[]): GroupedItem<item>[] {
|
|
4140
|
+
const entries: GroupedItem<item>[] = []
|
|
4141
|
+
let currentGroup: { items: item[]; signer: Address.Address } | null = null
|
|
3874
4142
|
|
|
3875
4143
|
for (const item of items) {
|
|
3876
4144
|
const itemSigner = signerOf(item)
|
|
@@ -3903,12 +4171,15 @@ export function groupByAccessKey(items: Item[]): Entry[] {
|
|
|
3903
4171
|
}
|
|
3904
4172
|
|
|
3905
4173
|
/** The access-key signer of an item, or `self` when it has no access-key signer. */
|
|
3906
|
-
function signerOf(item:
|
|
4174
|
+
function signerOf(item: GroupableItem): 'self' | Address.Address {
|
|
3907
4175
|
return (item.data as { signer?: 'self' | Address.Address }).signer ?? 'self'
|
|
3908
4176
|
}
|
|
3909
4177
|
|
|
3910
4178
|
/** Unwraps single-item groups; otherwise wraps the items in a `group` entry. */
|
|
3911
|
-
function flushGroup(group: {
|
|
4179
|
+
function flushGroup<item extends GroupableItem>(group: {
|
|
4180
|
+
items: item[]
|
|
4181
|
+
signer: Address.Address
|
|
4182
|
+
}): GroupedItem<item> {
|
|
3912
4183
|
if (group.items.length === 1) return group.items[0]!
|
|
3913
4184
|
const firstItem = group.items[0]!
|
|
3914
4185
|
const lastItem = group.items[group.items.length - 1]!
|