import pkg/stint import pkg/stew/endians2 import pkg/stew/byteutils import pkg/upraises import ./encoding push: {.upraises:[].} type AbiDecoder* = object bytes: seq[byte] stack: seq[Tuple] last: int Tuple = object start: int index: int dynamic: bool Padding = enum padLeft, padRight UInt = SomeUnsignedInt | StUint func init(_: type Tuple, offset: int, dynamic: bool): Tuple = Tuple(start: offset, index: offset, dynamic: dynamic) func init*(_: type AbiDecoder, bytes: seq[byte], offset=0): AbiDecoder = AbiDecoder(bytes: bytes, stack: @[Tuple.init(offset, dynamic=false)]) func currentTuple(decoder: var AbiDecoder): var Tuple = decoder.stack[^1] func index(decoder: var AbiDecoder): var int = decoder.currentTuple.index func `index=`(decoder: var AbiDecoder, value: int) = decoder.currentTuple.index = value func updateLast(decoder: var AbiDecoder, index: int) = if index > decoder.last: decoder.last = index func advance(decoder: var AbiDecoder, amount: int) = decoder.index += amount decoder.updateLast(decoder.index) func read(decoder: var AbiDecoder, amount: int, padding = padLeft): seq[byte] = let padlen = (32 - amount mod 32) mod 32 if padding == padLeft: decoder.advance(padlen) result = decoder.bytes[decoder.index.. 1, "unable to finish a tuple that hasn't started" let tupl = decoder.stack.pop() if not tupl.dynamic: decoder.index = tupl.index func decode*(decoder: var AbiDecoder, T: type tuple): T = const dynamic = AbiEncoder.isDynamic(typeof(result)) decoder.startTuple(dynamic) for element in result.fields: element = decoder.read(typeof(element)) decoder.finishTuple() func finish*(decoder: var AbiDecoder) = doAssert decoder.stack.len == 1, "not all tuples were finished" doAssert decoder.last == decoder.bytes.len, "unread trailing bytes found" doAssert decoder.last mod 32 == 0, "encoding variant broken" func decode*[T](decoder: var AbiDecoder, _: type seq[T]): seq[T] = let len = decoder.read(uint64) decoder.startTuple(dynamic=true) for _ in 0..