Skip to content

Commit fdc336e

Browse files
author
Roberto Bayardo
committed
handle EIP-1559 blocks & fee burning
1 parent fcd83f4 commit fdc336e

12 files changed

+2559
-7
lines changed

ethereum/client.go

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ const (
4545

4646
maxTraceConcurrency = int64(16) // nolint:gomnd
4747
semaphoreTraceWeight = int64(1) // nolint:gomnd
48+
49+
// eip1559TxType is the EthTypes.Transaction.Type() value that indicates this transaction
50+
// follows EIP-1559.
51+
eip1559TxType = 2
4852
)
4953

5054
// Client allows for querying a set of specific Ethereum endpoints in an
@@ -367,12 +371,19 @@ func (ec *Client) getBlock(
367371
for i, tx := range body.Transactions {
368372
txs[i] = tx.tx
369373
receipt := receipts[i]
370-
gasUsedBig := new(big.Int).SetUint64(receipt.GasUsed)
371-
feeAmount := gasUsedBig.Mul(gasUsedBig, txs[i].GasPrice())
372-
374+
gasUsed := new(big.Int).SetUint64(receipt.GasUsed)
375+
gasPrice, err := effectiveGasPrice(txs[i], head.BaseFee)
376+
if err != nil {
377+
return nil, nil, fmt.Errorf("%w: failure getting effective gas price", err)
378+
}
373379
loadedTxs[i] = tx.LoadedTransaction()
374380
loadedTxs[i].Transaction = txs[i]
375-
loadedTxs[i].FeeAmount = feeAmount
381+
loadedTxs[i].FeeAmount = new(big.Int).Mul(gasUsed, gasPrice)
382+
if head.BaseFee != nil { // EIP-1559
383+
loadedTxs[i].FeeBurned = new(big.Int).Mul(gasUsed, head.BaseFee)
384+
} else {
385+
loadedTxs[i].FeeBurned = nil
386+
}
376387
loadedTxs[i].Miner = MustChecksum(head.Coinbase.Hex())
377388
loadedTxs[i].Receipt = receipt
378389

@@ -388,6 +399,21 @@ func (ec *Client) getBlock(
388399
return types.NewBlockWithHeader(&head).WithBody(txs, uncles), loadedTxs, nil
389400
}
390401

402+
// effectiveGasPrice returns the price of gas charged to this transaction to be included in the
403+
// block.
404+
func effectiveGasPrice(tx *EthTypes.Transaction, baseFee *big.Int) (*big.Int, error) {
405+
if tx.Type() != eip1559TxType {
406+
return tx.GasPrice(), nil
407+
}
408+
// For EIP-1559 the gas price is determined by the base fee & miner tip instead
409+
// of the tx-specified gas price.
410+
tip, err := tx.EffectiveGasTip(baseFee)
411+
if err != nil {
412+
return nil, err
413+
}
414+
return new(big.Int).Add(tip, baseFee), nil
415+
}
416+
391417
func (ec *Client) getBlockTraces(
392418
ctx context.Context,
393419
blockHash common.Hash,
@@ -754,6 +780,7 @@ type loadedTransaction struct {
754780
BlockNumber *string
755781
BlockHash *common.Hash
756782
FeeAmount *big.Int
783+
FeeBurned *big.Int // nil if no fees were burned
757784
Miner string
758785
Status bool
759786

@@ -763,7 +790,13 @@ type loadedTransaction struct {
763790
}
764791

765792
func feeOps(tx *loadedTransaction) []*RosettaTypes.Operation {
766-
return []*RosettaTypes.Operation{
793+
var minerEarnedAmount *big.Int
794+
if tx.FeeBurned == nil {
795+
minerEarnedAmount = tx.FeeAmount
796+
} else {
797+
minerEarnedAmount = new(big.Int).Sub(tx.FeeAmount, tx.FeeBurned)
798+
}
799+
ops := []*RosettaTypes.Operation{
767800
{
768801
OperationIdentifier: &RosettaTypes.OperationIdentifier{
769802
Index: 0,
@@ -774,7 +807,7 @@ func feeOps(tx *loadedTransaction) []*RosettaTypes.Operation {
774807
Address: MustChecksum(tx.From.String()),
775808
},
776809
Amount: &RosettaTypes.Amount{
777-
Value: new(big.Int).Neg(tx.FeeAmount).String(),
810+
Value: new(big.Int).Neg(minerEarnedAmount).String(),
778811
Currency: Currency,
779812
},
780813
},
@@ -794,11 +827,29 @@ func feeOps(tx *loadedTransaction) []*RosettaTypes.Operation {
794827
Address: MustChecksum(tx.Miner),
795828
},
796829
Amount: &RosettaTypes.Amount{
797-
Value: tx.FeeAmount.String(),
830+
Value: minerEarnedAmount.String(),
798831
Currency: Currency,
799832
},
800833
},
801834
}
835+
if tx.FeeBurned == nil {
836+
return ops
837+
}
838+
burntOp := &RosettaTypes.Operation{
839+
OperationIdentifier: &RosettaTypes.OperationIdentifier{
840+
Index: 2,
841+
},
842+
Type: FeeOpType,
843+
Status: RosettaTypes.String(SuccessStatus),
844+
Account: &RosettaTypes.AccountIdentifier{
845+
Address: MustChecksum(tx.From.String()),
846+
},
847+
Amount: &RosettaTypes.Amount{
848+
Value: new(big.Int).Neg(tx.FeeBurned).String(),
849+
Currency: Currency,
850+
},
851+
}
852+
return append(ops, burntOp)
802853
}
803854

804855
// transactionReceipt returns the receipt of a transaction by transaction hash.

ethereum/client_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,122 @@ func TestBlock_468194(t *testing.T) {
22842284
mockGraphQL.AssertExpectations(t)
22852285
}
22862286

2287+
// Block with EIP-1559 base fee & txs. This block taken from mainnet:
2288+
// https://etherscan.io/block/0x68985b6b06bb5c6012393145729babb983fc16c50ec5207972ddda02de02f7e2
2289+
// This block has 7 transactions, all EIP-1559 type except the last.
2290+
func TestBlock_13998626(t *testing.T) {
2291+
mockJSONRPC := &mocks.JSONRPC{}
2292+
mockGraphQL := &mocks.GraphQL{}
2293+
2294+
tc, err := testTraceConfig()
2295+
assert.NoError(t, err)
2296+
c := &Client{
2297+
c: mockJSONRPC,
2298+
g: mockGraphQL,
2299+
tc: tc,
2300+
p: params.RopstenChainConfig,
2301+
traceSemaphore: semaphore.NewWeighted(100),
2302+
}
2303+
2304+
ctx := context.Background()
2305+
mockJSONRPC.On(
2306+
"CallContext",
2307+
ctx,
2308+
mock.Anything,
2309+
"eth_getBlockByNumber",
2310+
"0xd59a22",
2311+
true,
2312+
).Return(
2313+
nil,
2314+
).Run(
2315+
func(args mock.Arguments) {
2316+
r := args.Get(1).(*json.RawMessage)
2317+
2318+
file, err := ioutil.ReadFile("testdata/block_13998626.json")
2319+
assert.NoError(t, err)
2320+
2321+
*r = json.RawMessage(file)
2322+
},
2323+
).Once()
2324+
mockJSONRPC.On(
2325+
"CallContext",
2326+
ctx,
2327+
mock.Anything,
2328+
"debug_traceBlockByHash",
2329+
common.HexToHash("0x68985b6b06bb5c6012393145729babb983fc16c50ec5207972ddda02de02f7e2"),
2330+
tc,
2331+
).Return(
2332+
nil,
2333+
).Run(
2334+
func(args mock.Arguments) {
2335+
r := args.Get(1).(*json.RawMessage)
2336+
2337+
file, err := ioutil.ReadFile(
2338+
"testdata/block_trace_0x68985b6b06bb5c6012393145729babb983fc16c50ec5207972ddda02de02f7e2.json") // nolint
2339+
assert.NoError(t, err)
2340+
*r = json.RawMessage(file)
2341+
},
2342+
).Once()
2343+
mockJSONRPC.On(
2344+
"BatchCallContext",
2345+
ctx,
2346+
mock.Anything,
2347+
).Return(
2348+
nil,
2349+
).Run(
2350+
func(args mock.Arguments) {
2351+
r := args.Get(1).([]rpc.BatchElem)
2352+
assert.Len(t, r, 7)
2353+
for i, txHash := range []string{
2354+
"0xf121c8c07ed51b6ac2d11fe3f0892bff2221ec9168280d12581ea8ff45e71421",
2355+
"0xef0748860f1c1ba28a5ae3ae9d2d1133940f7c8090fc862acf48de42b00ae2b5",
2356+
"0xb240b922161bb0aeaa5ebe67e6cf77311092bd945b9582b8deba61e2ebdde74f",
2357+
"0xfac8149f95c20f62264991fe15dc74ca77c92ad6e4329496548277fb4d520509",
2358+
"0x0a4cd36d72c2ed4767c1d228a7aa0638c3e46397f48b6b09f35ed455c851bb04",
2359+
"0x9ee03d5922b2a901e3fc05d8a6351165b9f211162363c790c98746ef229e395c",
2360+
"0x0d4a4f924858a5b19f6b931a914701d4258e73fa738da3d38eb3be1d1e862a7a",
2361+
} {
2362+
assert.Equal(
2363+
t,
2364+
txHash,
2365+
r[i].Args[0],
2366+
)
2367+
2368+
file, err := ioutil.ReadFile(
2369+
"testdata/tx_receipt_" + txHash + ".json",
2370+
) // nolint
2371+
assert.NoError(t, err)
2372+
2373+
receipt := new(types.Receipt)
2374+
assert.NoError(t, receipt.UnmarshalJSON(file))
2375+
*(r[i].Result.(**types.Receipt)) = receipt
2376+
}
2377+
},
2378+
).Once()
2379+
2380+
correctRaw, err := ioutil.ReadFile("testdata/block_response_13998626.json")
2381+
assert.NoError(t, err)
2382+
var correctResp *RosettaTypes.BlockResponse
2383+
assert.NoError(t, json.Unmarshal(correctRaw, &correctResp))
2384+
2385+
resp, err := c.Block(
2386+
ctx,
2387+
&RosettaTypes.PartialBlockIdentifier{
2388+
Index: RosettaTypes.Int64(13998626),
2389+
},
2390+
)
2391+
assert.NoError(t, err)
2392+
2393+
// Ensure types match
2394+
jsonResp, err := jsonifyBlock(resp)
2395+
2396+
assert.NoError(t, err)
2397+
assert.Equal(t, correctResp.Block, jsonResp)
2398+
2399+
mockJSONRPC.AssertExpectations(t)
2400+
mockGraphQL.AssertExpectations(t)
2401+
}
2402+
22872403
func TestPendingNonceAt(t *testing.T) {
22882404
mockJSONRPC := &mocks.JSONRPC{}
22892405
mockGraphQL := &mocks.GraphQL{}

0 commit comments

Comments
 (0)