diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec2d886..a3c4dcbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (evm) [tharsis#24](https://github.com/tharsis/ethermint/pull/24) Implement metrics for `MsgEthereumTx`, state transitions, `BeginBlock` and `EndBlock`. * (rpc) [#124](https://github.com/tharsis/ethermint/issues/124) Implement `txpool_content`, `txpool_inspect` and `txpool_status` RPC methods * (rpc) [tharsis#112](https://github.com/tharsis/ethermint/pull/153) Fix `eth_coinbase` to return the ethereum address of the validator +* (rpc) [tharsis#176](https://github.com/tharsis/ethermint/issues/176) Support fetching pending nonce ### Bug Fixes diff --git a/ethereum/rpc/namespaces/eth/api.go b/ethereum/rpc/namespaces/eth/api.go index 7815089e..7fe3cbd7 100644 --- a/ethereum/rpc/namespaces/eth/api.go +++ b/ethereum/rpc/namespaces/eth/api.go @@ -256,7 +256,8 @@ func (e *PublicAPI) GetTransactionCount(address common.Address, blockNum rpctype return &n, nil } - _, nonce, err := accRet.GetAccountNumberSequence(e.clientCtx, from) + includePending := blockNum == rpctypes.EthPendingBlockNumber + nonce, err := getAccountNonce(e.clientCtx, e.backend, address, includePending, e.logger) if err != nil { return nil, err } @@ -565,16 +566,12 @@ func (e *PublicAPI) doCall( accessList = args.AccessList } - // Set destination address for call - var fromAddr sdk.AccAddress - if args.From != nil { - fromAddr = sdk.AccAddress(args.From.Bytes()) - } else { - fromAddr = sdk.AccAddress(common.Address{}.Bytes()) + if args.From == nil { args.From = &common.Address{} } - _, seq, err := e.clientCtx.AccountRetriever.GetAccountNumberSequence(e.clientCtx, fromAddr) + includePending := blockNr == rpctypes.EthPendingBlockNumber + seq, err := getAccountNonce(e.clientCtx, e.backend, *args.From, includePending, e.logger) if err != nil { return nil, err } @@ -1018,9 +1015,6 @@ func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, block // provided on the args func (e *PublicAPI) setTxDefaults(args rpctypes.SendTxArgs) (rpctypes.SendTxArgs, error) { - // Get nonce (sequence) from sender account - from := sdk.AccAddress(args.From.Bytes()) - if args.GasPrice == nil { // TODO: Change to either: // - min gas price from context once available through server/daemon, or @@ -1031,7 +1025,7 @@ func (e *PublicAPI) setTxDefaults(args rpctypes.SendTxArgs) (rpctypes.SendTxArgs if args.Nonce == nil { // get the nonce from the account retriever // ignore error in case tge account doesn't exist yet - _, nonce, _ := e.clientCtx.AccountRetriever.GetAccountNumberSequence(e.clientCtx, from) + nonce, _ := getAccountNonce(e.clientCtx, e.backend, args.From, true, e.logger) args.Nonce = (*hexutil.Uint64)(&nonce) } @@ -1084,3 +1078,40 @@ func (e *PublicAPI) setTxDefaults(args rpctypes.SendTxArgs) (rpctypes.SendTxArgs return args, nil } + +// getAccountNonce returns the account nonce for the given account address. +// If the pending value is true, it will iterate over the mempool (pending) +// txs in order to compute and return the pending tx sequence. +// Todo: include the ability to specify a blockNumber +func getAccountNonce(ctx client.Context, backend backend.Backend, accAddr common.Address, pending bool, logger log.Logger) (uint64, error) { + _, nonce, err := ctx.AccountRetriever.GetAccountNumberSequence(ctx, accAddr.Bytes()) + if err != nil { + return 0, err + } + + if !pending { + return nonce, nil + } + + // the account retriever doesn't include the uncommitted transactions on the nonce so we need to + // to manually add them. + pendingTxs, err := backend.PendingTransactions() + if err != nil { + logger.Errorln("fails to fetch pending transactions") + return nonce, nil + } + + // add the uncommitted txs to the nonce counter + if len(pendingTxs) != 0 { + for i := range pendingTxs { + if pendingTxs[i] == nil { + continue + } + if pendingTxs[i].From == accAddr { + nonce++ + } + } + } + + return nonce, nil +}