Skip to content

Commit 7a7e10a

Browse files
authored
feat(RPC): add authorizationList from eth_getTransactionByHash response for EIP-7702 transactions (#725) (#731)
* feat(RPC): add authorizationList from eth_getTransactionByHash result * test: unit * fix: tests * fix: coverage * doc: changelog * fix: review
1 parent 0c3bf44 commit 7a7e10a

File tree

6 files changed

+539
-29
lines changed

6 files changed

+539
-29
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
3535

3636
# Changelog
3737

38+
## Unreleased
39+
40+
* (evm) [#725](https://github.com/crypto-org-chain/ethermint/pull/725) feat(RPC): add authorizationList from eth_getTransactionByHash response for EIP-7702 transactions
41+
3842
## [v0.22.0] - 2025-08-12
3943

4044
* (geth) [#665](https://github.com/crypto-org-chain/ethermint/pull/665) Update go-ethereum version to [`v1.15.11`](https://github.com/ethereum/go-ethereum/releases/tag/v1.15.11).

rpc/backend/tx_info_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import (
1212
dbm "github.com/cosmos/cosmos-db"
1313
"github.com/ethereum/go-ethereum/common"
1414
"github.com/ethereum/go-ethereum/common/hexutil"
15+
ethtypes "github.com/ethereum/go-ethereum/core/types"
1516
"github.com/evmos/ethermint/indexer"
1617
"github.com/evmos/ethermint/rpc/backend/mocks"
1718
rpctypes "github.com/evmos/ethermint/rpc/types"
1819
ethermint "github.com/evmos/ethermint/types"
1920
evmtypes "github.com/evmos/ethermint/x/evm/types"
21+
"github.com/holiman/uint256"
2022
"google.golang.org/grpc/metadata"
2123
)
2224

@@ -648,3 +650,114 @@ func (suite *BackendTestSuite) TestGetGasUsed() {
648650
})
649651
}
650652
}
653+
654+
func (suite *BackendTestSuite) TestGetTransactionByHash_SetCodeTxType() {
655+
msgSetCodeTx := suite.buildSetCodeTx()
656+
txBz := suite.signAndEncodeEthTx(msgSetCodeTx)
657+
txHash := msgSetCodeTx.Hash()
658+
block := &types.Block{Header: types.Header{Height: 1, ChainID: "test"}, Data: types.Data{Txs: []types.Tx{txBz}}}
659+
responseDeliver := []*abci.ExecTxResult{
660+
{
661+
Code: 0,
662+
Events: []abci.Event{
663+
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
664+
{Key: "ethereumTxHash", Value: txHash.Hex()},
665+
{Key: "txIndex", Value: "0"},
666+
{Key: "amount", Value: "0"},
667+
{Key: "txGasUsed", Value: "100000"},
668+
{Key: "txHash", Value: ""},
669+
{Key: "recipient", Value: "0x742d35cc6561c9d8f6b1b8e6e2c8b9f8f4a1e2d3"},
670+
}},
671+
},
672+
},
673+
}
674+
675+
expectedRPCTx, _ := rpctypes.NewRPCTransaction(msgSetCodeTx, common.Hash{}, 0, 0, big.NewInt(1), suite.backend.chainID)
676+
677+
testCases := []struct {
678+
name string
679+
registerMock func()
680+
expPass bool
681+
}{
682+
{
683+
"pass - SetCodeTx transaction found and returned",
684+
func() {
685+
client := suite.backend.clientCtx.Client.(*mocks.Client)
686+
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
687+
RegisterBlock(client, 1, txBz)
688+
RegisterBlockResults(client, 1)
689+
RegisterBaseFee(queryClient, sdkmath.NewInt(1))
690+
},
691+
true,
692+
},
693+
}
694+
695+
for _, tc := range testCases {
696+
suite.Run(tc.name, func() {
697+
suite.SetupTest()
698+
tc.registerMock()
699+
700+
db := dbm.NewMemDB()
701+
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
702+
err := suite.backend.indexer.IndexBlock(block, responseDeliver)
703+
suite.Require().NoError(err)
704+
705+
rpcTx, err := suite.backend.GetTransactionByHash(txHash)
706+
707+
if tc.expPass {
708+
suite.Require().NoError(err)
709+
suite.Require().NotNil(rpcTx)
710+
711+
suite.Require().Equal(hexutil.Uint64(ethtypes.SetCodeTxType), rpcTx.Type)
712+
713+
suite.Require().NotNil(rpcTx.GasFeeCap, "GasFeeCap should be set")
714+
suite.Require().NotNil(rpcTx.GasTipCap, "GasTipCap should be set")
715+
suite.Require().NotNil(rpcTx.ChainID, "ChainID should be set")
716+
suite.Require().NotNil(rpcTx.Accesses, "AccessList should be set")
717+
suite.Require().NotNil(rpcTx.AuthorizationList, "AuthorizationList should be set")
718+
719+
suite.Require().Equal(expectedRPCTx.Type, rpcTx.Type)
720+
suite.Require().Equal(expectedRPCTx.From, rpcTx.From)
721+
suite.Require().Equal(expectedRPCTx.Hash, rpcTx.Hash)
722+
} else {
723+
suite.Require().Error(err)
724+
}
725+
})
726+
}
727+
}
728+
729+
func (suite *BackendTestSuite) buildSetCodeTx() *evmtypes.MsgEthereumTx {
730+
auth := ethtypes.SetCodeAuthorization{
731+
ChainID: *uint256.MustFromBig(suite.backend.chainID),
732+
Address: common.HexToAddress("0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9"),
733+
Nonce: 1,
734+
V: uint8(27),
735+
R: *uint256.NewInt(1),
736+
S: *uint256.NewInt(1),
737+
}
738+
739+
setCodeTx := &ethtypes.SetCodeTx{
740+
ChainID: uint256.MustFromBig(suite.backend.chainID),
741+
Nonce: 0,
742+
GasTipCap: uint256.NewInt(10000),
743+
GasFeeCap: uint256.NewInt(1000000000000),
744+
Gas: 100000,
745+
To: common.HexToAddress("0x742d35cc6561c9d8f6b1b8e6e2c8b9f8f4a1e2d3"),
746+
Value: uint256.NewInt(0),
747+
Data: []byte{},
748+
AccessList: ethtypes.AccessList{},
749+
AuthList: []ethtypes.SetCodeAuthorization{auth},
750+
V: uint256.NewInt(1),
751+
R: uint256.NewInt(1),
752+
S: uint256.NewInt(1),
753+
}
754+
755+
ethTx := ethtypes.NewTx(setCodeTx)
756+
msgEthereumTx := &evmtypes.MsgEthereumTx{}
757+
err := msgEthereumTx.FromSignedEthereumTx(ethTx, ethtypes.LatestSignerForChainID(suite.backend.chainID))
758+
suite.Require().NoError(err)
759+
760+
msgEthereumTx.From = suite.signerAddress
761+
762+
return msgEthereumTx
763+
}

rpc/types/types.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,27 @@ type StorageResult struct {
5353

5454
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
5555
type RPCTransaction struct {
56-
BlockHash *common.Hash `json:"blockHash"`
57-
BlockNumber *hexutil.Big `json:"blockNumber"`
58-
From common.Address `json:"from"`
59-
Gas hexutil.Uint64 `json:"gas"`
60-
GasPrice *hexutil.Big `json:"gasPrice"`
61-
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
62-
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
63-
Hash common.Hash `json:"hash"`
64-
Input hexutil.Bytes `json:"input"`
65-
Nonce hexutil.Uint64 `json:"nonce"`
66-
To *common.Address `json:"to"`
67-
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
68-
Value *hexutil.Big `json:"value"`
69-
Type hexutil.Uint64 `json:"type"`
70-
Accesses *ethtypes.AccessList `json:"accessList,omitempty"`
71-
ChainID *hexutil.Big `json:"chainId,omitempty"`
72-
V *hexutil.Big `json:"v"`
73-
R *hexutil.Big `json:"r"`
74-
S *hexutil.Big `json:"s"`
56+
BlockHash *common.Hash `json:"blockHash"`
57+
BlockNumber *hexutil.Big `json:"blockNumber"`
58+
From common.Address `json:"from"`
59+
Gas hexutil.Uint64 `json:"gas"`
60+
GasPrice *hexutil.Big `json:"gasPrice"`
61+
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
62+
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
63+
Hash common.Hash `json:"hash"`
64+
Input hexutil.Bytes `json:"input"`
65+
Nonce hexutil.Uint64 `json:"nonce"`
66+
To *common.Address `json:"to"`
67+
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
68+
Value *hexutil.Big `json:"value"`
69+
Type hexutil.Uint64 `json:"type"`
70+
Accesses *ethtypes.AccessList `json:"accessList,omitempty"`
71+
ChainID *hexutil.Big `json:"chainId,omitempty"`
72+
AuthorizationList []ethtypes.SetCodeAuthorization `json:"authorizationList,omitempty"`
73+
V *hexutil.Big `json:"v"`
74+
R *hexutil.Big `json:"r"`
75+
S *hexutil.Big `json:"s"`
76+
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
7577
}
7678

7779
// StateOverride is the collection of overridden accounts.

rpc/types/utils.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,25 +240,42 @@ func NewRPCTransaction(
240240
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
241241
result.TransactionIndex = (*hexutil.Uint64)(&index)
242242
}
243+
yparity := hexutil.Uint64(v.Sign()) //#nosec G115
243244
switch tx.Type() {
244245
case ethtypes.AccessListTxType:
245246
al := tx.AccessList()
246247
result.Accesses = &al
247248
result.ChainID = (*hexutil.Big)(tx.ChainId())
249+
result.YParity = &yparity
248250
case ethtypes.DynamicFeeTxType:
249251
al := tx.AccessList()
250252
result.Accesses = &al
251253
result.ChainID = (*hexutil.Big)(tx.ChainId())
254+
result.YParity = &yparity
252255
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
253256
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
254257
// if the transaction has been mined, compute the effective gas price
255258
if baseFee != nil && blockHash != (common.Hash{}) {
256-
// price = min(tip, gasFeeCap - baseFee) + baseFee
259+
// price = min(tip + baseFee, gasFeeCap)
257260
price := ethermint.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
258261
result.GasPrice = (*hexutil.Big)(price)
259262
} else {
260263
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
261264
}
265+
case ethtypes.SetCodeTxType:
266+
al := tx.AccessList()
267+
result.Accesses = &al
268+
result.ChainID = (*hexutil.Big)(tx.ChainId())
269+
result.YParity = &yparity
270+
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
271+
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
272+
if baseFee != nil && blockHash != (common.Hash{}) {
273+
price := ethermint.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
274+
result.GasPrice = (*hexutil.Big)(price)
275+
} else {
276+
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
277+
}
278+
result.AuthorizationList = tx.SetCodeAuthorizations()
262279
}
263280
return result, nil
264281
}

0 commit comments

Comments
 (0)