diff --git a/api/api.go b/api/api.go index b28f0d92..fc3d6c75 100644 --- a/api/api.go +++ b/api/api.go @@ -241,7 +241,7 @@ func (b *BlockChainAPI) GetTransactionByHash( return handleError[*Transaction](b.logger, err) } - return NewTransaction(tx, *rcp) + return NewTransaction(tx, *rcp, *b.config) } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. diff --git a/api/models.go b/api/models.go index 3b1ce30b..860d6f81 100644 --- a/api/models.go +++ b/api/models.go @@ -2,6 +2,7 @@ package api import ( errs "github.com/onflow/flow-evm-gateway/api/errors" + "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/models" "github.com/onflow/go-ethereum/common" @@ -91,7 +92,11 @@ type Transaction struct { YParity *hexutil.Uint64 `json:"yParity,omitempty"` } -func NewTransaction(tx models.Transaction, receipt types.Receipt) (*Transaction, error) { +func NewTransaction( + tx models.Transaction, + receipt types.Receipt, + config config.Config, +) (*Transaction, error) { txHash, err := tx.Hash() if err != nil { return nil, err @@ -112,23 +117,61 @@ func NewTransaction(tx models.Transaction, receipt types.Receipt) (*Transaction, v, r, s := tx.RawSignatureValues() index := uint64(receipt.TransactionIndex) - return &Transaction{ - Hash: txHash, - BlockHash: &receipt.BlockHash, - BlockNumber: (*hexutil.Big)(receipt.BlockNumber), + result := &Transaction{ + Type: hexutil.Uint64(tx.Type()), From: from, - To: to, - Gas: hexutil.Uint64(receipt.GasUsed), - GasPrice: (*hexutil.Big)(receipt.EffectiveGasPrice), + Gas: hexutil.Uint64(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + Hash: txHash, Input: tx.Data(), Nonce: hexutil.Uint64(tx.Nonce()), - TransactionIndex: (*hexutil.Uint64)(&index), + To: to, Value: (*hexutil.Big)(tx.Value()), - Type: hexutil.Uint64(tx.Type()), V: (*hexutil.Big)(v), R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), - }, nil + BlockHash: &receipt.BlockHash, + BlockNumber: (*hexutil.Big)(receipt.BlockNumber), + TransactionIndex: (*hexutil.Uint64)(&index), + } + chainID := config.EVMNetworkID + + switch tx.Type() { + case types.LegacyTxType: + result.ChainID = (*hexutil.Big)(chainID) + case types.AccessListTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(chainID) + result.YParity = &yparity + case types.DynamicFeeTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(chainID) + result.YParity = &yparity + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // Since BaseFee is `0`, this is the effective gas price + // the sender is willing to pay. + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + case types.BlobTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(chainID) + result.YParity = &yparity + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // Since BaseFee is `0`, this is the effective gas price + // the sender is willing to pay. + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap()) + result.BlobVersionedHashes = tx.BlobHashes() + } + + return result, nil } // SignTransactionResult represents a RLP encoded signed transaction. diff --git a/api/stream.go b/api/stream.go index faf361f8..70f3b2f6 100644 --- a/api/stream.go +++ b/api/stream.go @@ -145,7 +145,7 @@ func (s *StreamAPI) NewPendingTransactions(ctx context.Context, fullTx *bool) (* return nil, fmt.Errorf("failed to compute tx hash: %w", err) } - t, err := NewTransaction(tx, *rcp) + t, err := NewTransaction(tx, *rcp, *s.config) if err != nil { return nil, err } diff --git a/models/transaction.go b/models/transaction.go index 62bbd6c1..26fd1189 100644 --- a/models/transaction.go +++ b/models/transaction.go @@ -22,9 +22,14 @@ type Transaction interface { Value() *big.Int Type() uint8 Gas() uint64 + GasFeeCap() *big.Int + GasTipCap() *big.Int GasPrice() *big.Int BlobGas() uint64 + BlobGasFeeCap() *big.Int + BlobHashes() []common.Hash Size() uint64 + AccessList() gethTypes.AccessList MarshalBinary() ([]byte, error) } @@ -79,6 +84,14 @@ func (dc DirectCall) Gas() uint64 { return dc.DirectCall.GasLimit } +func (dc DirectCall) GasFeeCap() *big.Int { + return big.NewInt(0) +} + +func (dc DirectCall) GasTipCap() *big.Int { + return big.NewInt(0) +} + func (dc DirectCall) GasPrice() *big.Int { return big.NewInt(0) } @@ -87,6 +100,14 @@ func (dc DirectCall) BlobGas() uint64 { return 0 } +func (dc DirectCall) BlobGasFeeCap() *big.Int { + return big.NewInt(0) +} + +func (dc DirectCall) BlobHashes() []common.Hash { + return []common.Hash{} +} + func (dc DirectCall) Size() uint64 { encoded, err := dc.MarshalBinary() if err != nil { @@ -95,6 +116,10 @@ func (dc DirectCall) Size() uint64 { return uint64(len(encoded)) } +func (dc DirectCall) AccessList() gethTypes.AccessList { + return gethTypes.AccessList{} +} + func (dc DirectCall) MarshalBinary() ([]byte, error) { return dc.DirectCall.Encode() } diff --git a/tests/e2e_web3js_test.go b/tests/e2e_web3js_test.go index 2dcfb717..1fea511c 100644 --- a/tests/e2e_web3js_test.go +++ b/tests/e2e_web3js_test.go @@ -26,6 +26,10 @@ func TestWeb3_E2E(t *testing.T) { runWeb3Test(t, "eth_deploy_contract_and_interact_test") }) + t.Run("eth_getTransactionByHash", func(t *testing.T) { + runWeb3Test(t, "eth_get_transaction_by_hash_test") + }) + t.Run("transfer Flow between EOA accounts", func(t *testing.T) { runWeb3Test(t, "eth_transfer_between_eoa_accounts_test") }) @@ -38,10 +42,10 @@ func TestWeb3_E2E(t *testing.T) { runWeb3Test(t, "eth_get_filter_logs_test") }) - t.Run("rate-limit requests made by single client", func(t *testing.T) { + t.Run("rate-limit requests made by single client", func(t *testing.T) { runWeb3Test(t, "eth_rate_limit_test") }) - + t.Run("batch run transactions", func(t *testing.T) { // create multiple value transfers and batch run them before the test runWeb3TestWithSetup(t, "eth_batch_retrieval_test", func(emu emulator.Emulator) { diff --git a/tests/web3js/eth_get_transaction_by_hash_test.js b/tests/web3js/eth_get_transaction_by_hash_test.js new file mode 100644 index 00000000..4735cc51 --- /dev/null +++ b/tests/web3js/eth_get_transaction_by_hash_test.js @@ -0,0 +1,33 @@ +const { assert } = require('chai') +const conf = require('./config') +const helpers = require('./helpers') +const web3 = conf.web3 + +it('returns proper response for eth_getTransactionByHash', async () => { + let deployed = await helpers.deployContract('storage') + let contractAddress = deployed.receipt.contractAddress + + // make sure deploy results are correct + assert.equal(deployed.receipt.status, conf.successStatus) + + let updateData = deployed.contract.methods.store(1337).encodeABI() + let result = await helpers.signAndSend({ + from: conf.eoa.address, + to: contractAddress, + data: updateData, + gas: 25000, + value: 0, + maxFeePerGas: 3000000000, + maxPriorityFeePerGas: 800000000, + }) + assert.equal(result.receipt.status, conf.successStatus) + + let tx = await web3.eth.getTransactionFromBlock(result.receipt.blockNumber, 0) + assert.equal(tx.type, 2n) + assert.deepEqual(tx.accessList, []) + assert.equal(tx.chainId, 646n) + assert.equal(tx.gas, 25000n) + assert.equal(tx.maxFeePerGas, 3000000000n) + assert.equal(tx.maxPriorityFeePerGas, 800000000n) + assert.equal(tx.gasPrice, 3000000000n) +}).timeout(10 * 1000) diff --git a/tests/web3js/eth_non_interactive_test.js b/tests/web3js/eth_non_interactive_test.js index a71955ab..f1c42fd2 100644 --- a/tests/web3js/eth_non_interactive_test.js +++ b/tests/web3js/eth_non_interactive_test.js @@ -97,7 +97,7 @@ it('get transaction', async () => { assert.deepEqual(blockTx, tx) assert.isString(tx.hash) assert.equal(tx.blockNumber, conf.startBlockHeight) - assert.isAbove(parseInt(tx.gas), 1) + assert.equal(tx.gas, 300000n) assert.isNotEmpty(tx.from) assert.isNotEmpty(tx.r) assert.isNotEmpty(tx.s) @@ -109,10 +109,10 @@ it('get transaction', async () => { assert.equal(rcp.blockNumber, conf.startBlockHeight) assert.equal(rcp.from, tx.from) assert.equal(rcp.to, tx.to) - assert.equal(rcp.cumulativeGasUsed, tx.gas) // todo check + assert.equal(rcp.cumulativeGasUsed, 21000n) // todo check assert.equal(rcp.transactionHash, tx.hash) assert.equal(rcp.status, conf.successStatus) - assert.equal(rcp.gasUsed, tx.gas) + assert.equal(rcp.gasUsed, 21000n) }) it('get mining status', async () => {