Add convenience API for obtaining logs from a smart contract event

This commit is contained in:
Zahary Karadjov 2020-03-07 23:37:23 +02:00
parent 6927cd08a2
commit 270fd64620
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
4 changed files with 65 additions and 5 deletions

View File

@ -119,6 +119,11 @@ proc test() {.async.} =
echo "sendCoin: ", await ns.sendCoin(accounts[1], 100.u256).send()
echo "transfers: ", await ns.getJsonLogs(
Transfer,
fromBlock = some(blockId(deployedAtBlock)),
toBlock = some(blockId(1000'u64)))
await notifFut
await s.unsubscribe()

View File

@ -636,7 +636,8 @@ macro contract*(cname: untyped, body: untyped): untyped =
call.add argument
callWithRawData.add argument
let
cbident = ident obj.eventObject.name
eventName = obj.eventObject.name
cbident = ident eventName
procTy = nnkProcTy.newTree(params, newEmptyNode())
signature = getSignature(obj.eventObject)
@ -654,20 +655,49 @@ macro contract*(cname: untyped, body: untyped): untyped =
result.add quote do:
type `cbident` = object
proc subscribe(s: Sender[`cname`], t: typedesc[`cbident`], options: JsonNode, `callbackIdent`: `procTy`): Future[Subscription] =
let options = addAddressAndSignatureToOptions(options, s.contractAddress, "0x" & toLowerAscii($keccak256.digest(`signature`)))
template eventTopic(T: type `cbident`): string =
"0x" & toLowerAscii($keccak256.digest(`signature`))
proc subscribe(s: Sender[`cname`],
t: type `cbident`,
options: JsonNode,
`callbackIdent`: `procTy`): Future[Subscription] =
let options = addAddressAndSignatureToOptions(options, s.contractAddress, eventTopic(`cbident`))
s.web3.subscribeToLogs(options) do(`jsonIdent`: JsonNode):
`argParseBody`
`call`
proc subscribe(s: Sender[`cname`], t: typedesc[`cbident`], options: JsonNode, `callbackIdent`: `procTyWithRawData`): Future[Subscription] =
let options = addAddressAndSignatureToOptions(options, s.contractAddress, "0x" & toLowerAscii($keccak256.digest(`signature`)))
proc subscribe(s: Sender[`cname`],
t: type `cbident`,
options: JsonNode,
`callbackIdent`: `procTyWithRawData`): Future[Subscription] =
let options = addAddressAndSignatureToOptions(options, s.contractAddress, eventTopic(`cbident`))
s.web3.subscribeToLogs(options) do(`jsonIdent`: JsonNode):
`argParseBody`
`callWithRawData`
proc getJsonLogs(s: Sender[`cname`],
t: type `cbident`,
fromBlock, toBlock = none(RtBlockIdentifier),
blockHash = none(BlockHash)): Future[JsonNode] =
var options = newJObject()
options["address"] = %s.contractAddress
var topics = newJArray()
topics.elems.insert(%eventTopic(`cbident`), 0)
options["topics"] = topics
if blockHash.isSome:
doAssert fromBlock.isNone and toBlock.isNone
options["blockhash"] = %blockHash.unsafeGet
else:
if fromBlock.isSome:
options["fromBlock"] = %fromBlock.unsafeGet
if toBlock.isSome:
options["toBlock"] = %toBlock.unsafeGet
s.web3.provider.eth_getLogs(options)
else:
discard

View File

@ -110,3 +110,9 @@ proc `%`*(x: FilterOptions): JsonNode =
result["address"] = %x.address.unsafeGet
if x.topics.isSome:
result["topics"] = %x.topics.unsafeGet
proc `%`*(x: RtBlockIdentifier): JsonNode =
case x.kind
of BlockIdentifierKind.number: %x.number
of BlockIdentifierKind.alias: %x.alias

View File

@ -15,6 +15,17 @@ type
BlockHash* = FixedBytes[32]
BlockNumber* = uint64
BlockIdentifier* = string|BlockNumber
BlockIdentifierKind* = enum
number
alias
RtBlockIdentifier* = object
case kind*: BlockIdentifierKind
of BlockIdentifierKind.number:
number*: BlockNumber
of BlockIdentifierKind.alias:
alias*: string
Quantity* = distinct uint64
@ -117,6 +128,7 @@ type
toBlock*: Option[string] # (optional, default: "latest") integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.
address*: Option[Address] # (optional) contract address or a list of addresses from which logs should originate.
topics*: Option[seq[string]]#Option[seq[FilterData]] # (optional) list of DATA topics. Topics are order-dependent. Each topic can also be a list of DATA with "or" options.
blockhash*: Option[BlockHash]
LogObject* = object
#removed*: bool # true when the log was removed, due to a chain reorganization. false if its a valid log.
@ -171,3 +183,10 @@ proc `==`*[N](a, b: DynamicBytes[N]): bool {.inline.} =
proc `==`*(a, b: Address): bool {.inline.} =
array[20, byte](a) == array[20, byte](b)
func blockId*(n: BlockNumber): RtBlockIdentifier =
RtBlockIdentifier(kind: BlockIdentifierKind.number, number: n)
func blockId*(a: string): RtBlockIdentifier =
RtBlockIdentifier(kind: BlockIdentifierKind.alias, alias: a)