nim-nitro/tests/nitro/testAbi.nim
Mark Spanbroek 38a6e6ab95 Fix ABI encoding of tuples
Offset of dynamic elements is relative to the start of the tuple,
not relative to the start of the element.
2021-02-23 11:32:41 +01:00

162 lines
4.8 KiB
Nim

import std/unittest
import pkg/nitro/abi
import pkg/nitro/types
import pkg/stint
import ./examples
suite "ABI encoding":
proc zeroes(amount: int): seq[byte] =
newSeq[byte](amount)
test "encodes uint8":
check Abi.encode(42'u8) == 31.zeroes & 42'u8
test "encodes booleans":
check Abi.encode(false) == 31.zeroes & 0'u8
check Abi.encode(true) == 31.zeroes & 1'u8
test "encodes uint16, 32, 64":
check Abi.encode(0xABCD'u16) ==
30.zeroes & 0xAB'u8 & 0xCD'u8
check Abi.encode(0x11223344'u32) ==
28.zeroes & 0x11'u8 & 0x22'u8 & 0x33'u8 & 0x44'u8
check Abi.encode(0x1122334455667788'u64) ==
24.zeroes &
0x11'u8 & 0x22'u8 & 0x33'u8 & 0x44'u8 &
0x55'u8 & 0x66'u8 & 0x77'u8 & 0x88'u8
test "encodes ranges":
type SomeRange = range[0x0000'u16..0xAAAA'u16]
check Abi.encode(SomeRange(0x1122)) == 30.zeroes & 0x11'u8 & 0x22'u8
test "encodes enums":
type SomeEnum = enum
one = 1
two = 2
check Abi.encode(one) == 31.zeroes & 1'u8
check Abi.encode(two) == 31.zeroes & 2'u8
test "encodes stints":
let uint256 = UInt256.example
check Abi.encode(uint256) == @(uint256.toBytesBE)
let uint128 = UInt128.example
check Abi.encode(uint128) == 16.zeroes & @(uint128.toBytesBE)
test "encodes byte arrays":
let bytes3 = [1'u8, 2'u8, 3'u8]
check Abi.encode(bytes3) == @bytes3 & 29.zeroes
let bytes32 = array[32, byte].example
check Abi.encode(bytes32) == @bytes32
let bytes33 = array[33, byte].example
check Abi.encode(bytes33) == @bytes33 & 31.zeroes
test "encodes byte sequences":
let bytes3 = @[1'u8, 2'u8, 3'u8]
let bytes3len = Abi.encode(bytes3.len.uint64)
check Abi.encode(bytes3) == bytes3len & bytes3 & 29.zeroes
let bytes32 = @(array[32, byte].example)
let bytes32len = Abi.encode(bytes32.len.uint64)
check Abi.encode(bytes32) == bytes32len & bytes32
let bytes33 = @(array[33, byte].example)
let bytes33len = Abi.encode(bytes33.len.uint64)
check Abi.encode(bytes33) == bytes33len & bytes33 & 31.zeroes
test "encodes ethereum addresses":
let address = EthAddress.example
check Abi.encode(address) == 12.zeroes & @(address.toArray)
test "encodes tuples":
let a = true
let b = @[1'u8, 2'u8, 3'u8]
let c = 0xAABBCCDD'u32
let d = @[4'u8, 5'u8, 6'u8]
var writer: AbiWriter
writer.startTuple()
writer.write(a)
writer.write(b)
writer.write(c)
writer.write(d)
writer.finishTuple()
check writer.finish() ==
Abi.encode(a) &
Abi.encode(4 * 32'u8) & # offset from start of tuple
Abi.encode(c) &
Abi.encode(6 * 32'u8) & # offset from start of tuple
Abi.encode(b) &
Abi.encode(d)
test "encodes nested tuples":
let a = true
let b = @[1'u8, 2'u8, 3'u8]
let c = 0xAABBCCDD'u32
let d = @[4'u8, 5'u8, 6'u8]
var writer: AbiWriter
writer.startTuple()
writer.write(a)
writer.write(b)
writer.startTuple()
writer.write(c)
writer.write(d)
writer.finishTuple()
writer.finishTuple()
check writer.finish() ==
Abi.encode(a) &
Abi.encode(6 * 32'u8) & # offset from start of tuple
Abi.encode(c) &
Abi.encode(2 * 32'u8) & # offset from start of tuple
Abi.encode(d) &
Abi.encode(b)
test "encodes arrays":
let element1 = seq[byte].example
let element2 = seq[byte].example
var expected: AbiWriter
expected.startTuple()
expected.write(element1)
expected.write(element2)
expected.finishTuple()
check Abi.encode([element1, element2]) == expected.finish()
test "encodes sequences":
let element1 = seq[byte].example
let element2 = seq[byte].example
var expected: AbiWriter
expected.write(2'u8)
expected.startTuple()
expected.write(element1)
expected.write(element2)
expected.finishTuple()
check Abi.encode(@[element1, element2]) == expected.finish()
test "encodes sequence as dynamic element":
let s = @[42.u256, 43.u256]
var writer: AbiWriter
writer.startTuple()
writer.write(s)
writer.finishTuple()
check writer.finish() ==
Abi.encode(32'u8) & # offset from start of tuple
Abi.encode(s)
test "encodes array of static elements as static element":
let a = [[42'u8], [43'u8]]
var writer: AbiWriter
writer.startTuple()
writer.write(a)
writer.finishTuple()
check writer.finish() == Abi.encode(a)
test "encodes array of dynamic elements as dynamic element":
let a = [@[42'u8], @[43'u8]]
var writer: AbiWriter
writer.startTuple()
writer.write(a)
writer.finishTuple()
check writer.finish() ==
Abi.encode(32'u8) & # offset from start of tuple
Abi.encode(a)
# https://medium.com/b2expand/abi-encoding-explanation-4f470927092d
# https://docs.soliditylang.org/en/v0.8.1/abi-spec.html#formal-specification-of-the-encoding