@@ -49,6 +49,7 @@ func Transaction(w http.ResponseWriter, r *http.Request) {
4949 "transaction/statechanges.html" ,
5050 "transaction/transfers.html" ,
5151 "transaction/internaltxs.html" ,
52+ "transaction/authorizations.html" ,
5253 "transaction/blobs.html" ,
5354 )
5455 notfoundTemplateFiles := append (layoutTemplateFiles ,
@@ -103,7 +104,7 @@ func Transaction(w http.ResponseWriter, r *http.Request) {
103104
104105 if pageData .TxNotFound {
105106 data := InitPageData (w , r , "blockchain" , "/tx" , "Transaction not found" , notfoundTemplateFiles )
106- data .Data = pageData
107+ data .Data = "notfound"
107108 w .Header ().Set ("Content-Type" , "text/html" )
108109 handleTemplateError (w , r , "transaction.go" , "Transaction" , "notFound" , templates .GetTemplate (notfoundTemplateFiles ... ).ExecuteTemplate (w , "layout" , data ))
109110 return
@@ -423,8 +424,13 @@ func buildTransactionPageDataFromDB(ctx context.Context, pageData *models.Transa
423424 loadTransactionTransfersFromData (ctx , pageData , transfers )
424425 case "internaltxs" :
425426 loadTransactionInternalTxsFromBlockdb (ctx , pageData , tx .BlockUid )
427+ computeInternalTxIndent (pageData )
426428 case "statechanges" :
427429 loadTransactionStateChangesFromBlockdb (ctx , pageData , tx .BlockUid )
430+ case "authorizations" :
431+ if pageData .TxType == ethtypes .SetCodeTxType && len (pageData .Authorizations ) > 0 {
432+ resolveAuthorizationValidity (ctx , pageData , tx .BlockUid )
433+ }
428434 }
429435}
430436
@@ -532,6 +538,11 @@ func buildTransactionPageDataFromEL(ctx context.Context, pageData *models.Transa
532538 // Blob hashes
533539 pageData .BlobCount = uint32 (len (ethTx .BlobHashes ()))
534540
541+ // Authorization data for type 4 (EIP-7702) transactions
542+ if ethTx .Type () == ethtypes .SetCodeTxType {
543+ loadAuthorizationData (pageData , ethTx )
544+ }
545+
535546 // Generate RLP and JSON
536547 if rlpData , err := ethTx .MarshalBinary (); err == nil {
537548 pageData .TxRLP = "0x" + hex .EncodeToString (rlpData )
@@ -1162,6 +1173,35 @@ func loadTransactionInternalTxsFromDB(ctx context.Context, pageData *models.Tran
11621173 }
11631174}
11641175
1176+ // computeInternalTxIndent sets InternalTxIndentPx based on the maximum
1177+ // nesting depth so that deeply nested trees compress to ~300px total.
1178+ func computeInternalTxIndent (pageData * models.TransactionPageData ) {
1179+ if len (pageData .InternalTxs ) == 0 {
1180+ return
1181+ }
1182+
1183+ var maxDepth uint16
1184+ for _ , itx := range pageData .InternalTxs {
1185+ if itx .Depth > maxDepth {
1186+ maxDepth = itx .Depth
1187+ }
1188+ }
1189+
1190+ if maxDepth == 0 {
1191+ pageData .InternalTxIndentPx = 18.0
1192+ return
1193+ }
1194+
1195+ indent := 300.0 / float64 (maxDepth )
1196+ if indent > 18.0 {
1197+ indent = 18.0
1198+ }
1199+ if indent < 2.0 {
1200+ indent = 2.0
1201+ }
1202+ pageData .InternalTxIndentPx = indent
1203+ }
1204+
11651205func loadTransactionTransfersFromData (ctx context.Context , pageData * models.TransactionPageData , transfers []* dbtypes.ElTokenTransfer ) {
11661206 if len (transfers ) == 0 {
11671207 return
@@ -1294,6 +1334,11 @@ func loadFullTransactionData(ctx context.Context, pageData *models.TransactionPa
12941334 if ethTx .Type () == 3 && len (ethTx .BlobHashes ()) > 0 {
12951335 loadBlobData (pageData , & ethTx , blockData )
12961336 }
1337+
1338+ // Load authorization data for type 4 (EIP-7702) transactions
1339+ if ethTx .Type () == ethtypes .SetCodeTxType {
1340+ loadAuthorizationData (pageData , & ethTx )
1341+ }
12971342}
12981343
12991344// loadBlobData populates blob-related data for type 3 (blob) transactions.
@@ -1407,3 +1452,112 @@ func applyCallTargetResolution(ctx context.Context, pageData *models.Transaction
14071452 pageData .MethodID = res .MethodID
14081453 }
14091454}
1455+
1456+ // loadAuthorizationData extracts EIP-7702 authorization list entries from a
1457+ // parsed transaction and populates pageData.Authorizations.
1458+ func loadAuthorizationData (
1459+ pageData * models.TransactionPageData ,
1460+ ethTx * ethtypes.Transaction ,
1461+ ) {
1462+ authList := ethTx .SetCodeAuthorizations ()
1463+ if len (authList ) == 0 {
1464+ return
1465+ }
1466+
1467+ pageData .Authorizations = make (
1468+ []* models.TransactionPageDataAuthorization ,
1469+ len (authList ),
1470+ )
1471+
1472+ for i := range authList {
1473+ auth := & authList [i ]
1474+ entry := & models.TransactionPageDataAuthorization {
1475+ Index : uint32 (i ),
1476+ DelegateAddr : auth .Address .Bytes (),
1477+ }
1478+
1479+ if authority , err := auth .Authority (); err == nil {
1480+ entry .AuthorityAddr = authority .Bytes ()
1481+ entry .AuthorityOk = true
1482+ }
1483+
1484+ pageData .Authorizations [i ] = entry
1485+ }
1486+ }
1487+
1488+ // resolveAuthorizationValidity loads state diffs from blockdb and checks
1489+ // whether each EIP-7702 authorization was actually applied on-chain.
1490+ // An authorization is considered applied when the authority address has a code
1491+ // change whose post-state matches the delegation designator (0xef0100 + delegate).
1492+ func resolveAuthorizationValidity (
1493+ ctx context.Context ,
1494+ pageData * models.TransactionPageData ,
1495+ blockUid uint64 ,
1496+ ) {
1497+ if pageData .DataStatus & dbtypes .ElBlockDataStateChanges == 0 {
1498+ return
1499+ }
1500+
1501+ if blockdb .GlobalBlockDb == nil || ! blockdb .GlobalBlockDb .SupportsExecData () {
1502+ return
1503+ }
1504+
1505+ slot := blockUid >> 16
1506+ blockRoot := pageData .BlockRoot
1507+ if len (blockRoot ) == 0 {
1508+ return
1509+ }
1510+
1511+ ctx , cancel := context .WithTimeout (ctx , 10 * time .Second )
1512+ defer cancel ()
1513+
1514+ sections , err := blockdb .GlobalBlockDb .GetExecDataTxSections (
1515+ ctx , slot , blockRoot , pageData .TxHash ,
1516+ bdbtypes .ExecDataSectionStateChange ,
1517+ )
1518+ if err != nil || sections == nil || sections .StateChangeData == nil {
1519+ return
1520+ }
1521+
1522+ uncompData , err := snappy .Decode (nil , sections .StateChangeData )
1523+ if err != nil {
1524+ return
1525+ }
1526+
1527+ var accounts []bdbtypes.StateChangeAccount
1528+ if err := dynssz .GetGlobalDynSsz ().UnmarshalSSZ (& accounts , uncompData ); err != nil {
1529+ return
1530+ }
1531+
1532+ // Build a lookup of address -> post-code for accounts with code changes.
1533+ type codeInfo struct {
1534+ postCode []byte
1535+ }
1536+ codeByAddr := make (map [common.Address ]codeInfo , len (accounts ))
1537+ for i := range accounts {
1538+ a := & accounts [i ]
1539+ if (a .Flags & bdbtypes .StateChangeFlagCodeChanged ) != 0 {
1540+ codeByAddr [a .Address ] = codeInfo {postCode : a .PostCode }
1541+ }
1542+ }
1543+
1544+ for _ , auth := range pageData .Authorizations {
1545+ if ! auth .AuthorityOk {
1546+ continue
1547+ }
1548+
1549+ authorityAddr := common .BytesToAddress (auth .AuthorityAddr )
1550+ ci , found := codeByAddr [authorityAddr ]
1551+ if ! found {
1552+ auth .Applied = 2 // not applied
1553+ continue
1554+ }
1555+
1556+ delegateAddr , ok := ethtypes .ParseDelegation (ci .postCode )
1557+ if ok && delegateAddr == common .BytesToAddress (auth .DelegateAddr ) {
1558+ auth .Applied = 1 // applied
1559+ } else {
1560+ auth .Applied = 2 // not applied
1561+ }
1562+ }
1563+ }
0 commit comments