Skip to content

Commit ce5ef9f

Browse files
authored
Merge pull request #756 from ethpandaops/qu0b/dora/glamsterdam-devnet-6-calldata-size
feat(dora): calldata gas breakdown — standard / EIP-7623 (Prague) / EIP-7976 (Amsterdam)
2 parents 6753054 + 46f1edc commit ce5ef9f

3 files changed

Lines changed: 55 additions & 1 deletion

File tree

handlers/transaction.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ func buildTransactionPageDataFromEL(ctx context.Context, pageData *models.Transa
533533

534534
// Input data
535535
pageData.InputData = ethTx.Data()
536+
applyCalldataCosts(pageData)
536537
methodID := []byte(nil)
537538
if len(ethTx.Data()) >= 4 {
538539
methodID = ethTx.Data()[:4]
@@ -1315,6 +1316,7 @@ func loadFullTransactionData(ctx context.Context, pageData *models.TransactionPa
13151316

13161317
// Set input data from parsed transaction
13171318
pageData.InputData = ethTx.Data()
1319+
applyCalldataCosts(pageData)
13181320

13191321
// EIP-7976: calldata floor gas = 21000 + 64 × len(calldata)
13201322
if len(pageData.InputData) > 0 {
@@ -1446,6 +1448,35 @@ func applyCallTargetResolution(ctx context.Context, pageData *models.Transaction
14461448
}
14471449
}
14481450

1451+
// applyCalldataCosts computes calldata gas cost fields from pageData.InputData.
1452+
// Covers three pricing regimes: pre-Prague standard, EIP-7623 (Prague floor), EIP-7976 (Amsterdam floor).
1453+
// Must be called after InputData is set.
1454+
func applyCalldataCosts(pageData *models.TransactionPageData) {
1455+
data := pageData.InputData
1456+
if len(data) == 0 {
1457+
return
1458+
}
1459+
z := 0
1460+
for _, b := range data {
1461+
if b == 0 {
1462+
z++
1463+
}
1464+
}
1465+
nz := len(data) - z
1466+
total := uint64(len(data))
1467+
1468+
tokens := nz*4 + z
1469+
pageData.CalldataZeroBytes = z
1470+
pageData.CalldataNonZeroBytes = nz
1471+
pageData.CalldataPragueTokens = tokens
1472+
// Standard intrinsic (pre-Prague): TX_BASE + 4×zero + 16×nonzero
1473+
pageData.CalldataStandardGas = 21000 + uint64(z)*4 + uint64(nz)*16
1474+
// EIP-7623 floor (Prague+): tokens = 4×nonzero + zero; floor = TX_BASE + tokens×10
1475+
pageData.CalldataPragueFloor = 21000 + uint64(tokens)*10
1476+
// EIP-7976 floor (Amsterdam+): flat 64 gas per byte regardless of zero/nonzero
1477+
pageData.CalldataAmsterdamFloor = 21000 + total*64
1478+
}
1479+
14491480
// loadAuthorizationData extracts EIP-7702 authorization list entries from a
14501481
// parsed transaction and populates pageData.Authorizations.
14511482
func loadAuthorizationData(

templates/transaction/transaction.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,16 @@ <h5 class="mb-0"><i class="fas fa-info-circle me-2"></i>Overview</h5>
284284
{{ end }}
285285
{{ if gt (len .InputData) 0 }}
286286
<div class="row p-2 mx-0">
287-
<div class="col-md-3"><span data-bs-toggle="tooltip" data-bs-placement="top" title="Transaction input data">Input Data:</span></div>
287+
<div class="col-md-3"><span data-bs-toggle="tooltip" data-bs-placement="top" title="Transaction input data ({{ .CalldataZeroBytes }} zero bytes, {{ .CalldataNonZeroBytes }} non-zero bytes)">Input Data:</span> <span class="badge bg-secondary ms-1">{{ formatNumber (len .InputData) }} bytes</span> <small class="text-muted" style="font-size:0.75em;">{{ .CalldataZeroBytes }}×0 / {{ .CalldataNonZeroBytes }}×≠0</small></div>
288288
<div class="col-md-9">
289289
<div class="d-flex justify-content-between align-items-center mb-2">
290290
{{ if eq .TargetCallType "deploy" }}
291291
<span class="badge rounded-pill text-bg-warning">Contract Creation</span>
292+
{{ if gt (len .InputData) 49152 }}
293+
<span class="badge rounded-pill text-bg-danger ms-1" title="Initcode ({{ formatNumber (len .InputData) }} bytes) exceeds Prague MAX_INIT_CODE_SIZE=49152. This deployment requires EIP-7954 (Amsterdam MAX_INIT_CODE_SIZE=131072).">EIP-7954</span>
294+
{{ else if gt (len .InputData) 24576 }}
295+
<span class="badge rounded-pill text-bg-warning ms-1" title="Initcode ({{ formatNumber (len .InputData) }} bytes) may deploy code exceeding Prague MAX_CODE_SIZE=24576. EIP-7954 (Amsterdam) raises the limit to 65536.">EIP-7954</span>
296+
{{ end }}
292297
{{ else if eq .TargetCallType "precompile" }}
293298
<span class="badge rounded-pill text-bg-info">Precompile: {{ .TargetCallName }}</span>
294299
{{ else if eq .TargetCallType "system" }}
@@ -332,6 +337,16 @@ <h5 class="mb-0"><i class="fas fa-info-circle me-2"></i>Overview</h5>
332337
<div id="input-data-ascii" class="border rounded p-2 text-monospace text-break" style="max-height: 200px; overflow-y: auto; font-size: 0.85rem; background-color: var(--bs-tertiary-bg); display: none; white-space: pre-wrap;"></div>
333338
</div>
334339
</div>
340+
<div class="row border-bottom p-2 mx-0">
341+
<div class="col-md-3 text-muted" style="font-size:0.85em; padding-top:0.2rem;"><span data-bs-toggle="tooltip" data-bs-placement="top" title="Minimum intrinsic gas required by this transaction&#39;s calldata under each fork&#39;s pricing rules. Does not include execution gas.">Calldata gas:</span></div>
342+
<div class="col-md-9 d-flex flex-wrap align-items-center gap-1" style="font-size:0.85em;">
343+
<span class="badge text-bg-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Pre-Prague standard pricing: 4 gas per zero byte, 16 gas per non-zero byte&#xA;= 21,000 + 4×{{ .CalldataZeroBytes }} + 16×{{ .CalldataNonZeroBytes }} = {{ formatAddCommas .CalldataStandardGas }} gas">pre-Prague&nbsp;{{ formatAddCommas .CalldataStandardGas }}</span>
344+
<span class="text-muted"></span>
345+
<span class="badge text-bg-info" data-bs-toggle="tooltip" data-bs-placement="top" title="EIP-7623 floor (Prague / Electra+): tokens = 4×nonzero + zero = {{ .CalldataPragueTokens }} tokens&#xA;floor = 21,000 + {{ .CalldataPragueTokens }}×10 = {{ formatAddCommas .CalldataPragueFloor }} gas&#xA;Always ≥ standard; data-heavy txs pay proportionally more.">EIP-7623&nbsp;{{ formatAddCommas .CalldataPragueFloor }}</span>
346+
<span class="text-muted"></span>
347+
<span class="badge text-bg-warning text-dark" data-bs-toggle="tooltip" data-bs-placement="top" title="EIP-7976 floor (Amsterdam+): flat 64 gas per byte (all bytes equal, zero or not)&#xA;= 21,000 + {{ len .InputData }}×64 = {{ formatAddCommas .CalldataAmsterdamFloor }} gas&#xA;Supersedes EIP-7623; especially raises cost for zero-heavy calldata.">EIP-7976&nbsp;{{ formatAddCommas .CalldataAmsterdamFloor }}</span>
348+
</div>
349+
</div>
335350
{{ end }}
336351
</div>
337352
</div>

types/models/transaction.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ type TransactionPageData struct {
9898
TargetCallName string `json:"target_call_name"` // Precompile or system contract name
9999
DecodedCalldata []*utils.DecodedCalldataParam `json:"decoded_calldata"` // Decoded params (nil if not available)
100100

101+
// Calldata gas cost breakdown (computed from InputData; zero when InputData is empty)
102+
CalldataZeroBytes int `json:"calldata_zero_bytes"` // Zero bytes in calldata
103+
CalldataNonZeroBytes int `json:"calldata_nonzero_bytes"` // Non-zero bytes in calldata
104+
CalldataPragueTokens int `json:"calldata_prague_tokens"` // EIP-7623 tokens: 4×nonzero + zero (used as prague floor multiplier)
105+
CalldataStandardGas uint64 `json:"calldata_standard_gas"` // Intrinsic pre-Prague: 21000 + 4×zero + 16×nonzero
106+
CalldataPragueFloor uint64 `json:"calldata_prague_floor"` // EIP-7623 floor (Prague+): 21000 + (4×nonzero + zero)×10
107+
CalldataAmsterdamFloor uint64 `json:"calldata_amsterdam_floor"` // EIP-7976 floor (Amsterdam+): 21000 + total×64
108+
101109
// Full transaction data (loaded from beacon block)
102110
TxRLP string `json:"tx_rlp"` // Hex-encoded RLP for copy button
103111
TxJSON string `json:"tx_json"` // JSON representation for copy button

0 commit comments

Comments
 (0)