mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-11 14:33:52 +00:00
support get native token balance from multicall
This commit is contained in:
parent
516f98e356
commit
6fc7bc6c3e
2 changed files with 99 additions and 12 deletions
|
|
@ -1012,17 +1012,6 @@ func (e *revertError) ErrorData() interface{} {
|
||||||
return e.reason
|
return e.reason
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlockChainAPI) ethCallCacheBlockNr(blockNrOrHash rpc.BlockNumberOrHash) int64 {
|
|
||||||
var blockNr int64
|
|
||||||
if n, ok := blockNrOrHash.Number(); ok {
|
|
||||||
blockNr = n.Int64()
|
|
||||||
if n == rpc.LatestBlockNumber || n == rpc.PendingBlockNumber {
|
|
||||||
blockNr = s.b.CurrentBlock().Header().Number.Int64()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blockNr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
//
|
//
|
||||||
// Additionally, the caller can specify a batch of contract for fields overriding.
|
// Additionally, the caller can specify a batch of contract for fields overriding.
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -63,6 +64,61 @@ var (
|
||||||
errCancelled = fmt.Errorf("execution aborted (timeout = %v)", singleCallTimeout)
|
errCancelled = fmt.Errorf("execution aborted (timeout = %v)", singleCallTimeout)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nativeAddr = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// copied from: accounts/abi/abi_test.go
|
||||||
|
Uint8, _ = abi.NewType("uint8", "", nil)
|
||||||
|
Uint256, _ = abi.NewType("uint256", "", nil)
|
||||||
|
String, _ = abi.NewType("string", "", nil)
|
||||||
|
Address, _ = abi.NewType("address", "", nil)
|
||||||
|
|
||||||
|
erc20ABI = abi.ABI{
|
||||||
|
Methods: map[string]abi.Method{
|
||||||
|
"name": funcName,
|
||||||
|
"symbol": funcSymbol,
|
||||||
|
"decimals": funcDecimals,
|
||||||
|
"totalSupply": funcTotalSupply,
|
||||||
|
"balanceOf": funcBalanceOf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
funcName = abi.NewMethod("name", "name", abi.Function, "", false, false,
|
||||||
|
[]abi.Argument{},
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: String, Indexed: false},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
funcSymbol = abi.NewMethod("symbol", "symbol", abi.Function, "", false, false,
|
||||||
|
[]abi.Argument{},
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: String, Indexed: false},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
funcDecimals = abi.NewMethod("decimals", "decimals", abi.Function, "", false, false,
|
||||||
|
[]abi.Argument{},
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: Uint8, Indexed: false},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
funcTotalSupply = abi.NewMethod("totalSupply", "totalSupply", abi.Function, "", false, false,
|
||||||
|
[]abi.Argument{},
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: Uint256, Indexed: false},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
funcBalanceOf = abi.NewMethod("balanceOf", "balanceOf", abi.Function, "", false, false,
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: Address, Indexed: false},
|
||||||
|
},
|
||||||
|
[]abi.Argument{
|
||||||
|
{Name: "", Type: Uint256, Indexed: false},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
func ethCallCacheKey(b Backend, blockHash common.Hash, to *common.Address, input []byte) string {
|
func ethCallCacheKey(b Backend, blockHash common.Hash, to *common.Address, input []byte) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|
||||||
|
|
@ -78,6 +134,40 @@ func ethCallCacheKey(b Backend, blockHash common.Hash, to *common.Address, input
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleNative(ctx context.Context, state *state.StateDB, msg types.Message) ([]byte, error) {
|
||||||
|
data := msg.Data()
|
||||||
|
method, err := erc20ABI.MethodById(data)
|
||||||
|
if err != nil {
|
||||||
|
return []byte("0x"), err
|
||||||
|
}
|
||||||
|
switch method.Name {
|
||||||
|
case "name", "symbol":
|
||||||
|
return method.Outputs.Pack("ETH")
|
||||||
|
case "decimals":
|
||||||
|
return method.Outputs.Pack(18)
|
||||||
|
case "totalSupply":
|
||||||
|
return method.Outputs.Pack(120 * 1_000_000 * 18)
|
||||||
|
}
|
||||||
|
|
||||||
|
if method.Name == "balanceOf" {
|
||||||
|
inputs, err := method.Inputs.Unpack(data[4:])
|
||||||
|
if err != nil || len(inputs) == 0 {
|
||||||
|
return []byte("0x"), fmt.Errorf("input error")
|
||||||
|
}
|
||||||
|
address, ok := inputs[0].(common.Address)
|
||||||
|
if !ok {
|
||||||
|
return []byte("0x"), fmt.Errorf("input address parse error")
|
||||||
|
}
|
||||||
|
balance, err := method.Outputs.Pack(state.GetBalance(address))
|
||||||
|
if err != nil {
|
||||||
|
return []byte("0x"), err
|
||||||
|
}
|
||||||
|
return balance, state.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte("0x"), nil
|
||||||
|
}
|
||||||
|
|
||||||
func doOneCall(ctx context.Context, b Backend, state *state.StateDB, header *types.Header, arg TransactionArgs, disableCache bool) (*callResult, error) {
|
func doOneCall(ctx context.Context, b Backend, state *state.StateDB, header *types.Header, arg TransactionArgs, disableCache bool) (*callResult, error) {
|
||||||
var err error
|
var err error
|
||||||
var result = &callResult{}
|
var result = &callResult{}
|
||||||
|
|
@ -108,7 +198,6 @@ func doOneCall(ctx context.Context, b Backend, state *state.StateDB, header *typ
|
||||||
result.TimeCost = time.Since(start).Seconds()
|
result.TimeCost = time.Since(start).Seconds()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Get a new instance of the EVM.
|
|
||||||
msg, err := arg.ToMessage(b.RPCGasCap(), header.BaseFee)
|
msg, err := arg.ToMessage(b.RPCGasCap(), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Code = errParam
|
result.Code = errParam
|
||||||
|
|
@ -116,6 +205,15 @@ func doOneCall(ctx context.Context, b Backend, state *state.StateDB, header *typ
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip EVM if requests for native token
|
||||||
|
if strings.ToLower(msg.To().Hex()) == nativeAddr {
|
||||||
|
result.Result, err = handleNative(ctx, state, msg)
|
||||||
|
result.Code = errLogic
|
||||||
|
result.Err = err.Error()
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a new instance of the EVM.
|
||||||
evm, _, _ := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}) // never return error
|
evm, _, _ := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}) // never return error
|
||||||
|
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue