Support distinct types for Event fields
Add support for indexed (and non-indexed) Event fields types that are distinct `ValueType` or `SmallByteArray`. For example, ```nim type DistinctAlias = distinct array[32, byte] MyEvent = object of Event a {.indexed.}: DistinctAlias b: DistinctAlias # also allowed for non-indexed fields ## The below funcs generally need to be included for ABI ## encoding/decoding purposes when implementing distinct types. func toArray(value: DistinctAlias): array[32, byte] = array[32, byte](value) func encode*(encoder: var AbiEncoder, value: DistinctAlias) = encoder.write(value.toArray) func decode*(decoder: var AbiDecoder, T: type DistinctAlias): ?!T = let d = ?decoder.read(type array[32, byte]) success DistinctAlias(d) ```
This commit is contained in:
parent
e8d0fdf1a9
commit
ff3173986f
29
Readme.md
29
Readme.md
|
@ -106,6 +106,35 @@ type Transfer = object of Event
|
||||||
Notice that `Transfer` inherits from `Event`, and that some event parameters are
|
Notice that `Transfer` inherits from `Event`, and that some event parameters are
|
||||||
marked with `{.indexed.}` to match the definition in Solidity.
|
marked with `{.indexed.}` to match the definition in Solidity.
|
||||||
|
|
||||||
|
Note that valid types of indexed parameters are:
|
||||||
|
```nim
|
||||||
|
uint8 | uint16 | uint32 | uint64 | UInt256 | UInt128 |
|
||||||
|
int8 | int16 | int32 | int64 | Int256 | Int128 |
|
||||||
|
bool | Address | array[ 1..32, byte]
|
||||||
|
```
|
||||||
|
Distinct types of valid types are also supported for indexed fields, eg:
|
||||||
|
```nim
|
||||||
|
type
|
||||||
|
DistinctAlias = distinct array[32, byte]
|
||||||
|
MyEvent = object of Event
|
||||||
|
a {.indexed.}: DistinctAlias
|
||||||
|
b: DistinctAlias # also allowed for non-indexed fields
|
||||||
|
|
||||||
|
## The below funcs generally need to be included for ABI
|
||||||
|
## encoding/decoding purposes when implementing distinct types.
|
||||||
|
|
||||||
|
func toArray(value: DistinctAlias): array[32, byte] =
|
||||||
|
array[32, byte](value)
|
||||||
|
|
||||||
|
func encode*(encoder: var AbiEncoder, value: DistinctAlias) =
|
||||||
|
encoder.write(value.toArray)
|
||||||
|
|
||||||
|
func decode*(decoder: var AbiDecoder,
|
||||||
|
T: type DistinctAlias): ?!T =
|
||||||
|
let d = ?decoder.read(type array[32, byte])
|
||||||
|
success DistinctAlias(d)
|
||||||
|
```
|
||||||
|
|
||||||
You can now subscribe to Transfer events by calling `subscribe` on the contract
|
You can now subscribe to Transfer events by calling `subscribe` on the contract
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import std/macros
|
import std/macros
|
||||||
|
import std/typetraits
|
||||||
import pkg/contractabi
|
import pkg/contractabi
|
||||||
import ./basics
|
import ./basics
|
||||||
import ./provider
|
import ./provider
|
||||||
|
@ -40,7 +41,10 @@ func decode*[E: Event](_: type E, data: seq[byte], topics: seq[Topic]): ?!E =
|
||||||
if field.hasCustomPragma(indexed):
|
if field.hasCustomPragma(indexed):
|
||||||
if i >= topics.len:
|
if i >= topics.len:
|
||||||
return failure "indexed event parameter not found"
|
return failure "indexed event parameter not found"
|
||||||
if typeof(field) is ValueType or typeof(field) is SmallByteArray:
|
if typeof(field) is ValueType or
|
||||||
field = ?AbiDecoder.decode(@(topics[i]), typeof(field))
|
typeof(field) is SmallByteArray or
|
||||||
|
typeof(field).distinctBase is ValueType or
|
||||||
|
typeof(field).distinctBase is SmallByteArray:
|
||||||
|
field = ?AbiDecoder.decode(@(topics[i]), typeof(field))
|
||||||
inc i
|
inc i
|
||||||
success event
|
success event
|
||||||
|
|
|
@ -3,6 +3,26 @@ import pkg/ethers
|
||||||
import pkg/contractabi
|
import pkg/contractabi
|
||||||
import ./examples
|
import ./examples
|
||||||
|
|
||||||
|
## Define outside the scope of the suite to allow for exporting
|
||||||
|
## To use custom distinct types, these procs will generally need
|
||||||
|
## to be defined in the application code anyway, to support ABI
|
||||||
|
## encoding/decoding
|
||||||
|
type
|
||||||
|
DistinctAlias = distinct array[32, byte]
|
||||||
|
|
||||||
|
proc `==`*(x, y: DistinctAlias): bool {.borrow.}
|
||||||
|
|
||||||
|
func toArray(value: DistinctAlias): array[32, byte] =
|
||||||
|
array[32, byte](value)
|
||||||
|
|
||||||
|
func encode*(encoder: var AbiEncoder, value: DistinctAlias) =
|
||||||
|
encoder.write(value.toArray)
|
||||||
|
|
||||||
|
func decode*(decoder: var AbiDecoder,
|
||||||
|
T: type DistinctAlias): ?!T =
|
||||||
|
let d = ?decoder.read(type array[32, byte])
|
||||||
|
success DistinctAlias(d)
|
||||||
|
|
||||||
suite "Events":
|
suite "Events":
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -25,6 +45,9 @@ suite "Events":
|
||||||
d {.indexed.}: seq[byte]
|
d {.indexed.}: seq[byte]
|
||||||
e {.indexed.}: (Address, UInt256)
|
e {.indexed.}: (Address, UInt256)
|
||||||
f {.indexed.}: array[33, byte]
|
f {.indexed.}: array[33, byte]
|
||||||
|
IndexedWithDistinctType = object of Event
|
||||||
|
a {.indexed.}: DistinctAlias
|
||||||
|
b: DistinctAlias
|
||||||
|
|
||||||
proc example(_: type SimpleEvent): SimpleEvent =
|
proc example(_: type SimpleEvent): SimpleEvent =
|
||||||
SimpleEvent(
|
SimpleEvent(
|
||||||
|
@ -47,6 +70,14 @@ suite "Events":
|
||||||
e: array[32, byte].example
|
e: array[32, byte].example
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc example(_: type IndexedWithDistinctType): IndexedWithDistinctType =
|
||||||
|
IndexedWithDistinctType(
|
||||||
|
a: DistinctAlias(array[32, byte].example)
|
||||||
|
)
|
||||||
|
|
||||||
|
func encode(_: type AbiEncoder, value: DistinctAlias): seq[byte] =
|
||||||
|
@(value.toArray)
|
||||||
|
|
||||||
func encode[T](_: type Topic, value: T): Topic =
|
func encode[T](_: type Topic, value: T): Topic =
|
||||||
let encoded = AbiEncoder.encode(value)
|
let encoded = AbiEncoder.encode(value)
|
||||||
result[0..<Topic.len] = encoded[0..<Topic.len]
|
result[0..<Topic.len] = encoded[0..<Topic.len]
|
||||||
|
@ -71,6 +102,14 @@ suite "Events":
|
||||||
let data = AbiEncoder.encode( (event.a, event.c) )
|
let data = AbiEncoder.encode( (event.a, event.c) )
|
||||||
check IndexedEvent.decode(data, topics) == success event
|
check IndexedEvent.decode(data, topics) == success event
|
||||||
|
|
||||||
|
test "decodes indexed fields with distinct types":
|
||||||
|
let event = IndexedWithDistinctType.example
|
||||||
|
var topics: seq[Topic]
|
||||||
|
topics.add Topic.default
|
||||||
|
topics.add Topic.encode(event.a)
|
||||||
|
let data = AbiEncoder.encode( (event.b,) )
|
||||||
|
check IndexedWithDistinctType.decode(data, topics) == success event
|
||||||
|
|
||||||
test "fails when data is incomplete":
|
test "fails when data is incomplete":
|
||||||
let event = SimpleEvent.example
|
let event = SimpleEvent.example
|
||||||
let invalid = AbiEncoder.encode( (event.a,) )
|
let invalid = AbiEncoder.encode( (event.a,) )
|
||||||
|
|
Loading…
Reference in New Issue