Skip to content

Commit

Permalink
Unify EthCall/EthSend into TransactionArgs
Browse files Browse the repository at this point in the history
  • Loading branch information
jangko committed Mar 19, 2024
1 parent 80c7aa6 commit 994f340
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 66 deletions.
6 changes: 3 additions & 3 deletions tests/helpers/handlers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ proc installHandlers*(server: RpcServer) =
fh = decodeFromString(x, FeeHistoryResult)
return fh

server.rpc("eth_estimateGas") do(x: JsonString, call: EthCall) -> Quantity:
server.rpc("eth_estimateGas") do(x: JsonString, call: TransactionArgs) -> Quantity:
if x != "-1".JsonString:
result = decodeFromString(x, Quantity)

server.rpc("eth_createAccessList") do(x: JsonString, call: EthCall, blockId: RtBlockIdentifier) -> AccessListResult:
server.rpc("eth_createAccessList") do(x: JsonString, call: TransactionArgs, blockId: RtBlockIdentifier) -> AccessListResult:
var z: AccessListResult
if x != "-1".JsonString:
z = decodeFromString(x, AccessListResult)
Expand All @@ -122,7 +122,7 @@ proc installHandlers*(server: RpcServer) =
if x != "-1".JsonString:
result = decodeFromString(x, Quantity)

server.rpc("eth_call") do(x: JsonString, call: EthCall, blockId: RtBlockIdentifier) -> seq[byte]:
server.rpc("eth_call") do(x: JsonString, call: TransactionArgs, blockId: RtBlockIdentifier) -> seq[byte]:
if x != "-1".JsonString:
result = decodeFromString(x, seq[byte])

Expand Down
6 changes: 3 additions & 3 deletions tests/helpers/utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import

proc deployContract*(web3: Web3, code: string, gasPrice = 0): Future[ReceiptObject] {.async.} =
var code = code
var tr: EthSend
tr.`from` = web3.defaultAccount
tr.data = hexToSeqByte(code)
var tr: TransactionArgs
tr.`from` = some(web3.defaultAccount)
tr.data = some(hexToSeqByte(code))
tr.gas = Quantity(3000000).some
if gasPrice != 0:
tr.gasPrice = some(gasPrice.Quantity)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_json_marshalling.nim
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ suite "JSON-RPC Quantity":
checkRandomObject(StorageProof)
checkRandomObject(ProofResponse)
checkRandomObject(FilterOptions)
checkRandomObject(EthSend)
checkRandomObject(EthCall)
checkRandomObject(TransactionArgs)

checkRandomObject(BlockHeader)
checkRandomObject(BlockObject)
Expand Down
11 changes: 5 additions & 6 deletions tests/test_signed_tx.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2018-2023 Status Research & Development GmbH
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -46,14 +46,13 @@ suite "Signed transactions":
privateKey = PrivateKey.fromHex("0x4646464646464646464646464646464646464646464646464646464646464646").tryGet()
publicKey = privateKey.toPublicKey()
address = publicKey.toCanonicalAddress()
var tx: EthSend
var tx: TransactionArgs
tx.nonce = some(Quantity(9))
tx.`from` = Address(address)
tx.`from` = some(Address(address))
tx.value = some(1000000000000000000.u256)
tx.to = some(Address(hexToByteArray[20]("0x3535353535353535353535353535353535353535")))
tx.gas = some(Quantity(21000'u64))
tx.gasPrice = some(Quantity(20000000000'i64))
tx.data = @[]

let txBytes = encodeTransaction(tx, privateKey, ChainId(1))
let txHex = "0x" & txBytes.toHex
Expand All @@ -71,8 +70,8 @@ suite "Signed transactions":
let pk = PrivateKey.random(theRNG[])
let acc = Address(toCanonicalAddress(pk.toPublicKey()))

var tx: EthSend
tx.`from` = accounts[0]
var tx: TransactionArgs
tx.`from` = some(accounts[0])
tx.value = some(ethToWei(10.u256))
tx.to = some(acc)
tx.gasPrice = some(gasPrice)
Expand Down
22 changes: 11 additions & 11 deletions web3.nim
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ proc nextNonce*(web3: Web3): Future[Quantity] {.async.} =
result = await web3.provider.eth_getTransactionCount(fromAddress, "latest")
web3.lastKnownNonce = some result

proc send*(web3: Web3, c: EthSend): Future[TxHash] {.async.} =
proc send*(web3: Web3, c: TransactionArgs): Future[TxHash] {.async.} =
if web3.privateKey.isSome():
var cc = c
if cc.nonce.isNone:
Expand All @@ -304,7 +304,7 @@ proc send*(web3: Web3, c: EthSend): Future[TxHash] {.async.} =
else:
return await web3.provider.eth_sendTransaction(c)

proc send*(web3: Web3, c: EthSend, chainId: ChainId): Future[TxHash] {.async.} =
proc send*(web3: Web3, c: TransactionArgs, chainId: ChainId): Future[TxHash] {.async.} =
doAssert(web3.privateKey.isSome())
var cc = c
if cc.nonce.isNone:
Expand All @@ -326,9 +326,9 @@ proc sendData(web3: Web3,
nonce = if web3.privateKey.isSome(): some(await web3.nextNonce())
else: none(Quantity)

cc = EthSend(
data: data,
`from`: defaultAccount,
cc = TransactionArgs(
data: some(data),
`from`: some(defaultAccount),
to: some(contractAddress),
gas: some(Quantity(gas)),
value: some(value),
Expand Down Expand Up @@ -362,7 +362,7 @@ proc callAux(
value = 0.u256,
gas = 3000000'u64,
blockNumber = high(BlockNumber)): Future[seq[byte]] {.async.} =
var cc: EthCall
var cc: TransactionArgs
cc.data = some(data)
cc.source = some(defaultAccount)
cc.to = some(contractAddress)
Expand Down Expand Up @@ -408,7 +408,7 @@ proc exec*[T](c: ContractInvocation[T, Web3SenderImpl], value = 0.u256, gas = 30

# Set up a JsonRPC call to send a transaction
# The idea here is to let the Web3 object contain the RPC calls, then allow the
# above DSL to create helpers to create the EthSend object and perform the
# above DSL to create helpers to create the TransactionArgs object and perform the
# transaction. The current idea is to make all this reduce to something like:
# var
# w3 = initWeb3("127.0.0.1", 8545)
Expand All @@ -420,7 +420,7 @@ proc exec*[T](c: ContractInvocation[T, Web3SenderImpl], value = 0.u256, gas = 30
# )
# If the address of the contract on the chain should be part of the DSL or
# dynamically registered is still not decided.
#var cc: EthSend
#var cc: TransactionArgs
#cc.source = [0x78.byte, 0x0b, 0xc7, 0xb4, 0x05, 0x59, 0x41, 0xc2, 0xcb, 0x0e, 0xe1, 0x05, 0x10, 0xe3, 0xfc, 0x83, 0x7e, 0xb0, 0x93, 0xc1]
#cc.to = some([0x0a.byte, 0x78, 0xc0, 0x8F, 0x31, 0x4E, 0xB2, 0x5A, 0x35, 0x1B, 0xfB, 0xA9, 0x03,0x21, 0xa6, 0x96, 0x04, 0x74, 0xbD, 0x79])
#cc.data = "0x90b98a11000000000000000000000000e375b6fb6d0bf0d86707884f3952fee3977251FE0000000000000000000000000000000000000000000000000000000000000258"
Expand Down Expand Up @@ -467,9 +467,9 @@ proc createImmutableContractInvocation*(
raise newException(CatchableError, "No response from the Web3 provider")

proc deployContractAux(web3: Web3, data: seq[byte], gasPrice = 0): Future[Address] {.async.} =
var tr: EthSend
tr.`from` = web3.defaultAccount
tr.data = data
var tr: TransactionArgs
tr.`from` = some(web3.defaultAccount)
tr.data = some(data)
tr.gas = Quantity(30000000).some
if gasPrice != 0:
tr.gasPrice = some(gasPrice.Quantity)
Expand Down
3 changes: 1 addition & 2 deletions web3/conversions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ LogObject.useDefaultSerializationIn JrpcConv
StorageProof.useDefaultSerializationIn JrpcConv
ProofResponse.useDefaultSerializationIn JrpcConv
FilterOptions.useDefaultSerializationIn JrpcConv
EthSend.useDefaultSerializationIn JrpcConv
EthCall.useDefaultSerializationIn JrpcConv
TransactionArgs.useDefaultSerializationIn JrpcConv
FeeHistoryResult.useDefaultSerializationIn JrpcConv

derefType(BlockHeader).useDefaultSerializationIn JrpcConv
Expand Down
10 changes: 1 addition & 9 deletions web3/engine_api_types.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2022-2023 Status Research & Development GmbH
# Copyright (c) 2022-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand All @@ -15,10 +15,6 @@ import
export
options, stint, primitives

const
# https://github.com/ethereum/execution-apis/blob/c4089414bbbe975bbc4bf1ccf0a3d31f76feb3e1/src/engine/cancun.md#blobsbundlev1
fieldElementsPerBlob = 4096

type
TypedTransaction* = distinct seq[byte]

Expand Down Expand Up @@ -119,10 +115,6 @@ type
ExecutionPayloadV2 |
ExecutionPayloadV3

KZGCommitment* = FixedBytes[48]
KZGProof* = FixedBytes[48]
Blob* = FixedBytes[fieldElementsPerBlob * 32]

# https://github.com/ethereum/execution-apis/blob/ee3df5bc38f28ef35385cefc9d9ca18d5e502778/src/engine/cancun.md#blobsbundlev1
BlobsBundleV1* = object
commitments*: seq[KZGCommitment]
Expand Down
13 changes: 7 additions & 6 deletions web3/eth_api.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2019-2023 Status Research & Development GmbH
# Copyright (c) 2019-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
Expand Down Expand Up @@ -44,6 +44,7 @@ createRpcSigsFromNim(RpcClient):
# TODO: Investigate why nim v2 cannot instantiate generic functions
# with oneof params `blockId: BlockIdentifier` and and return type
# Opt[seq[ReceiptObject]], this is a regression after all
# https://github.com/nim-lang/Nim/issues/23310
when false:
proc eth_getBlockReceipts(blockId: BlockIdentifier): Opt[seq[ReceiptObject]]

Expand All @@ -55,12 +56,12 @@ createRpcSigsFromNim(RpcClient):
proc eth_getUncleCountByBlockNumber(blockId: BlockIdentifier): Quantity
proc eth_getCode(data: Address, blockId: BlockIdentifier): seq[byte]
proc eth_sign(address: Address, data: seq[byte]): seq[byte]
proc eth_signTransaction(data: EthSend): seq[byte]
proc eth_sendTransaction(obj: EthSend): TxHash
proc eth_signTransaction(args: TransactionArgs): seq[byte]
proc eth_sendTransaction(args: TransactionArgs): TxHash
proc eth_sendRawTransaction(data: seq[byte]): TxHash
proc eth_call(call: EthCall, blockId: BlockIdentifier): seq[byte]
proc eth_estimateGas(call: EthCall): Quantity
proc eth_createAccessList(call: EthCall, blockId: BlockIdentifier): AccessListResult
proc eth_call(args: TransactionArgs, blockId: BlockIdentifier): seq[byte]
proc eth_estimateGas(args: TransactionArgs): Quantity
proc eth_createAccessList(args: TransactionArgs, blockId: BlockIdentifier): AccessListResult
proc eth_getBlockByHash(data: BlockHash, fullTransactions: bool): BlockObject
proc eth_getBlockByNumber(blockId: BlockIdentifier, fullTransactions: bool): BlockObject
proc eth_getTransactionByHash(data: TxHash): TransactionObject
Expand Down
51 changes: 32 additions & 19 deletions web3/eth_api_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,34 @@ type
## setting. Downstream libraries that want to enforce the up-to-date limit are
## expected to do this on their own.

EthSend* = object
`from`*: Address # the address the transaction is sent from.
to*: Option[Address] # (optional when creating new contract) the address the transaction is directed to.
gas*: Option[Quantity] # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
gasPrice*: Option[Quantity] # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
value*: Option[UInt256] # (optional) integer of the value sent with this transaction.
data*: seq[byte] # the compiled code of a contract OR the hash of the invoked method signature and encoded parameters.
# For details see Ethereum Contract ABI.
nonce*: Option[Quantity] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce

# TODO: Both `EthSend` and `EthCall` are super outdated, according to new spec
# those should be merged into one type `GenericTransaction` with a lot more fields
# see: https://github.com/ethereum/execution-apis/blob/main/src/schemas/transaction.yaml#L244
EthCall* = object
TransactionArgs* = object
`from`*: Option[Address] # (optional) The address the transaction is sent from.
to*: Option[Address] # The address the transaction is directed to.
gas*: Option[Quantity] # (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.
gasPrice*: Option[Quantity] # (optional) Integer of the gasPrice used for each paid gas.
value*: Option[UInt256] # (optional) Integer of the value sent with this transaction.
data*: Option[seq[byte]] # (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI.
maxFeePerGas*: Option[Quantity] # (optional) MaxFeePerGas is the maximum fee per gas offered, in wei.
maxPriorityFeePerGas*: Option[Quantity] # (optional) MaxPriorityFeePerGas is the maximum miner tip per gas offered, in wei.
value*: Option[UInt256] # (optional) Integer of the value sent with this transaction.
nonce*: Option[Quantity] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce

# We accept "data" and "input" for backwards-compatibility reasons.
# "input" is the newer name and should be preferred by clients.
# Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
data*: Option[seq[byte]] # (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI.
input*: Option[seq[byte]]

# Introduced by EIP-2930.
accessList*: Option[seq[AccessTuple]]
chainId*: Option[Quantity]

# EIP-4844
maxFeePerBlobGas*: Option[UInt256]
blobVersionedHashes*: Option[seq[Hash256]]

# EIP-4844 blob side car
blobs*: Option[Blob]
commitments*: Option[KZGCommitment]
proofs*: Option[KZGProof]

## A block header object
BlockHeader* = ref object
Expand Down Expand Up @@ -255,7 +261,7 @@ func blockId*(n: BlockNumber): RtBlockIdentifier =
RtBlockIdentifier(kind: bidNumber, number: n)

func blockId*(b: BlockObject): RtBlockIdentifier =
RtBlockIdentifier(kind: bidNumber, number: BlockNumber b.number)
RtBlockIdentifier(kind: bidNumber, number: b.number)

func blockId*(a: string): RtBlockIdentifier =
RtBlockIdentifier(kind: bidAlias, alias: a)
Expand All @@ -266,11 +272,18 @@ func txOrHash*(hash: TxHash): TxOrHash =
func txOrHash*(tx: TransactionObject): TxOrHash =
TxOrHash(kind: tohTx, tx: tx)

proc `source=`*(c: var EthCall, a: Option[Address]) =
proc `source=`*(c: var TransactionArgs, a: Option[Address]) =
c.`from` = a

func source*(c: EthCall): Option[Address] =
func source*(c: TransactionArgs): Option[Address] =
c.`from`

template `==`*(a, b: RlpEncodedBytes): bool =
distinctBase(a) == distinctBase(b)

func payload*(args: TransactionArgs): seq[byte] =
# Retrieves the transaction calldata. `input` field is preferred.
if args.input.isSome:
return args.input.get
if args.data.isSome:
return args.data.get
8 changes: 8 additions & 0 deletions web3/primitives.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import
export
hashes, options, typetraits

const
# https://github.com/ethereum/execution-apis/blob/c4089414bbbe975bbc4bf1ccf0a3d31f76feb3e1/src/engine/cancun.md#blobsbundlev1
fieldElementsPerBlob = 4096

type
FixedBytes*[N: static[int]] = distinct array[N, byte]

Expand All @@ -32,6 +36,10 @@ type
StorageHash* = FixedBytes[32]
VersionedHash* = FixedBytes[32]

KZGCommitment* = FixedBytes[48]
KZGProof* = FixedBytes[48]
Blob* = FixedBytes[fieldElementsPerBlob * 32]

{.push raises: [].}

template `==`*[N](a, b: FixedBytes[N]): bool =
Expand Down
10 changes: 5 additions & 5 deletions web3/transaction_signing.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2019-2023 Status Research & Development GmbH
# Copyright (c) 2019-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -39,7 +39,7 @@ func signTransactionEip155(tr: var Transaction, pk: PrivateKey) =

tr.V = int64(v) + int64(chainId) * 2 + 35

func encodeTransaction*(s: EthSend, pk: PrivateKey): seq[byte] =
func encodeTransaction*(s: TransactionArgs, pk: PrivateKey): seq[byte] =
var tr = Transaction(txType: TxLegacy)
tr.gasLimit = s.gas.get.GasInt
tr.gasPrice = s.gasPrice.get.GasInt
Expand All @@ -49,11 +49,11 @@ func encodeTransaction*(s: EthSend, pk: PrivateKey): seq[byte] =
if s.value.isSome:
tr.value = s.value.get
tr.nonce = uint64(s.nonce.get)
tr.payload = s.data
tr.payload = s.payload
signTransaction(tr, pk)
return rlp.encode(tr)

func encodeTransaction*(s: EthSend, pk: PrivateKey, chainId: ChainId): seq[byte] =
func encodeTransaction*(s: TransactionArgs, pk: PrivateKey, chainId: ChainId): seq[byte] =
var tr = Transaction(txType: TxLegacy, chainId: chainId)
tr.gasLimit = s.gas.get.GasInt
tr.gasPrice = s.gasPrice.get.GasInt
Expand All @@ -63,6 +63,6 @@ func encodeTransaction*(s: EthSend, pk: PrivateKey, chainId: ChainId): seq[byte]
if s.value.isSome:
tr.value = s.value.get
tr.nonce = uint64(s.nonce.get)
tr.payload = s.data
tr.payload = s.payload
signTransactionEip155(tr, pk)
return rlp.encode(tr)

0 comments on commit 994f340

Please sign in to comment.