Refactor based on PR comments
- `TransactionReceipt.blockHash` is optional - Block.number is optional (in case node doesn’t return this in the event) - Refactor confirmations waiting such that there is no polling for a receipt at the start - Make BlockHandler and SubscriptionHandler async - change casing of constants - change return type checking of contract method to check for `Confirmable` instead of `?TransactionRepsonse` - Reduce miner sleep to 10ms - Change `wait` and `Waitable` to `confirm` and `Confirmable` to avoid conflict with chrono’s `.wait`. - Update params on `.confirm` so that the compiler can restrict values of the `int` to `Positive` and `Natural`. - Add `Block` and `TransactionReceipt` conversion tests to test for missing block number and block hash. - Add tests for confirmation calculations and determining if a tx has been mined from its receipt. - Assume that blockNumber returned from node will be null or empty string, in which case we can parse as 0 and test for that condition.
This commit is contained in:
parent
a3e888128c
commit
c5c9534876
|
@ -17,6 +17,7 @@ type
|
|||
signer: ?Signer
|
||||
address: Address
|
||||
ContractError* = object of EthersError
|
||||
Confirmable* = ?TransactionResponse
|
||||
EventHandler*[E: Event] = proc(event: E) {.gcsafe, upraises:[].}
|
||||
|
||||
func new*(ContractType: type Contract,
|
||||
|
@ -102,17 +103,16 @@ func isConstant(procedure: NimNode): bool =
|
|||
false
|
||||
|
||||
func isTxResponse(returntype: NimNode): bool =
|
||||
return returntype.kind == nnkPrefix and
|
||||
returntype[0].kind == nnkIdent and
|
||||
returntype[0].strVal == "?" and
|
||||
returntype[1].kind == nnkIdent and
|
||||
returntype[1].strVal == $TransactionResponse
|
||||
return returntype.eqIdent($ Confirmable)
|
||||
|
||||
func addContractCall(procedure: var NimNode) =
|
||||
let contract = procedure[3][1][0]
|
||||
let function = $basename(procedure[0])
|
||||
let parameters = getParameterTuple(procedure)
|
||||
let returntype = procedure[3][0]
|
||||
# procedure[5] =
|
||||
# quote:
|
||||
# static: checkReturnType(type(result))
|
||||
procedure[6] =
|
||||
if procedure.isConstant:
|
||||
if returntype.kind == nnkEmpty:
|
||||
|
@ -145,7 +145,6 @@ func addAsyncPragma(procedure: var NimNode) =
|
|||
|
||||
func checkReturnType(procedure: NimNode) =
|
||||
let returntype = procedure[3][0]
|
||||
|
||||
if returntype.kind != nnkEmpty:
|
||||
# Do not throw exception for methods that have a TransactionResponse
|
||||
# return type as that is needed for .wait
|
||||
|
|
|
@ -33,23 +33,22 @@ type
|
|||
transactionIndex*: UInt256
|
||||
gasUsed*: UInt256
|
||||
logsBloom*: seq[byte]
|
||||
blockHash*: BlockHash
|
||||
blockHash*: ?BlockHash
|
||||
transactionHash*: TransactionHash
|
||||
logs*: seq[Log]
|
||||
blockNumber*: ?UInt256
|
||||
cumulativeGasUsed*: UInt256
|
||||
status*: TransactionStatus
|
||||
LogHandler* = proc(log: Log) {.gcsafe, upraises:[].}
|
||||
BlockHandler* = proc(blck: Block) {.gcsafe, upraises:[].}
|
||||
BlockHandler* = proc(blck: Block): Future[void] {.gcsafe, upraises:[].}
|
||||
Topic* = array[32, byte]
|
||||
Block* = object
|
||||
number*: UInt256
|
||||
number*: ?UInt256
|
||||
timestamp*: UInt256
|
||||
hash*: array[32, byte]
|
||||
|
||||
const DEFAULT_CONFIRMATIONS* {.intdefine.} = 12
|
||||
const RECEIPT_TIMEOUT_BLKS* {.intdefine.} = 50 # in blocks
|
||||
const RECEIPT_POLLING_INTERVAL* {.intdefine.} = 1 # in seconds
|
||||
const EthersDefaultConfirmations* {.intdefine.} = 12
|
||||
const EthersReceiptTimeoutBlks* {.intdefine.} = 50 # in blocks
|
||||
|
||||
method getBlockNumber*(provider: Provider): Future[UInt256] {.base.} =
|
||||
doAssert false, "not implemented"
|
||||
|
@ -71,6 +70,11 @@ method getTransactionCount*(provider: Provider,
|
|||
Future[UInt256] {.base.} =
|
||||
doAssert false, "not implemented"
|
||||
|
||||
method getTransactionReceipt*(provider: Provider,
|
||||
txHash: TransactionHash):
|
||||
Future[?TransactionReceipt] {.base.} =
|
||||
doAssert false, "not implemented"
|
||||
|
||||
method estimateGas*(provider: Provider,
|
||||
transaction: Transaction): Future[UInt256] {.base.} =
|
||||
doAssert false, "not implemented"
|
||||
|
|
|
@ -25,7 +25,7 @@ type
|
|||
provider: JsonRpcProvider
|
||||
address: ?Address
|
||||
JsonRpcProviderError* = object of EthersError
|
||||
SubscriptionHandler = proc(id, arguments: JsonNode) {.gcsafe, upraises:[].}
|
||||
SubscriptionHandler = proc(id, arguments: JsonNode): Future[void] {.gcsafe, upraises:[].}
|
||||
|
||||
template raiseProviderError(message: string) =
|
||||
raise newException(JsonRpcProviderError, message)
|
||||
|
@ -59,7 +59,8 @@ proc connect(provider: JsonRpcProvider, url: string) =
|
|||
proc handleSubscription(arguments: JsonNode) {.upraises: [].} =
|
||||
if id =? arguments["subscription"].catch and
|
||||
handler =? getSubscriptionHandler(id):
|
||||
handler(id, arguments)
|
||||
# fire and forget
|
||||
discard handler(id, arguments)
|
||||
|
||||
proc subscribe: Future[RpcClient] {.async.} =
|
||||
let client = await RpcClient.connect(url)
|
||||
|
@ -114,6 +115,7 @@ method getTransactionCount*(provider: JsonRpcProvider,
|
|||
Future[UInt256] {.async.} =
|
||||
let client = await provider.client
|
||||
return await client.eth_getTransactionCount(address, blockTag)
|
||||
|
||||
method getTransactionReceipt*(provider: JsonRpcProvider,
|
||||
txHash: TransactionHash):
|
||||
Future[?TransactionReceipt] {.async.} =
|
||||
|
@ -148,7 +150,7 @@ method subscribe*(provider: JsonRpcProvider,
|
|||
filter: Filter,
|
||||
callback: LogHandler):
|
||||
Future[Subscription] {.async.} =
|
||||
proc handler(id, arguments: JsonNode) =
|
||||
proc handler(id, arguments: JsonNode) {.async.} =
|
||||
if log =? Log.fromJson(arguments["result"]).catch:
|
||||
callback(log)
|
||||
return await provider.subscribe("logs", filter.some, handler)
|
||||
|
@ -156,9 +158,9 @@ method subscribe*(provider: JsonRpcProvider,
|
|||
method subscribe*(provider: JsonRpcProvider,
|
||||
callback: BlockHandler):
|
||||
Future[Subscription] {.async.} =
|
||||
proc handler(id, arguments: JsonNode) =
|
||||
proc handler(id, arguments: JsonNode) {.async.} =
|
||||
if blck =? Block.fromJson(arguments["result"]).catch:
|
||||
callback(blck)
|
||||
await callback(blck)
|
||||
return await provider.subscribe("newHeads", Filter.none, handler)
|
||||
|
||||
method unsubscribe*(subscription: JsonRpcSubscription) {.async.} =
|
||||
|
@ -196,84 +198,111 @@ method sendTransaction*(signer: JsonRpcSigner,
|
|||
|
||||
return TransactionResponse(hash: hash, provider: signer.provider)
|
||||
|
||||
method wait*(tx: TransactionResponse,
|
||||
wantedConfirms = DEFAULT_CONFIRMATIONS,
|
||||
timeoutInBlocks = RECEIPT_TIMEOUT_BLKS.some): # will error if tx not mined in x blocks
|
||||
|
||||
# Removed from `confirm` closure and exported so it can be tested.
|
||||
# Likely there is a better way
|
||||
func confirmations*(receiptBlk, atBlk: UInt256): UInt256 =
|
||||
## Calculates the number of confirmations between two blocks
|
||||
if atBlk < receiptBlk:
|
||||
return 0.u256
|
||||
else:
|
||||
return (atBlk - receiptBlk) + 1 # add 1 for current block
|
||||
|
||||
# Removed from `confirm` closure and exported so it can be tested.
|
||||
# Likely there is a better way
|
||||
func hasBeenMined*(receipt: ?TransactionReceipt,
|
||||
atBlock: UInt256,
|
||||
wantedConfirms: int): bool =
|
||||
## Returns true if the transaction receipt has been returned from the node
|
||||
## with a valid block number and block hash and the specified number of
|
||||
## blocks have passed since the tx was mined (confirmations)
|
||||
|
||||
if receipt.isSome and
|
||||
receipt.get.blockNumber.isSome and
|
||||
receipt.get.blockNumber.get > 0 and
|
||||
# from ethers.js: "geth-etc" returns receipts before they are ready
|
||||
receipt.get.blockHash.isSome:
|
||||
|
||||
let receiptBlock = receipt.get.blockNumber.get
|
||||
return receiptBlock.confirmations(atBlock) >= wantedConfirms.u256
|
||||
|
||||
return false
|
||||
|
||||
method confirm*(tx: TransactionResponse,
|
||||
wantedConfirms: Positive = EthersDefaultConfirmations,
|
||||
timeoutInBlocks: Natural = EthersReceiptTimeoutBlks):
|
||||
Future[TransactionReceipt]
|
||||
{.async, upraises: [JsonRpcProviderError].} = # raises for clarity
|
||||
## Waits for a transaction to be mined and for the specified number of blocks
|
||||
## to pass since it was mined (confirmations).
|
||||
## A timeout, in blocks, can be specified that will raise a
|
||||
## JsonRpcProviderError if too many blocks have passed without the tx
|
||||
## having been mined.
|
||||
## Note: this method requires that the JsonRpcProvider client connects
|
||||
## using RpcWebSocketClient, otherwise it will raise a Defect.
|
||||
|
||||
var
|
||||
receipt: ?TransactionReceipt
|
||||
subscription: JsonRpcSubscription
|
||||
|
||||
var subscription: JsonRpcSubscription
|
||||
let
|
||||
provider = JsonRpcProvider(tx.provider)
|
||||
retFut = newFuture[TransactionReceipt]("wait")
|
||||
|
||||
proc confirmations(receipt: TransactionReceipt, atBlkNum: UInt256): UInt256 =
|
||||
|
||||
var confirms = (atBlkNum - !receipt.blockNumber) + 1
|
||||
if confirms <= 0: confirms = 1.u256
|
||||
return confirms
|
||||
|
||||
proc newBlock(blk: Block) =
|
||||
# has been mined, need to check # of confirmations thus far
|
||||
let confirms = (!receipt).confirmations(blk.number)
|
||||
if confirms >= wantedConfirms.u256:
|
||||
# fire and forget
|
||||
discard subscription.unsubscribe()
|
||||
retFut.complete(!receipt)
|
||||
|
||||
# used to check for block timeouts
|
||||
let startBlock = await provider.getBlockNumber()
|
||||
|
||||
# loop until the tx is mined, or times out (in blocks) if timeout specified
|
||||
while receipt.isNone:
|
||||
receipt = await provider.getTransactionReceipt(tx.hash)
|
||||
if receipt.isSome and (!receipt).blockNumber.isSome:
|
||||
break
|
||||
proc newBlock(blk: Block) {.async.} =
|
||||
## subscription callback, called every time a new block event is sent from
|
||||
## the node
|
||||
|
||||
if timeoutInBlocks.isSome:
|
||||
let currBlock = await provider.getBlockNumber()
|
||||
let blocksPassed = (currBlock - startBlock) + 1
|
||||
if blocksPassed >= (!timeoutInBlocks).u256:
|
||||
raiseProviderError("Transaction was not mined in " &
|
||||
$(!timeoutInBlocks) & " blocks")
|
||||
# if ethereum node doesn't include blockNumber in the event
|
||||
without blkNum =? blk.number:
|
||||
return
|
||||
|
||||
await sleepAsync(RECEIPT_POLLING_INTERVAL.seconds)
|
||||
let receipt = await provider.getTransactionReceipt(tx.hash)
|
||||
if receipt.hasBeenMined(blkNum, wantedConfirms):
|
||||
# fire and forget
|
||||
discard subscription.unsubscribe()
|
||||
retFut.complete(receipt.get)
|
||||
|
||||
# has been mined, need to check # of confirmations thus far
|
||||
let confirms = (!receipt).confirmations(startBlock)
|
||||
if confirms >= wantedConfirms.u256:
|
||||
return !receipt
|
||||
elif timeoutInBlocks > 0:
|
||||
let blocksPassed = (blkNum - startBlock) + 1
|
||||
if blocksPassed >= timeoutInBlocks.u256:
|
||||
discard subscription.unsubscribe()
|
||||
retFut.fail(
|
||||
newException(JsonRpcProviderError, "Transaction was not mined in " &
|
||||
$timeoutInBlocks & " blocks"))
|
||||
|
||||
# If our tx is already mined, return the receipt. Otherwise, check each
|
||||
# new block to see if the tx has been mined
|
||||
let receipt = await provider.getTransactionReceipt(tx.hash)
|
||||
if receipt.hasBeenMined(startBlock, wantedConfirms):
|
||||
return receipt.get
|
||||
else:
|
||||
let sub = await provider.subscribe(newBlock)
|
||||
subscription = JsonRpcSubscription(sub)
|
||||
return (await retFut)
|
||||
|
||||
method wait*(tx: Future[TransactionResponse],
|
||||
wantedConfirms = DEFAULT_CONFIRMATIONS,
|
||||
timeoutInBlocks = RECEIPT_TIMEOUT_BLKS.some):
|
||||
method confirm*(tx: Future[TransactionResponse],
|
||||
wantedConfirms: Positive = EthersDefaultConfirmations,
|
||||
timeoutInBlocks: Natural = EthersReceiptTimeoutBlks):
|
||||
Future[TransactionReceipt] {.async.} =
|
||||
## Convenience method that allows wait to be chained to a sendTransaction
|
||||
## call, eg:
|
||||
## `await signer.sendTransaction(populated).wait(3)`
|
||||
## `await signer.sendTransaction(populated).confirm(3)`
|
||||
|
||||
let txResp = await tx
|
||||
return await txResp.wait(wantedConfirms, timeoutInBlocks)
|
||||
return await txResp.confirm(wantedConfirms, timeoutInBlocks)
|
||||
|
||||
method wait*(tx: Future[?TransactionResponse],
|
||||
wantedConfirms = DEFAULT_CONFIRMATIONS,
|
||||
timeoutInBlocks = RECEIPT_TIMEOUT_BLKS.some):
|
||||
method confirm*(tx: Future[?TransactionResponse],
|
||||
wantedConfirms: Positive = EthersDefaultConfirmations,
|
||||
timeoutInBlocks: Natural = EthersReceiptTimeoutBlks):
|
||||
Future[TransactionReceipt] {.async.} =
|
||||
## Convenience method that allows wait to be chained to a contract
|
||||
## transaction, eg:
|
||||
## `await token.connect(signer0)
|
||||
## .mint(accounts[1], 100.u256)
|
||||
## .wait(3)`
|
||||
## .confirm(3)`
|
||||
|
||||
let txResp = await tx
|
||||
if txResp.isNone:
|
||||
without txResp =? (await tx):
|
||||
raiseProviderError("Transaction hash required. Possibly was a call instead of a send?")
|
||||
|
||||
return await (!txResp).wait(wantedConfirms, timeoutInBlocks)
|
||||
return await txResp.confirm(wantedConfirms, timeoutInBlocks)
|
||||
|
|
|
@ -5,4 +5,6 @@ import pkg/ethers/providers/jsonrpc
|
|||
proc mineBlocks*(provider: JsonRpcProvider, blks: int) {.async.} =
|
||||
for i in 1..blks:
|
||||
discard await provider.send("evm_mine")
|
||||
await sleepAsync(1.seconds)
|
||||
# Gives time for the subscription to occur in `.wait`.
|
||||
# Likely needed in slower environments, like CI.
|
||||
await sleepAsync(2.milliseconds)
|
||||
|
|
|
@ -19,7 +19,7 @@ method totalSupply*(erc20: Erc20): UInt256 {.base, contract, view.}
|
|||
method balanceOf*(erc20: Erc20, account: Address): UInt256 {.base, contract, view.}
|
||||
method allowance*(erc20: Erc20, owner, spender: Address): UInt256 {.base, contract, view.}
|
||||
method transfer*(erc20: Erc20, recipient: Address, amount: UInt256) {.base, contract.}
|
||||
method mint(token: TestToken, holder: Address, amount: UInt256): ?TransactionResponse {.base, contract.}
|
||||
method mint(token: TestToken, holder: Address, amount: UInt256): Confirmable {.base, contract.}
|
||||
|
||||
suite "Contracts":
|
||||
|
||||
|
@ -122,14 +122,20 @@ suite "Contracts":
|
|||
check transfers == @[Transfer(receiver: accounts[0], value: 100.u256)]
|
||||
|
||||
test "can wait for contract interaction tx to be mined":
|
||||
# must be spawned so we can get newHeads inside of .wait
|
||||
asyncSpawn provider.mineBlocks(3)
|
||||
# must not be awaited so we can get newHeads inside of .wait
|
||||
let futMined = provider.mineBlocks(10)
|
||||
|
||||
let signer0 = provider.getSigner(accounts[0])
|
||||
let receipt = await token.connect(signer0)
|
||||
.mint(accounts[1], 100.u256)
|
||||
.wait(3) # wait for 3 confirmations
|
||||
.confirm(3) # wait for 3 confirmations
|
||||
let endBlock = await provider.getBlockNumber()
|
||||
|
||||
check receipt.blockNumber.isSome # was eventually mined
|
||||
check (endBlock - !receipt.blockNumber) + 1 == 3 # +1 for the block the tx was mined in
|
||||
|
||||
# >= 3 because more blocks may have been mined by the time the
|
||||
# check in `.wait` was done.
|
||||
# +1 for the block the tx was mined in
|
||||
check (endBlock - !receipt.blockNumber) + 1 >= 3
|
||||
|
||||
await futMined
|
||||
|
|
|
@ -40,16 +40,16 @@ suite "JsonRpcProvider":
|
|||
let block1 = !await provider.getBlock(BlockTag.earliest)
|
||||
let block2 = !await provider.getBlock(BlockTag.latest)
|
||||
check block1.hash != block2.hash
|
||||
check block1.number < block2.number
|
||||
check !block1.number < !block2.number
|
||||
check block1.timestamp < block2.timestamp
|
||||
|
||||
test "subscribes to new blocks":
|
||||
let oldBlock = !await provider.getBlock(BlockTag.latest)
|
||||
var newBlock: Block
|
||||
let blockHandler = proc(blck: Block) = newBlock = blck
|
||||
let blockHandler = proc(blck: Block) {.async.} = newBlock = blck
|
||||
let subscription = await provider.subscribe(blockHandler)
|
||||
discard await provider.send("evm_mine")
|
||||
check newBlock.number > oldBlock.number
|
||||
check !newBlock.number > !oldBlock.number
|
||||
check newBlock.timestamp > oldBlock.timestamp
|
||||
check newBlock.hash != oldBlock.hash
|
||||
await subscription.unsubscribe()
|
||||
|
@ -67,28 +67,180 @@ suite "JsonRpcProvider":
|
|||
let transaction = Transaction.example
|
||||
let populated = await signer.populateTransaction(transaction)
|
||||
|
||||
# must be spawned so we can get newHeads inside of .wait
|
||||
asyncSpawn provider.mineBlocks(3)
|
||||
# must not be awaited so we can get newHeads inside of .wait
|
||||
let futMined = provider.mineBlocks(5)
|
||||
|
||||
let receipt = await signer.sendTransaction(populated).wait(3)
|
||||
let receipt = await signer.sendTransaction(populated).confirm(3)
|
||||
let endBlock = await provider.getBlockNumber()
|
||||
|
||||
check receipt.blockNumber.isSome # was eventually mined
|
||||
check (endBlock - !receipt.blockNumber) + 1 == 3 # +1 for the block the tx was mined in
|
||||
|
||||
# >= 3 because more blocks may have been mined by the time the
|
||||
# check in `.wait` was done.
|
||||
# +1 for the block the tx was mined in
|
||||
check (endBlock - !receipt.blockNumber) + 1 >= 3
|
||||
|
||||
await futMined
|
||||
|
||||
test "waiting for block to be mined times out":
|
||||
|
||||
# must be spawned so we can get newHeads inside of .wait
|
||||
asyncSpawn provider.mineBlocks(10)
|
||||
# must not be awaited so we can get newHeads inside of .wait
|
||||
let futMined = provider.mineBlocks(7)
|
||||
|
||||
let startBlock = await provider.getBlockNumber()
|
||||
let response = TransactionResponse(hash: TransactionHash.example,
|
||||
provider: provider)
|
||||
try:
|
||||
discard await response.wait(wantedConfirms = 2,
|
||||
timeoutInBlocks = 5.some)
|
||||
discard await response.confirm(wantedConfirms = 2,
|
||||
timeoutInBlocks = 5)
|
||||
|
||||
await futMined
|
||||
except JsonRpcProviderError as e:
|
||||
check e.msg == "Transaction was not mined in 5 blocks"
|
||||
|
||||
let endBlock = await provider.getBlockNumber()
|
||||
check (endBlock - startBlock) + 1 == 5 # +1 including start block
|
||||
|
||||
# >= 5 because more blocks may have been mined by the time the
|
||||
# check in `.wait` was done.
|
||||
# +1 for including the start block
|
||||
check (endBlock - startBlock) + 1 >= 5 # +1 including start block
|
||||
if not futMined.completed and not futMined.finished: await futMined
|
||||
|
||||
test "Conversion: missing block number in Block isNone":
|
||||
|
||||
var blkJson = %*{
|
||||
"subscription": "0x20",
|
||||
"result":{
|
||||
"number": newJNull(),
|
||||
"hash":"0x2d7d68c8f48b4213d232a1f12cab8c9fac6195166bb70a5fb21397984b9fe1c7",
|
||||
"timestamp":"0x6285c293"
|
||||
}
|
||||
}
|
||||
|
||||
var blk = Block.fromJson(blkJson["result"])
|
||||
check blk.number.isNone
|
||||
|
||||
blkJson["result"]["number"] = newJString("")
|
||||
|
||||
blk = Block.fromJson(blkJson["result"])
|
||||
check blk.number.isSome
|
||||
check blk.number.get.isZero
|
||||
|
||||
test "Conversion: missing block number in TransactionReceipt isNone":
|
||||
|
||||
var txReceiptJson = %*{
|
||||
"sender": newJNull(),
|
||||
"to": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
|
||||
"contractAddress": newJNull(),
|
||||
"transactionIndex": "0x0",
|
||||
"gasUsed": "0x10db1",
|
||||
"logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000",
|
||||
"blockHash": "0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960",
|
||||
"transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c",
|
||||
"logs": [
|
||||
{
|
||||
"data": "0x0000000000000000000000000000000000000000000000000000000000000064",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8"
|
||||
]
|
||||
}
|
||||
],
|
||||
"blockNumber": newJNull(),
|
||||
"cumulativeGasUsed": "0x10db1",
|
||||
"status": "0000000000000001"
|
||||
}
|
||||
|
||||
var txReceipt = TransactionReceipt.fromJson(txReceiptJson)
|
||||
check txReceipt.blockNumber.isNone
|
||||
|
||||
txReceiptJson["blockNumber"] = newJString("")
|
||||
txReceipt = TransactionReceipt.fromJson(txReceiptJson)
|
||||
check txReceipt.blockNumber.isSome
|
||||
check txReceipt.blockNumber.get.isZero
|
||||
|
||||
test "Conversion: missing block hash in TransactionReceipt isNone":
|
||||
|
||||
var txReceiptJson = %*{
|
||||
"sender": newJNull(),
|
||||
"to": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
|
||||
"contractAddress": newJNull(),
|
||||
"transactionIndex": "0x0",
|
||||
"gasUsed": "0x10db1",
|
||||
"logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000",
|
||||
"blockHash": newJNull(),
|
||||
"transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c",
|
||||
"logs": [
|
||||
{
|
||||
"data": "0x0000000000000000000000000000000000000000000000000000000000000064",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8"
|
||||
]
|
||||
}
|
||||
],
|
||||
"blockNumber": newJNull(),
|
||||
"cumulativeGasUsed": "0x10db1",
|
||||
"status": "0000000000000001"
|
||||
}
|
||||
|
||||
var txReceipt = TransactionReceipt.fromJson(txReceiptJson)
|
||||
check txReceipt.blockHash.isNone
|
||||
|
||||
test "confirmations calculated correctly":
|
||||
# when receipt block number is higher than current block number,
|
||||
# should return 0
|
||||
check confirmations(2.u256, 1.u256) == 0.u256
|
||||
|
||||
# Same receipt and current block counts as one confirmation
|
||||
check confirmations(1.u256, 1.u256) == 1.u256
|
||||
|
||||
check confirmations(1.u256, 2.u256) == 2.u256
|
||||
|
||||
test "checks if transation has been mined correctly":
|
||||
|
||||
var receipt = TransactionReceipt.none
|
||||
var currentBlock = 1.u256
|
||||
var wantedConfirms = 1
|
||||
let blockHash = hexToByteArray[32](
|
||||
"0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960"
|
||||
).some
|
||||
|
||||
# if TransactionReceipt is none, should not be considered mined
|
||||
check not receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
# missing blockHash
|
||||
receipt = TransactionReceipt(
|
||||
blockNumber: 1.u256.some
|
||||
).some
|
||||
check not receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
# missing block number
|
||||
receipt = TransactionReceipt(
|
||||
blockHash: blockHash
|
||||
).some
|
||||
check not receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
# block number is 0
|
||||
receipt = TransactionReceipt(
|
||||
blockNumber: 0.u256.some
|
||||
).some
|
||||
check not receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
# not enough confirms
|
||||
receipt = TransactionReceipt(
|
||||
blockNumber: 1.u256.some
|
||||
).some
|
||||
check not receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
# success
|
||||
receipt = TransactionReceipt(
|
||||
blockNumber: 1.u256.some,
|
||||
blockHash: blockHash
|
||||
).some
|
||||
currentBlock = int.high.u256
|
||||
wantedConfirms = int.high
|
||||
check receipt.hasBeenMined(currentBlock, wantedConfirms)
|
||||
|
||||
|
|
Loading…
Reference in New Issue