mirror of
https://github.com/status-im/nim-ethers.git
synced 2025-02-26 14:10:41 +00:00
support custom errors with arguments
This commit is contained in:
parent
74f15fca9c
commit
9c76803302
@ -1,3 +1,4 @@
|
||||
import pkg/contractabi
|
||||
import pkg/contractabi/selector
|
||||
import ./basics
|
||||
|
||||
@ -7,14 +8,33 @@ type SolidityError* = object of EthersError
|
||||
|
||||
template errors*(types) {.pragma.}
|
||||
|
||||
func selector(E: type): FunctionSelector =
|
||||
when compiles(E.arguments):
|
||||
selector($E, typeof(E.arguments))
|
||||
else:
|
||||
selector($E, tuple[])
|
||||
|
||||
func matchesSelector(E: type, data: seq[byte]): bool =
|
||||
const selector = E.selector.toArray
|
||||
data.len >= 4 and selector[0..<4] == data[0..<4]
|
||||
|
||||
func decodeArguments(E: type, data: seq[byte]): auto =
|
||||
AbiDecoder.decode(data[4..^1], E.arguments)
|
||||
|
||||
func decode*[E: SolidityError](_: type E, data: seq[byte]): ?!(ref E) =
|
||||
const name = $E
|
||||
const selector = selector(name, typeof(()))
|
||||
if data.len < 4:
|
||||
return failure "unable to decode " & name & ": signature too short"
|
||||
if selector.toArray[0..<4] != data[0..<4]:
|
||||
return failure "unable to decode " & name & ": signature doesn't match"
|
||||
success (ref E)()
|
||||
if not E.matchesSelector(data):
|
||||
return failure "unable to decode " & $E & ": selector doesn't match"
|
||||
when compiles(E.arguments):
|
||||
without arguments =? E.decodeArguments(data), error:
|
||||
return failure "unable to decode " & $E & ": " & error.msg
|
||||
success (ref E)(arguments: arguments)
|
||||
else:
|
||||
success (ref E)()
|
||||
|
||||
func encode*[E: SolidityError](_: type AbiEncoder, error: ref E): seq[byte] =
|
||||
result = @(E.selector.toArray)
|
||||
when compiles(error.arguments):
|
||||
result &= AbiEncoder.encode(error.arguments)
|
||||
|
||||
template convertCustomErrors*[ErrorTypes: tuple](body: untyped): untyped =
|
||||
try:
|
||||
|
@ -8,6 +8,8 @@ suite "Contract custom errors":
|
||||
type
|
||||
TestCustomErrors = ref object of Contract
|
||||
SimpleError = object of SolidityError
|
||||
ErrorWithArguments = object of SolidityError
|
||||
arguments: tuple[one: UInt256, two: bool]
|
||||
|
||||
var contract: TestCustomErrors
|
||||
var provider: JsonRpcProvider
|
||||
@ -30,3 +32,14 @@ suite "Contract custom errors":
|
||||
|
||||
expect SimpleError:
|
||||
await contract.revertsSimpleError()
|
||||
|
||||
test "handles error with arguments":
|
||||
proc revertsErrorWithArguments(contract: TestCustomErrors)
|
||||
{.contract, pure, errors:[ErrorWithArguments].}
|
||||
|
||||
try:
|
||||
await contract.revertsErrorWithArguments()
|
||||
fail()
|
||||
except ErrorWithArguments as error:
|
||||
check error.arguments.one == 1
|
||||
check error.arguments.two == true
|
||||
|
@ -1,10 +1,14 @@
|
||||
import std/unittest
|
||||
import pkg/questionable/results
|
||||
import pkg/contractabi
|
||||
import pkg/ethers/errors
|
||||
|
||||
suite "Decoding of custom errors":
|
||||
|
||||
type SimpleError = object of SolidityError
|
||||
type
|
||||
SimpleError = object of SolidityError
|
||||
ErrorWithArguments = object of SolidityError
|
||||
arguments: tuple[one: UInt256, two: bool]
|
||||
|
||||
test "decodes a simple error":
|
||||
let decoded = SimpleError.decode(@[0xc2'u8, 0xbb, 0x94, 0x7c])
|
||||
@ -12,6 +16,14 @@ suite "Decoding of custom errors":
|
||||
check decoded.isSuccess
|
||||
check (!decoded) != nil
|
||||
|
||||
test "decodes error with arguments":
|
||||
let expected = (ref ErrorWithArguments)(arguments: (1.u256, true))
|
||||
let encoded = AbiEncoder.encode(expected)
|
||||
let decoded = ErrorWithArguments.decode(encoded)
|
||||
check decoded.isSuccess
|
||||
check (!decoded).arguments.one == 1.u256
|
||||
check (!decoded).arguments.two == true
|
||||
|
||||
test "returns failure when decoding fails":
|
||||
let invalid = @[0xc2'u8, 0xbb, 0x94, 0x0] # last byte is wrong
|
||||
let decoded = SimpleError.decode(invalid)
|
||||
|
Loading…
x
Reference in New Issue
Block a user