nim-contract-abi/contractabi/encoding.nim

156 lines
4.2 KiB
Nim
Raw Normal View History

import pkg/stint
import pkg/upraises
2021-11-29 09:39:13 +00:00
import pkg/stew/byteutils
import ./integers
2022-01-19 08:16:22 +00:00
import ./address
export stint
2022-01-19 08:16:22 +00:00
export address
push: {.upraises:[].}
type
AbiEncoder* = object
stack: seq[Tuple]
Tuple = object
bytes: seq[byte]
postponed: seq[Split]
dynamic: bool
Split = object
head: Slice[int]
tail: seq[byte]
func write*[T](encoder: var AbiEncoder, value: T)
func encode*[T](_: type AbiEncoder, value: T): seq[byte]
func init(_: type AbiEncoder): AbiEncoder =
AbiEncoder(stack: @[Tuple()])
func append(tupl: var Tuple, bytes: openArray[byte]) =
tupl.bytes.add(bytes)
func postpone(tupl: var Tuple, bytes: seq[byte]) =
var split: Split
split.head.a = tupl.bytes.len
tupl.append(AbiEncoder.encode(0'u64))
split.head.b = tupl.bytes.high
split.tail = bytes
tupl.postponed.add(split)
func finish(tupl: Tuple): seq[byte] =
var bytes = tupl.bytes
for split in tupl.postponed:
let offset = bytes.len
bytes[split.head] = AbiEncoder.encode(offset.uint64)
bytes.add(split.tail)
bytes
func append(encoder: var AbiEncoder, bytes: openArray[byte]) =
encoder.stack[^1].append(bytes)
func postpone(encoder: var AbiEncoder, bytes: seq[byte]) =
if encoder.stack.len > 1:
encoder.stack[^1].postpone(bytes)
else:
encoder.stack[0].append(bytes)
func setDynamic(encoder: var AbiEncoder) =
encoder.stack[^1].dynamic = true
2021-12-01 09:58:23 +00:00
func startTuple(encoder: var AbiEncoder) =
encoder.stack.add(Tuple())
func encode(encoder: var AbiEncoder, tupl: Tuple) =
if tupl.dynamic:
encoder.postpone(tupl.finish())
encoder.setDynamic()
else:
encoder.append(tupl.finish())
2021-12-01 09:58:23 +00:00
func finishTuple(encoder: var AbiEncoder) =
encoder.encode(encoder.stack.pop())
2021-12-08 16:27:54 +00:00
func pad(encoder: var AbiEncoder, len: int, padding=0'u8) =
let padlen = (32 - len mod 32) mod 32
for _ in 0..<padlen:
2021-12-08 16:27:54 +00:00
encoder.append([padding])
2021-12-08 16:27:54 +00:00
func padleft(encoder: var AbiEncoder, bytes: openArray[byte], padding=0'u8) =
encoder.pad(bytes.len, padding)
encoder.append(bytes)
2021-12-08 16:27:54 +00:00
func padright(encoder: var AbiEncoder, bytes: openArray[byte], padding=0'u8) =
encoder.append(bytes)
2021-12-08 16:27:54 +00:00
encoder.pad(bytes.len, padding)
func encode(encoder: var AbiEncoder, value: SomeUnsignedInt | StUint) =
encoder.padleft(value.toBytesBE)
func encode(encoder: var AbiEncoder, value: SomeSignedInt | StInt) =
let bytes = value.unsigned.toBytesBE
2021-12-08 16:27:54 +00:00
let padding = if value.isNegative: 0xFF'u8 else: 0x00'u8
encoder.padleft(bytes, padding)
func encode(encoder: var AbiEncoder, value: bool) =
encoder.encode(if value: 1'u8 else: 0'u8)
func encode(encoder: var AbiEncoder, value: enum) =
encoder.encode(uint64(ord(value)))
2022-01-19 08:16:22 +00:00
func encode(encoder: var AbiEncoder, value: Address) =
encoder.padleft(value.toArray)
func encode[I](encoder: var AbiEncoder, bytes: array[I, byte]) =
encoder.padright(bytes)
func encode(encoder: var AbiEncoder, bytes: seq[byte]) =
encoder.encode(bytes.len.uint64)
encoder.padright(bytes)
encoder.setDynamic()
func encode[I, T](encoder: var AbiEncoder, value: array[I, T]) =
encoder.startTuple()
for element in value:
encoder.write(element)
encoder.finishTuple()
func encode[T](encoder: var AbiEncoder, value: seq[T]) =
encoder.encode(value.len.uint64)
encoder.startTuple()
for element in value:
encoder.write(element)
encoder.finishTuple()
encoder.setDynamic()
2021-11-29 09:39:13 +00:00
func encode(encoder: var AbiEncoder, value: string) =
encoder.encode(value.toBytes)
2021-12-01 09:58:23 +00:00
func encode(encoder: var AbiEncoder, tupl: tuple) =
encoder.startTuple()
for element in tupl.fields:
encoder.write(element)
encoder.finishTuple()
func write*[T](encoder: var AbiEncoder, value: T) =
var writer = AbiEncoder.init()
writer.encode(value)
encoder.encode(writer.stack[0])
func finish(encoder: var AbiEncoder): seq[byte] =
doAssert encoder.stack.len == 1, "not all tuples were finished"
doAssert encoder.stack[0].bytes.len mod 32 == 0, "encoding invariant broken"
encoder.stack[0].bytes
func encode*[T](_: type AbiEncoder, value: T): seq[byte] =
var encoder = AbiEncoder.init()
encoder.write(value)
encoder.finish()
2021-11-30 15:22:52 +00:00
proc isDynamic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
var encoder = AbiEncoder.init()
encoder.encode(T.default)
encoder.stack[^1].dynamic
proc isStatic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
not AbiEncoder.isDynamic(T)