import std/[typetraits, strutils, macros, math] import stint, stew/byteutils, ./ethtypes type EncodeResult* = tuple[dynamic: bool, data: string] func encode*[bits: static[int]](x: Stuint[bits]): EncodeResult = ## Encodes a `Stuint` to a textual representation for use in the JsonRPC ## `sendTransaction` call. (dynamic: false, data: '0'.repeat((256 - bits) div 4) & x.dumpHex) func encode*[bits: static[int]](x: Stint[bits]): EncodeResult = ## Encodes a `Stint` to a textual representation for use in the JsonRPC ## `sendTransaction` call. (dynamic: false, data: if x.isNegative: 'f'.repeat((256 - bits) div 4) & x.dumpHex else: '0'.repeat((256 - bits) div 4) & x.dumpHex ) func decode*(input: string, offset: int, to: var Stuint): int = let meaningfulLen = to.bits div 8 * 2 to = type(to).fromHex(input[offset .. offset + meaningfulLen - 1]) meaningfulLen func decode*[N](input: string, offset: int, to: var Stint[N]): int = let meaningfulLen = N div 8 * 2 fromHex(input[offset .. offset + meaningfulLen], to) meaningfulLen func fixedEncode(a: openarray[byte]): EncodeResult = var padding = a.len mod 32 if padding != 0: padding = 32 - padding result = (dynamic: false, data: "00".repeat(padding) & byteutils.toHex(a)) func encode*[N](b: FixedBytes[N]): EncodeResult = fixedEncode(array[N, byte](b)) func encode*(b: Address): EncodeResult = fixedEncode(array[20, byte](b)) func decodeFixed(input: string, offset: int, to: var openarray[byte]): int = let meaningfulLen = to.len * 2 var padding = to.len mod 32 if padding != 0: padding = (32 - padding) * 2 let offset = offset + padding hexToByteArray(input[offset .. offset + meaningfulLen - 1], to) meaningfulLen + padding func decode*[N](input: string, offset: int, to: var FixedBytes[N]): int {.inline.} = decodeFixed(input, offset, array[N, byte](to)) func decode*(input: string, offset: int, to: var Address): int {.inline.} = decodeFixed(input, offset, array[20, byte](to)) func encodeDynamic(v: openarray[byte]): EncodeResult = result.dynamic = true result.data = v.len.toHex(64).toLower for y in v: result.data &= y.toHex.toLower result.data &= "00".repeat(v.len mod 32) func encode*(x: DynamicBytes): EncodeResult {.inline.} = encodeDynamic(distinctBase x) func decode*(input: string, offset: int, to: var DynamicBytes): int {.inline.} = var dataOffset, dataLen: UInt256 result = decode(input, offset, dataOffset) discard decode(input, dataOffset.truncate(int) * 2, dataLen) # TODO: Check data len, and raise? let actualDataOffset = (dataOffset.truncate(int) + 32) * 2 to = typeof(to)(hexToSeqByte(input[actualDataOffset ..< actualDataOffset + dataLen.truncate(int) * 2])) macro makeTypeEnum(): untyped = ## This macro creates all the various types of Solidity contracts and maps ## them to the type used for their encoding. It also creates an enum to ## identify these types in the contract signatures, along with encoder ## functions used in the generated procedures. result = newStmtList() var lastpow2: int for i in countdown(256, 8, 8): let identUint = newIdentNode("Uint" & $i) identInt = newIdentNode("Int" & $i) if ceil(log2(i.float)) == floor(log2(i.float)): lastpow2 = i if i notin {256, 125}: # Int/Uint256/128 are already defined in stint. No need to repeat. result.add quote do: type `identUint`* = Stuint[`lastpow2`] `identInt`* = Stint[`lastpow2`] let identUint = ident("Uint") identInt = ident("Int") identBool = ident("Bool") result.add quote do: type `identUint`* = Uint256 `identInt`* = Int256 `identBool`* = distinct Int256 for m in countup(8, 256, 8): let identInt = ident("Int" & $m) identUint = ident("Uint" & $m) identFixed = ident "Fixed" & $m identUfixed = ident "Ufixed" & $m identT = ident "T" result.add quote do: # Fixed stuff is not actually implemented yet, these procedures don't # do what they are supposed to. type `identFixed`*[N: static[int]] = distinct `identInt` `identUfixed`*[N: static[int]] = distinct `identUint` # func to*(x: `identInt`, `identT`: typedesc[`identFixed`]): `identT` = # T(x) # func to*(x: `identUint`, `identT`: typedesc[`identUfixed`]): `identT` = # T(x) # func encode*[N: static[int]](x: `identFixed`[N]): EncodeResult = # encode(`identInt`(x) * (10 ^ N).to(`identInt`)) # func encode*[N: static[int]](x: `identUfixed`[N]): EncodeResult = # encode(`identUint`(x) * (10 ^ N).to(`identUint`)) # func decode*[N: static[int]](input: string, to: `identFixed`[N]): `identFixed`[N] = # decode(input, `identInt`) div / (10 ^ N).to(`identInt`) # func decode*[N: static[int]](input: string, to: `identUfixed`[N]): `identFixed`[N] = # decode(input, `identUint`) div / (10 ^ N).to(`identUint`) let identFixed = ident("Fixed") identUfixed = ident("Ufixed") result.add quote do: type `identFixed`* = distinct Int128 `identUfixed`* = distinct Uint128 for i in 1..256: let identBytes = ident("Bytes" & $i) identResult = ident "result" result.add quote do: type `identBytes`* = FixedBytes[`i`] #result.add newEnum(ident "FieldKind", fields, public = true, pure = true) makeTypeEnum() proc parse*(T: type Bool, val: bool): T = let i = if val: 1 else: 0 T i.i256 proc `==`*(a: Bool, b: Bool): bool = Int256(a) == Int256(b) func encode*(x: Bool): EncodeResult = encode(Int256(x)) func decode*(input: string, offset: int, to: var Bool): int = let meaningfulLen = Int256.bits div 8 * 2 to = Bool Int256.fromHex(input[offset .. offset + meaningfulLen - 1]) meaningfulLen func decode*(input: string, offset: int, obj: var object): int = var offset = offset for field in fields(obj): offset += decode(input, offset, field) type Encodable = concept x encode(x) is EncodeResult func encode*(x: seq[Encodable]): EncodeResult = result.dynamic = true result.data = x.len.toHex(64).toLower var offset = 32*x.len data = "" for i in x: let encoded = encode(i) if encoded.dynamic: result.data &= offset.toHex(64).toLower data &= encoded.data else: result.data &= encoded.data offset += encoded.data.len result.data &= data func decode*[T](input: string, to: seq[T]): seq[T] = var count = input[0..64].decode(Stuint) result = newSeq[T](count) for i in 0..count: result[i] = input[i*64 .. (i+1)*64].decode(T) func encode*(x: openArray[Encodable]): EncodeResult = result.dynamic = false result.data = "" var offset = 32*x.len data = "" for i in x: let encoded = encode(i) if encoded.dynamic: result.data &= offset.toHex(64).toLower data &= encoded.data else: result.data &= encoded.data offset += encoded.data.len func decode*[T; I: static int](input: string, to: array[0..I, T]): array[0..I, T] = for i in 0..I: result[i] = input[i*64 .. (i+1)*64].decode(T)