Skip to content

Commit ee4b368

Browse files
tom2drumclaude
andcommitted
Display the fee payer and batched calls on the transaction details page (#3611)
* docs: spec for displaying fee payer and calls on the tx details page Refs #3607 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Display the fee payer and batched calls on the transaction details page Eden transactions of type 0x76 are sponsored batches: an executor submits an ordered list of calls and a separate sponsor pays the fee. Render both fields inside the collapsible details, gated on their presence in the response. Pin @blockscout/api-types to a beta built from the backend `dev` branch once `master` had been merged into it, which is the first published build carrying the eden chain type alongside the response shorthands the app relies on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Tag a sponsored transaction in the details page header Transaction lists have no room for another badge, so `sponsored_transaction` is listed last in TYPES_ORDER with no label of its own. Without an entry there it scores -1 and sorts ahead of every real type, masking labels like "Contract call". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 1a39a49 commit ee4b368

14 files changed

Lines changed: 444 additions & 6 deletions

File tree

.agents/GLOSSARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ vars are documented in `docs/ENVS.md`. Architectural concepts like
3636
| **Connect Wallet** | feature | Lets users write to contracts, sign transactions, and connect a wallet to the explorer. Previously named `blockchain-interaction`; the current config key is `connectWallet`. Distinct from **Web3 Wallet**. |
3737
| **Dispute Games** | entity | Part of the Optimism **Fault Proof System**. On-chain games used to challenge and resolve disputed L2 output roots. |
3838
| **Easter Eggs** | feature | Hidden mini-games wired to claim links for badge rewards. |
39+
| **Eden** | chain | A rollup built on `ev-reth` / evstack. Introduces the **Sponsored Transaction** type. |
3940
| **Epoch** | entity | A consensus time period specific to **Celo**. Has its own index and detail pages. Always refers to a Celo epoch in this codebase — not a generic blockchain concept. |
4041
| **Fault Proof System** | feature | Optimism's mechanism for proving the correctness of L2 state transitions on L1 via **Dispute Games**. |
4142
| **Flashblocks** | feature | MegaETH's sub-second block streaming mechanism. |
@@ -52,6 +53,7 @@ vars are documented in `docs/ENVS.md`. Architectural concepts like
5253
| **Rewards** | feature | The Blockscout Merits program — a token rewards and incentives system operated by Blockscout. Entirely distinct from **Block Reward** (on-chain block-producer payouts). |
5354
| **Rollup** | concept | A chain that settles transactions on a parent (L1) chain. Introduces specific entities: deposits, withdrawals, transaction batches, output roots. Contrast with **Chain Variant**. |
5455
| **SolidityScan** | service | Third-party smart contract security vulnerability scanner integrated into contract detail pages. |
56+
| **Sponsored Transaction** | entity | **Eden**-specific transaction type (EIP-2718 type `0x76`): an executor submits an ordered batch of calls, while a separate sponsor signs for and pays the fee. No equivalent on standard EVM chains. |
5557
| **SUAVE** | chain | MEV-focused chain developed by Flashbots, built around a trusted execution environment (TEE) architecture. Introduces the **Kettle** entity. |
5658
| **TAC (Ton Application Chain)** | chain | A chain that bridges the TON blockchain and EVM ecosystems. Introduces the **Operation** entity. |
5759
| **Tx Actions** | feature | Structured per-transaction action breakdown rendered on the tx details page — a first-party Blockscout interpretation of what a tx did. Distinct from **Tx Interpretation** (natural-language summary) and from raw calldata. |

.agents/tasks/3607-tx-details-fee-payer-calls/spec.md

Lines changed: 275 additions & 0 deletions
Large diffs are not rendered by default.

.claude/launch.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
"runtimeArgs": ["dev:preset", "staging"],
88
"port": 3000
99
},
10+
{
11+
"name": "dev-eden-testnet",
12+
"runtimeExecutable": "pnpm",
13+
"runtimeArgs": ["dev:preset", "eden_testnet"],
14+
"port": 3000
15+
},
1016
{
1117
"name": "dev-eth",
1218
"runtimeExecutable": "pnpm",

.github/workflows/deploy-review.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ on:
3030
- blackfort_testnet
3131
- celo
3232
- celo_sepolia
33+
- eden_testnet
3334
- eth
3435
- eth_sepolia
3536
- filecoin

.vscode/tasks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@
328328
"blackfort_testnet",
329329
"celo",
330330
"celo_sepolia",
331+
"eden_testnet",
331332
"eth",
332333
"eth_sepolia",
333334
"filecoin",

cspell.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"Emelyanov",
116116
"Enkrypt",
117117
"esbuild",
118+
"evstack",
118119
"explorable",
119120
"facebookexternalhit",
120121
"favicons",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
},
5454
"dependencies": {
5555
"@blockscout/admin-rs-types": "1.5.0",
56-
"@blockscout/api-types": "0.0.1-beta.82839e44ce",
56+
"@blockscout/api-types": "0.0.1-beta.8e1692a",
5757
"@blockscout/bens-types": "1.7.1",
5858
"@blockscout/contracts-info-types": "1.5.2",
5959
"@blockscout/interchain-indexer-types": "1.6.0",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-License-Identifier: LicenseRef-Blockscout
2+
3+
import { Flex, Grid } from '@chakra-ui/react';
4+
import React from 'react';
5+
6+
import type { schemas } from '@blockscout/api-types';
7+
8+
import AddressEntity from 'src/slices/address/components/entity/AddressEntity';
9+
10+
import * as DetailedInfo from 'src/shared/detailed-info/DetailedInfo';
11+
import CopyToClipboard from 'src/shared/texts/CopyToClipboard';
12+
import NativeCoinValue from 'src/shared/values/entity/NativeCoinValue';
13+
14+
import { Skeleton } from 'src/toolkit/chakra/skeleton';
15+
import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText';
16+
17+
/** One call of a sponsored batch transaction. `to` is `null` for a contract-creation call. */
18+
type TransactionEdenCall = NonNullable<schemas['TransactionResponse']['calls']>[number];
19+
20+
interface Props {
21+
data: schemas['TransactionResponse'];
22+
isLoading?: boolean;
23+
}
24+
25+
const HeaderItem = ({ children, isLoading }: { children: React.ReactNode; isLoading?: boolean }) => {
26+
return (
27+
<Skeleton
28+
fontWeight="semibold"
29+
pb={ 1 }
30+
loading={ isLoading }
31+
>
32+
{ children }
33+
</Skeleton>
34+
);
35+
};
36+
37+
const CallRow = ({ to, value, input, isLoading }: TransactionEdenCall & { isLoading?: boolean }) => {
38+
return (
39+
<>
40+
<div>
41+
{ to ?
42+
<AddressEntity address={{ hash: to }} isLoading={ isLoading }/> :
43+
<Skeleton loading={ isLoading } display="inline-block"><span>[ Contract creation ]</span></Skeleton>
44+
}
45+
</div>
46+
<div>
47+
<NativeCoinValue amount={ value } loading={ isLoading }/>
48+
</div>
49+
<Flex alignItems="flex-start" whiteSpace="normal" wordBreak="break-all">
50+
<TruncatedText text={ input } loading={ isLoading }/>
51+
<CopyToClipboard text={ input } isLoading={ isLoading }/>
52+
</Flex>
53+
</>
54+
);
55+
};
56+
57+
const TxDetailsEden = ({ data, isLoading }: Props) => {
58+
const { fee_payer: feePayer, calls } = data;
59+
60+
if (!feePayer && !calls?.length) {
61+
return null;
62+
}
63+
64+
return (
65+
<>
66+
{ feePayer && (
67+
<>
68+
<DetailedInfo.ItemLabel
69+
hint="Address that paid the transaction fee on behalf of the sender"
70+
isLoading={ isLoading }
71+
>
72+
Fee payer
73+
</DetailedInfo.ItemLabel>
74+
<DetailedInfo.ItemValue>
75+
<AddressEntity address={ feePayer } isLoading={ isLoading }/>
76+
</DetailedInfo.ItemValue>
77+
</>
78+
) }
79+
80+
{ calls && calls.length > 0 && (
81+
<>
82+
<DetailedInfo.ItemLabel
83+
hint="Ordered list of calls batched into this sponsored transaction"
84+
isLoading={ isLoading }
85+
>
86+
Calls
87+
</DetailedInfo.ItemLabel>
88+
<DetailedInfo.ItemValue alignItems="flex-start" flexWrap="wrap">
89+
<Grid
90+
gridTemplateColumns="minmax(140px, 1fr) minmax(50px, 1fr) 1fr"
91+
textStyle="sm"
92+
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
93+
p={ 4 }
94+
mt={ 2 }
95+
w="100%"
96+
columnGap={ 5 }
97+
rowGap={ 5 }
98+
borderRadius="md"
99+
>
100+
<HeaderItem isLoading={ isLoading }>To</HeaderItem>
101+
<HeaderItem isLoading={ isLoading }>Value</HeaderItem>
102+
<HeaderItem isLoading={ isLoading }>Input</HeaderItem>
103+
{ calls.map((call, index) => (
104+
// a batch can repeat the same call, so the position in the batch is the only stable key
105+
<CallRow key={ index } { ...call } isLoading={ isLoading }/>
106+
)) }
107+
</Grid>
108+
</DetailedInfo.ItemValue>
109+
</>
110+
) }
111+
</>
112+
);
113+
};
114+
115+
export default React.memo(TxDetailsEden);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @vitest-environment jsdom
2+
// SPDX-License-Identifier: LicenseRef-Blockscout
3+
4+
import React from 'react';
5+
6+
import { afterEach, describe, expect, it } from 'vitest';
7+
import { cleanup, render, screen } from 'vitest/lib';
8+
9+
import TxType from './TxType';
10+
11+
describe('TxType', () => {
12+
afterEach(cleanup);
13+
14+
it('prefers a more informative type over sponsored_transaction', () => {
15+
render(<TxType types={ [ 'sponsored_transaction', 'contract_call' ] }/>);
16+
17+
expect(screen.queryByText('Contract call')).not.toBeNull();
18+
});
19+
20+
it('falls back to the generic label when a transaction is only sponsored', () => {
21+
render(<TxType types={ [ 'sponsored_transaction' ] }/>);
22+
23+
expect(screen.queryByText('Transaction')).not.toBeNull();
24+
});
25+
});

0 commit comments

Comments
 (0)