Event decoding from data and topics
This commit is contained in:
parent
27d6e89672
commit
21f98c4086
|
@ -4,9 +4,11 @@ import pkg/contractabi
|
||||||
import ./basics
|
import ./basics
|
||||||
import ./provider
|
import ./provider
|
||||||
import ./signer
|
import ./signer
|
||||||
|
import ./events
|
||||||
|
|
||||||
export basics
|
export basics
|
||||||
export provider
|
export provider
|
||||||
|
export events
|
||||||
|
|
||||||
type
|
type
|
||||||
Contract* = ref object of RootObj
|
Contract* = ref object of RootObj
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import std/macros
|
||||||
|
import pkg/contractabi
|
||||||
|
import ./basics
|
||||||
|
|
||||||
|
type
|
||||||
|
Event* = object of RootObj
|
||||||
|
Topic* = array[32, byte]
|
||||||
|
ValueType = uint8 | uint16 | uint32 | uint64 | UInt256 | UInt128 |
|
||||||
|
int8 | int16 | int32 | int64 | Int256 | Int128 |
|
||||||
|
bool | Address
|
||||||
|
|
||||||
|
push: {.upraises: [].}
|
||||||
|
|
||||||
|
template indexed* {.pragma.}
|
||||||
|
|
||||||
|
func decode[E: Event](decoder: var AbiDecoder, _: type E): ?!E =
|
||||||
|
var event: E
|
||||||
|
for field in event.fields:
|
||||||
|
if not field.hasCustomPragma(indexed):
|
||||||
|
field = ?decoder.read(typeof(field))
|
||||||
|
success event
|
||||||
|
|
||||||
|
func decode*[E: Event](_: type E, data: seq[byte], topics: seq[Topic]): ?!E =
|
||||||
|
bind decode
|
||||||
|
var event = ?Abidecoder.decode(data, E)
|
||||||
|
var i = 1
|
||||||
|
for field in event.fields:
|
||||||
|
if field.hasCustomPragma(indexed):
|
||||||
|
if i >= topics.len:
|
||||||
|
return failure "indexed event parameter not found"
|
||||||
|
if typeof(field) is ValueType:
|
||||||
|
field = ?AbiDecoder.decode(@(topics[i]), typeof(field))
|
||||||
|
inc i
|
||||||
|
success event
|
|
@ -1,5 +1,6 @@
|
||||||
import ./testJsonRpcProvider
|
import ./testJsonRpcProvider
|
||||||
import ./testJsonRpcSigner
|
import ./testJsonRpcSigner
|
||||||
import ./testContracts
|
import ./testContracts
|
||||||
|
import ./testEvents
|
||||||
|
|
||||||
{.warning[UnusedImport]:off.}
|
{.warning[UnusedImport]:off.}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import pkg/asynctest
|
||||||
|
import pkg/ethers
|
||||||
|
import pkg/contractabi
|
||||||
|
import ./examples
|
||||||
|
|
||||||
|
suite "Events":
|
||||||
|
|
||||||
|
type
|
||||||
|
SimpleEvent = object of Event
|
||||||
|
a: UInt256
|
||||||
|
b: Address
|
||||||
|
IndexedEvent = object of Event
|
||||||
|
a: UInt256
|
||||||
|
b {.indexed.}: Address
|
||||||
|
c: Address
|
||||||
|
d {.indexed.}: UInt256
|
||||||
|
ComplexIndexedEvent = object of Event
|
||||||
|
a {.indexed.}: array[42, UInt256]
|
||||||
|
b {.indexed.}: seq[UInt256]
|
||||||
|
c {.indexed.}: string
|
||||||
|
d {.indexed.}: seq[byte]
|
||||||
|
e {.indexed.}: (Address, UInt256)
|
||||||
|
|
||||||
|
proc example(_: type SimpleEvent): SimpleEvent =
|
||||||
|
SimpleEvent(
|
||||||
|
a: UInt256.example,
|
||||||
|
b: Address.example
|
||||||
|
)
|
||||||
|
|
||||||
|
proc example(_: type IndexedEvent): IndexedEvent =
|
||||||
|
IndexedEvent(
|
||||||
|
a: UInt256.example,
|
||||||
|
b: Address.example,
|
||||||
|
c: Address.example,
|
||||||
|
d: UInt256.example
|
||||||
|
)
|
||||||
|
|
||||||
|
func encode[T](_: type Topic, value: T): Topic =
|
||||||
|
let encoded = AbiEncoder.encode(value)
|
||||||
|
result[0..<Topic.len] = encoded[0..<Topic.len]
|
||||||
|
|
||||||
|
test "decodes event fields":
|
||||||
|
let event = SimpleEvent.example
|
||||||
|
let data = AbiEncoder.encode( (event.a, event.b) )
|
||||||
|
check SimpleEvent.decode(data, @[]) == success event
|
||||||
|
|
||||||
|
test "decodes indexed fields":
|
||||||
|
let event = IndexedEvent.example
|
||||||
|
var topics: seq[Topic]
|
||||||
|
topics.add Topic.default
|
||||||
|
topics.add Topic.encode(event.b)
|
||||||
|
topics.add Topic.encode(event.d)
|
||||||
|
let data = AbiEncoder.encode( (event.a, event.c) )
|
||||||
|
check IndexedEvent.decode(data, topics) == success event
|
||||||
|
|
||||||
|
test "fails when data is incomplete":
|
||||||
|
let event = SimpleEvent.example
|
||||||
|
let invalid = AbiEncoder.encode( (event.a,) )
|
||||||
|
check SimpleEvent.decode(invalid, @[]).isFailure
|
||||||
|
|
||||||
|
test "fails when topics are incomplete":
|
||||||
|
let event = IndexedEvent.example
|
||||||
|
var invalid: seq[Topic]
|
||||||
|
invalid.add Topic.default
|
||||||
|
invalid.add Topic.encode(event.b)
|
||||||
|
let data = AbiEncoder.encode( (event.a, event.c) )
|
||||||
|
check IndexedEvent.decode(data, invalid).isFailure
|
||||||
|
|
||||||
|
test "ignores indexed complex arguments":
|
||||||
|
let topics = @[
|
||||||
|
Topic.default,
|
||||||
|
Topic.example,
|
||||||
|
Topic.example,
|
||||||
|
Topic.example,
|
||||||
|
Topic.example,
|
||||||
|
Topic.example
|
||||||
|
]
|
||||||
|
check ComplexIndexedEvent.decode(@[], topics) == success ComplexIndexedEvent()
|
Loading…
Reference in New Issue