grpahql: add EIP-2718 and EIP-1559 features to graphql API

EIP-2718:
- chainID: Long! of Query
- chainID: Long of Transaction

EIP-1559:
- baseFeePerGas: BigInt of Block
- effectiveGasPrice: BigInt of Transaction
- maxFeePerGas: BigInt of Transaction
- maxPriorityFeePerGas: BigInt of Transaction
This commit is contained in:
jangko 2021-09-20 19:58:24 +07:00
parent 7a42d037f2
commit f3d194c05e
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
3 changed files with 119 additions and 11 deletions

View File

@ -49,6 +49,7 @@ type
blockNumber: BlockNumber blockNumber: BlockNumber
receipt: Receipt receipt: Receipt
gasUsed: GasInt gasUsed: GasInt
baseFee: Option[UInt256]
LogNode = ref object of Node LogNode = ref object of Node
log: Log log: Log
@ -88,14 +89,15 @@ proc accountNode(ctx: GraphqlContextRef, acc: Account, address: EthAddress, db:
db: db db: db
) )
proc txNode(ctx: GraphqlContextRef, tx: Transaction, index: int, blockNumber: BlockNumber): Node = proc txNode(ctx: GraphqlContextRef, tx: Transaction, index: int, blockNumber: BlockNumber, baseFee: Option[UInt256]): Node =
TxNode( TxNode(
kind: nkMap, kind: nkMap,
typeName: ctx.ids[ethTransaction], typeName: ctx.ids[ethTransaction],
pos: Pos(), pos: Pos(),
tx: tx, tx: tx,
index: index, index: index,
blockNumber: blockNumber blockNumber: blockNumber,
baseFee: baseFee
) )
proc logNode(ctx: GraphqlContextRef, log: Log, index: int, tx: TxNode): Node = proc logNode(ctx: GraphqlContextRef, log: Log, index: int, tx: TxNode): Node =
@ -254,7 +256,7 @@ proc getTxs(ctx: GraphqlContextRef, header: BlockHeader): RespResult =
var index = 0 var index = 0
for n in getBlockTransactionData(ctx.chainDB, header.txRoot): for n in getBlockTransactionData(ctx.chainDB, header.txRoot):
let tx = rlp.decode(n, Transaction) let tx = rlp.decode(n, Transaction)
list.add txNode(ctx, tx, index, header.blockNumber) list.add txNode(ctx, tx, index, header.blockNumber, header.fee)
inc index inc index
index = 0 index = 0
@ -276,7 +278,7 @@ proc getTxAt(ctx: GraphqlContextRef, header: BlockHeader, index: int): RespResul
try: try:
var tx: Transaction var tx: Transaction
if getTransaction(ctx.chainDB, header.txRoot, index, tx): if getTransaction(ctx.chainDB, header.txRoot, index, tx):
let txn = txNode(ctx, tx, index, header.blockNumber) let txn = txNode(ctx, tx, index, header.blockNumber, header.fee)
var i = 0 var i = 0
var prevUsed = 0.GasInt var prevUsed = 0.GasInt
@ -595,10 +597,47 @@ proc txValue(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.}
bigIntNode(tx.tx.value) bigIntNode(tx.tx.value)
proc txGasPrice(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = proc txGasPrice(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let ctx = GraphqlContextRef(ud)
let tx = TxNode(parent) let tx = TxNode(parent)
if tx.tx.txType == TxEip1559:
if tx.baseFee.isNone:
return bigIntNode(tx.tx.gasPrice)
let baseFee = tx.baseFee.get().truncate(GasInt)
let priorityFee = min(tx.tx.maxPriorityFee, tx.tx.maxFee - baseFee)
bigIntNode(priorityFee + baseFee)
else:
bigIntNode(tx.tx.gasPrice) bigIntNode(tx.tx.gasPrice)
proc txMaxFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let tx = TxNode(parent)
if tx.tx.txType == TxEip1559:
bigIntNode(tx.tx.maxFee)
else:
ok(respNull())
proc txMaxPriorityFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let tx = TxNode(parent)
if tx.tx.txType == TxEip1559:
bigIntNode(tx.tx.maxPriorityFee)
else:
ok(respNull())
proc txEffectiveGasPrice(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let tx = TxNode(parent)
if tx.baseFee.isNone:
return bigIntNode(tx.tx.gasPrice)
let baseFee = tx.baseFee.get().truncate(GasInt)
let priorityFee = min(tx.tx.maxPriorityFee, tx.tx.maxFee - baseFee)
bigIntNode(priorityFee + baseFee)
proc txChainId(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let tx = TxNode(parent)
if tx.tx.txType == TxLegacy:
ok(respNull())
else:
longNode(tx.tx.chainId.uint64)
proc txGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = proc txGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let ctx = GraphqlContextRef(ud) let ctx = GraphqlContextRef(ud)
let tx = TxNode(parent) let tx = TxNode(parent)
@ -706,7 +745,11 @@ const txProcs = {
"s": txS, "s": txS,
"v": txV, "v": txV,
"type": txType, "type": txType,
"accessList": txAccessList "accessList": txAccessList,
"maxFeePerGas": txMaxFeePerGas,
"maxPriorityFeePerGas": txMaxPriorityFeePerGas,
"effectiveGasPrice": txEffectiveGasPrice,
"chainID": txChainId
} }
proc aclAddress(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = proc aclAddress(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
@ -919,6 +962,13 @@ proc blockEstimateGas(ud: RootRef, params: Args, parent: Node): RespResult {.api
except Exception as em: except Exception as em:
err("estimateGas error: " & em.msg) err("estimateGas error: " & em.msg)
proc blockBaseFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let h = HeaderNode(parent)
if h.header.fee.isSome:
bigIntNode(h.header.fee.get)
else:
ok(respNull())
const blockProcs = { const blockProcs = {
"parent": blockParent, "parent": blockParent,
"number": blockNumberImpl, "number": blockNumberImpl,
@ -946,7 +996,8 @@ const blockProcs = {
"logs": blockLogs, "logs": blockLogs,
"account": blockAccount, "account": blockAccount,
"call": blockCall, "call": blockCall,
"estimateGas": blockEstimateGas "estimateGas": blockEstimateGas,
"baseFeePerGas": blockBaseFeePerGas
} }
proc callResultData(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = proc callResultData(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
@ -1118,6 +1169,14 @@ proc querySyncing(ud: RootRef, params: Args, parent: Node): RespResult {.apiPrag
let ctx = GraphqlContextRef(ud) let ctx = GraphqlContextRef(ud)
ok(respMap(ctx.ids[ethSyncState])) ok(respMap(ctx.ids[ethSyncState]))
proc queryMaxPriorityFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
# TODO: stub, missing impl
err("not implemented")
proc queryChainId(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =
let ctx = GraphqlContextRef(ud)
longNode(ctx.chainDB.config.chainId.uint64)
const queryProcs = { const queryProcs = {
"account": queryAccount, "account": queryAccount,
"block": queryBlock, "block": queryBlock,
@ -1127,7 +1186,9 @@ const queryProcs = {
"logs": queryLogs, "logs": queryLogs,
"gasPrice": queryGasPrice, "gasPrice": queryGasPrice,
"protocolVersion": queryProtocolVersion, "protocolVersion": queryProtocolVersion,
"syncing": querySyncing "syncing": querySyncing,
"maxPriorityFeePerGas": queryMaxPriorityFeePerGas,
"chainID": queryChainId
} }
proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} =

View File

@ -97,6 +97,12 @@ type Transaction {
# GasPrice is the price offered to miners for gas, in wei per unit. # GasPrice is the price offered to miners for gas, in wei per unit.
gasPrice: BigInt! gasPrice: BigInt!
# MaxFeePerGas is the maximum fee per gas offered to include a transaction, in wei.
maxFeePerGas: BigInt
# MaxPriorityFeePerGas is the maximum miner tip per gas offered to include a transaction, in wei.
maxPriorityFeePerGas: BigInt
# Gas is the maximum amount of gas this transaction can consume. # Gas is the maximum amount of gas this transaction can consume.
gas: Long! gas: Long!
@ -124,6 +130,14 @@ type Transaction {
# will be null. # will be null.
cumulativeGasUsed: Long cumulativeGasUsed: Long
# EffectiveGasPrice is actual value per gas deducted from the sender's
# account. Before EIP-1559, this is equal to the transaction's gas price.
# After EIP-1559, it is baseFeePerGas + min(maxFeePerGas - baseFeePerGas,
# maxPriorityFeePerGas). Legacy transactions and EIP-2930 transactions are
# coerced into the EIP-1559 format by setting both maxFeePerGas and
# maxPriorityFeePerGas as the transaction's gas price.
effectiveGasPrice: BigInt
# CreatedContract is the account that was created by a contract creation # CreatedContract is the account that was created by a contract creation
# transaction. If the transaction was not a contract creation transaction, # transaction. If the transaction was not a contract creation transaction,
# or it has not yet been mined, this field will be null. # or it has not yet been mined, this field will be null.
@ -147,6 +161,10 @@ type Transaction {
# EIP 2930: optional access list, null if not present # EIP 2930: optional access list, null if not present
accessList: [AccessTuple!] accessList: [AccessTuple!]
# If type == 0, chainID returns null.
# If type > 0, chainID returns replay protection chainID
chainID: Long
} }
# BlockFilterCriteria encapsulates log filter criteria for a filter applied # BlockFilterCriteria encapsulates log filter criteria for a filter applied
@ -209,6 +227,9 @@ type Block {
# GasUsed is the amount of gas that was used executing transactions in this block. # GasUsed is the amount of gas that was used executing transactions in this block.
gasUsed: Long! gasUsed: Long!
# BaseFeePerGas is the fee perunit of gas burned by the protocol in this block.
baseFeePerGas: BigInt
# Timestamp is the unix timestamp at which this block was mined. # Timestamp is the unix timestamp at which this block was mined.
timestamp: BigInt! timestamp: BigInt!
@ -282,6 +303,12 @@ input CallData {
# GasPrice is the price, in wei, offered for each unit of gas. # GasPrice is the price, in wei, offered for each unit of gas.
gasPrice: BigInt gasPrice: BigInt
# MaxFeePerGas is the maximum fee per gas offered, in wei.
maxFeePerGas: BigInt
# MaxPriorityFeePerGas is the maximum miner tip per gas offered, in wei.
maxPriorityFeePerGas: BigInt
# Value is the value, in wei, sent along with the call. # Value is the value, in wei, sent along with the call.
value: BigInt value: BigInt
@ -394,11 +421,18 @@ type Query {
# ensure a transaction is mined in a timely fashion. # ensure a transaction is mined in a timely fashion.
gasPrice: BigInt! gasPrice: BigInt!
# MaxPriorityFeePerGas returns the node's estimate of a gas tip sufficient
# to ensure a transaction is mined in a timely fashion.
maxPriorityFeePerGas: BigInt!
# ProtocolVersion returns the current wire protocol version number. # ProtocolVersion returns the current wire protocol version number.
protocolVersion: Int! protocolVersion: Int!
# Syncing returns information on the current synchronisation state. # Syncing returns information on the current synchronisation state.
syncing: SyncState syncing: SyncState
# ChainID returns the current chain ID for transaction replay protection.
chainID: Long!
} }
type Mutation { type Mutation {

View File

@ -45,6 +45,7 @@
name = "query.block(number)" name = "query.block(number)"
code = """ code = """
{ {
chainID
block(number: 3) { block(number: 3) {
__typename __typename
number number
@ -68,6 +69,7 @@
} }
extraData extraData
gasLimit gasLimit
baseFeePerGas
gasUsed gasUsed
timestamp timestamp
logsBloom logsBloom
@ -104,6 +106,7 @@
""" """
result = """ result = """
{ {
"chainID":1,
"block":{ "block":{
"__typename":"Block", "__typename":"Block",
"number":3, "number":3,
@ -127,6 +130,7 @@
}, },
"extraData":"0x42", "extraData":"0x42",
"gasLimit":3141592, "gasLimit":3141592,
"baseFeePerGas":null,
"gasUsed":21000, "gasUsed":21000,
"timestamp":"0x54c99839", "timestamp":"0x54c99839",
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
@ -203,6 +207,11 @@
address address
storageKeys storageKeys
} }
maxFeePerGas
maxPriorityFeePerGas
effectiveGasPrice
chainID
} }
transactionAt(index: 0) { transactionAt(index: 0) {
__typename __typename
@ -244,7 +253,11 @@
"s":"0x773806df18e22db29acde1dd96c0418e28738af7f520e5e2c5c673494029e5", "s":"0x773806df18e22db29acde1dd96c0418e28738af7f520e5e2c5c673494029e5",
"v":"0x1b", "v":"0x1b",
"type":0, "type":0,
"accessList":null "accessList":null,
"maxFeePerGas":null,
"maxPriorityFeePerGas":null,
"effectiveGasPrice":"0x3e8",
"chainID":null
} }
], ],
"transactionAt":{ "transactionAt":{