diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 7b55fba174..cbe0943a2d 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -272,6 +272,21 @@ func (b Uint64) String() string { return EncodeUint64(uint64(b)) } +func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" } + +func (b *Uint64) UnmarshalGraphQL(input interface{}) error { + var err error + switch input := input.(type) { + case string: + return b.UnmarshalText([]byte(input)) + case int32: + *b = Uint64(input) + default: + err = fmt.Errorf("Unexpected type for BigInt: %v", input) + } + return err +} + // Uint marshals/unmarshals as a JSON string with 0x prefix. // The zero value marshals as "0x0". type Uint uint diff --git a/common/types.go b/common/types.go index a4b9995267..6ecef40592 100644 --- a/common/types.go +++ b/common/types.go @@ -268,6 +268,19 @@ func (a Address) Value() (driver.Value, error) { return a[:], nil } +func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" } + +func (a *Address) UnmarshalGraphQL(input interface{}) error { + var err error + switch input := input.(type) { + case string: + *a = HexToAddress(input) + default: + err = fmt.Errorf("Unexpected type for Hash: %v", input) + } + return err +} + // UnprefixedAddress allows marshaling an Address without 0x prefix. type UnprefixedAddress Address diff --git a/ethgraphql/main.go b/ethgraphql/main.go index 7480282d08..f700556ada 100644 --- a/ethgraphql/main.go +++ b/ethgraphql/main.go @@ -848,13 +848,12 @@ func (r *Resolver) SendRawTransaction(ctx context.Context, args struct{ Data hex } type CallData struct { - From *Address - To *Address - Gas *int32 - GasPrice *hexutil.Big - Value *hexutil.Big - Data *hexutil.Bytes - BlockNumber *int32 + From *Address + To *Address + Gas *hexutil.Uint64 + GasPrice *hexutil.Big + Value *hexutil.Big + Data *hexutil.Bytes } type CallResult struct { @@ -875,45 +874,21 @@ func (c *CallResult) Status() int32 { return c.status } -func convertCallData(data CallData) (ethapi.CallArgs, rpc.BlockNumber) { - callArgs := ethapi.CallArgs{} - if data.From != nil { - callArgs.From = data.From.Address - } - if data.To != nil { - addr := data.To.Address - callArgs.To = &addr - } - if data.Gas != nil { - callArgs.Gas = hexutil.Uint64(*data.Gas) - } - if data.GasPrice != nil { - callArgs.GasPrice = *data.GasPrice - } - if data.Value != nil { - callArgs.Value = *data.Value - } - if data.Data != nil { - callArgs.Data = *data.Data - } - - blockNumber := rpc.LatestBlockNumber - if data.BlockNumber != nil { - blockNumber = rpc.BlockNumber(*data.BlockNumber) - } - - return callArgs, blockNumber -} - -func (r *Resolver) Call(ctx context.Context, args struct{ Data CallData }) (*CallResult, error) { +func (r *Resolver) Call(ctx context.Context, args struct { + Data ethapi.CallArgs + BlockNumber *int32 +}) (*CallResult, error) { be, err := getBackend(r.node) if err != nil { return nil, err } - callArgs, blockNumber := convertCallData(args.Data) + blockNumber := rpc.LatestBlockNumber + if args.BlockNumber != nil { + blockNumber = rpc.BlockNumber(*args.BlockNumber) + } - result, gas, failed, err := ethapi.DoCall(ctx, be, callArgs, blockNumber, vm.Config{}, 5*time.Second) + result, gas, failed, err := ethapi.DoCall(ctx, be, args.Data, blockNumber, vm.Config{}, 5*time.Second) status := int32(1) if failed { status = 0 @@ -925,15 +900,21 @@ func (r *Resolver) Call(ctx context.Context, args struct{ Data CallData }) (*Cal }, err } -func (r *Resolver) EstimateGas(ctx context.Context, args struct{ Data CallData }) (int32, error) { +func (r *Resolver) EstimateGas(ctx context.Context, args struct { + Data ethapi.CallArgs + BlockNumber *int32 +}) (int32, error) { be, err := getBackend(r.node) if err != nil { return 0, err } - callArgs, blockNumber := convertCallData(args.Data) + blockNumber := rpc.LatestBlockNumber + if args.BlockNumber != nil { + blockNumber = rpc.BlockNumber(*args.BlockNumber) + } - gas, err := ethapi.DoEstimateGas(ctx, be, callArgs, blockNumber) + gas, err := ethapi.DoEstimateGas(ctx, be, args.Data, blockNumber) return int32(gas), err } @@ -945,6 +926,7 @@ func NewHandler(n *node.Node) (http.Handler, error) { scalar Address scalar Bytes scalar BigInt + scalar Long schema { query: Query @@ -1015,11 +997,10 @@ func NewHandler(n *node.Node) (http.Handler, error) { input CallData { from: Address to: Address - gas: Int + gas: Long gasPrice: BigInt value: BigInt data: Bytes - blockNumber: Int } type CallResult { @@ -1033,8 +1014,8 @@ func NewHandler(n *node.Node) (http.Handler, error) { block(number: Int, hash: Bytes32): Block blocks(from: Int!, to: Int): [Block!]! transaction(hash: Bytes32!): Transaction - call(data: CallData!): CallResult - estimateGas(data: CallData!): Int! + call(data: CallData!, blockNumber: Int): CallResult + estimateGas(data: CallData!, blockNumber: Int): Int! } type Mutation { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 75f638d91e..b85350bdd7 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -609,12 +609,12 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A // CallArgs represents the arguments for a call. type CallArgs struct { - From common.Address `json:"from"` + From *common.Address `json:"from"` To *common.Address `json:"to"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice hexutil.Big `json:"gasPrice"` - Value hexutil.Big `json:"value"` - Data hexutil.Bytes `json:"data"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"data"` } func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) { @@ -625,25 +625,38 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb return nil, 0, false, err } // Set sender address or use a default if none specified - addr := args.From - if addr == (common.Address{}) { + var addr common.Address + if args.From == nil { if wallets := b.AccountManager().Wallets(); len(wallets) > 0 { if accounts := wallets[0].Accounts(); len(accounts) > 0 { addr = accounts[0].Address } } + } else { + addr = *args.From } // Set default gas & gas price if none were set - gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt() - if gas == 0 { - gas = math.MaxUint64 / 2 + gas := uint64(math.MaxUint64 / 2) + if args.Gas != nil { + gas = uint64(*args.Gas) } - if gasPrice.Sign() == 0 { - gasPrice = new(big.Int).SetUint64(defaultGasPrice) + gasPrice := new(big.Int).SetUint64(defaultGasPrice) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + + value := new(big.Int) + if args.Value != nil { + value = args.Value.ToInt() + } + + var data []byte + if args.Data != nil { + data = []byte(*args.Data) } // Create new call message - msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false) + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) // Setup context so it may be cancelled the call has completed // or, in case of unmetered gas, setup a context with a timeout. @@ -695,8 +708,8 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl hi uint64 cap uint64 ) - if uint64(args.Gas) >= params.TxGas { - hi = uint64(args.Gas) + if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { + hi = uint64(*args.Gas) } else { // Retrieve the block to act as the gas ceiling block, err := b.BlockByNumber(ctx, blockNr) @@ -709,7 +722,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl // Create a helper to check if a gas allowance results in an executable transaction executable := func(gas uint64) bool { - args.Gas = hexutil.Uint64(gas) + args.Gas = (*hexutil.Uint64)(&gas) _, _, failed, err := DoCall(ctx, b, args, blockNr, vm.Config{}, 0) if err != nil || failed {