Compare commits

..

No commits in common. "main" and "0.4.3" have entirely different histories.
main ... 0.4.3

18 changed files with 46 additions and 237 deletions

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
nim: [2.0.14, stable]
nim: [stable, 1.4.8, 1.2.14]
steps:
- uses: actions/checkout@v2
- uses: iffy/install-nim@v3

3
.gitignore vendored
View File

@ -1,6 +1,3 @@
*
!*/
!*.*
nimble.develop
nimble.paths
nimbledeps/

View File

@ -11,7 +11,7 @@ Use the [Nimble][2] package manager to add `contractabi` to an existing project.
Add the following to its .nimble file:
```nim
requires "contractabi >= 0.7.1 & < 0.8.0"
requires "contractabi >= 0.4.3 & < 0.5.0"
```
Usage

View File

@ -1,4 +0,0 @@
# begin Nimble config (version 1)
when fileExists("nimble.paths"):
include "nimble.paths"
# end Nimble config

View File

@ -1,9 +1,10 @@
version = "0.7.3"
version = "0.4.3"
author = "Contract ABI Authors"
description = "ABI Encoding for Ethereum contracts"
license = "MIT"
requires "stint >= 0.8.1 & < 0.9.0"
requires "stew >= 0.2.0"
requires "nimcrypto >= 0.6.2 & < 0.8.0"
requires "questionable >= 0.10.10 & < 0.11.0"
requires "stint"
requires "stew"
requires "nimcrypto >= 0.5.4 & < 0.6.0"
requires "questionable >= 0.10.0 & < 0.11.0"
requires "upraises >= 0.1.0 & < 0.2.0"

View File

@ -1,7 +1,8 @@
import pkg/stew/byteutils
import pkg/questionable
import pkg/upraises
{.push raises:[].}
push: {.upraises: [].}
type
Address* = distinct array[20, byte]

View File

@ -1,8 +1,8 @@
import std/typetraits
import pkg/stint
import pkg/stew/endians2
import pkg/stew/byteutils
import pkg/questionable/results
import pkg/upraises
import ./encoding
import ./integers
import ./address
@ -10,7 +10,7 @@ import ./address
export stint
export address
{.push raises:[].}
push: {.upraises:[].}
type
AbiDecoder* = object
@ -43,10 +43,11 @@ func index(decoder: var AbiDecoder): var int =
func `index=`(decoder: var AbiDecoder, value: int) =
decoder.currentTuple.index = value
func startTuple*(decoder: var AbiDecoder) =
func startTuple(decoder: var AbiDecoder): ?!void =
decoder.stack.add(Tuple.init(decoder.index))
success()
func finishTuple*(decoder: var AbiDecoder) =
func finishTuple(decoder: var AbiDecoder) =
doAssert decoder.stack.len > 1, "unable to finish a tuple that hasn't started"
let tupl = decoder.stack.pop()
decoder.index = tupl.index
@ -133,7 +134,7 @@ func decode(decoder: var AbiDecoder, T: type seq[byte]): ?!T =
func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
var tupl: T
decoder.startTuple()
?decoder.startTuple()
for element in tupl.fields:
element = ?decoder.read(typeof(element))
decoder.finishTuple()
@ -142,7 +143,7 @@ func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
var sequence: seq[T]
let len = ?decoder.read(uint64)
decoder.startTuple()
?decoder.startTuple()
for _ in 0..<len:
sequence.add(?decoder.read(T))
decoder.finishTuple()
@ -150,7 +151,7 @@ func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
var arr: array[I, T]
decoder.startTuple()
?decoder.startTuple()
for i in 0..<arr.len:
arr[i] = ?decoder.read(T)
decoder.finishTuple()
@ -159,9 +160,6 @@ func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
func decode(decoder: var AbiDecoder, T: type string): ?!T =
success string.fromBytes(?decoder.read(seq[byte]))
func decode[T: distinct](decoder: var AbiDecoder, _: type T): ?!T =
success T(?decoder.read(distinctBase T))
func readOffset(decoder: var AbiDecoder): ?!int =
let offset = ?decoder.read(uint64)
success decoder.currentTuple.start + offset.int
@ -192,13 +190,3 @@ func decode*(_: type AbiDecoder, bytes: seq[byte], T: type): ?!T =
var value = ?decoder.decode(T)
?decoder.finish()
success value
func decodeRecord*(_: type AbiDecoder, bytes: seq[byte], T: type): ?!T =
var decoder = AbiDecoder.init(bytes)
var res: T
decoder.startTuple()
for value in res.fields:
value = ?decoder.read(typeof(value))
decoder.finishTuple()
?decoder.finish()
success res

View File

@ -1,14 +1,13 @@
import std/typetraits
import pkg/stint
import pkg/upraises
import pkg/stew/byteutils
import pkg/stew/endians2
import ./integers
import ./address
export stint
export address
{.push raises:[].}
push: {.upraises:[].}
type
AbiEncoder* = object
@ -58,7 +57,7 @@ func postpone(encoder: var AbiEncoder, bytes: seq[byte]) =
func setDynamic(encoder: var AbiEncoder) =
encoder.stack[^1].dynamic = true
func startTuple*(encoder: var AbiEncoder) =
func startTuple(encoder: var AbiEncoder) =
encoder.stack.add(Tuple())
func encode(encoder: var AbiEncoder, tupl: Tuple) =
@ -68,7 +67,7 @@ func encode(encoder: var AbiEncoder, tupl: Tuple) =
else:
encoder.append(tupl.finish())
func finishTuple*(encoder: var AbiEncoder) =
func finishTuple(encoder: var AbiEncoder) =
encoder.encode(encoder.stack.pop())
func pad(encoder: var AbiEncoder, len: int, padding=0'u8) =
@ -87,12 +86,7 @@ func padright(encoder: var AbiEncoder, bytes: openArray[byte], padding=0'u8) =
func encode(encoder: var AbiEncoder, value: SomeUnsignedInt | StUint) =
encoder.padleft(value.toBytesBE)
func encode(encoder: var AbiEncoder, value: SomeSignedInt) =
let bytes = value.unsigned.toBytesBE
let padding = if value < 0: 0xFF'u8 else: 0x00'u8
encoder.padleft(bytes, padding)
func encode(encoder: var AbiEncoder, value: StInt) =
func encode(encoder: var AbiEncoder, value: SomeSignedInt | StInt) =
let bytes = value.unsigned.toBytesBE
let padding = if value.isNegative: 0xFF'u8 else: 0x00'u8
encoder.padleft(bytes, padding)
@ -137,29 +131,25 @@ func encode(encoder: var AbiEncoder, tupl: tuple) =
encoder.write(element)
encoder.finishTuple()
func encode(encoder: var AbiEncoder, value: distinct) =
type Base = distinctBase(typeof(value))
encoder.write(Base(value))
func finish(encoder: var AbiEncoder): Tuple =
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]
func write*[T](encoder: var AbiEncoder, value: T) =
var writer = AbiEncoder.init()
writer.encode(value)
encoder.encode(writer.finish())
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().bytes
encoder.finish()
proc isDynamic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
var encoder = AbiEncoder.init()
encoder.write(T.default)
encoder.finish().dynamic
encoder.encode(T.default)
encoder.stack[^1].dynamic
proc isStatic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
not AbiEncoder.isDynamic(T)

View File

@ -14,4 +14,4 @@ func unsigned*(value: SomeSignedInt): SomeUnsignedInt =
cast[typeof(value).unsigned](value)
func unsigned*[bits](value: StInt[bits]): StUint[bits] =
cast[typeof(value).unsigned](value)
value.stuint(bits)

View File

@ -1,5 +1,4 @@
import std/strutils
import std/typetraits
import pkg/nimcrypto
import pkg/stint
import pkg/stew/byteutils
@ -38,7 +37,6 @@ solidityType Int256, "int256"
solidityType bool, "bool"
solidityType string, "string"
solidityType Address, "address"
solidityType enum, "uint8"
func solidityType*[N: static int, T](_: type array[N, T]): string =
when T is byte:
@ -61,9 +59,6 @@ func solidityType*(Tuple: type tuple): string =
names.add(solidityType(typeof parameter))
"(" & names.join(",") & ")"
func solidityType*[T: distinct](_: type T): string =
solidityType(distinctBase T)
func signature*(function: string, Parameters: type tuple = ()): string =
function & solidityType(Parameters)

View File

@ -1,72 +0,0 @@
{
"version": 2,
"packages": {
"nimcrypto": {
"version": "0.7.2",
"vcsRevision": "8085515e717b07e29de1ef50d5e9f15a3f6004c0",
"url": "https://github.com/cheatfate/nimcrypto",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "8ed2a20f2efaa08782bb871284cbcc3100ca1dea"
}
},
"questionable": {
"version": "0.10.15",
"vcsRevision": "82d90b67bcfb7f2e918b61dace2ff1a4ced60935",
"url": "https://github.com/codex-storage/questionable",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "3238ff637c7b44d2fa8fcb839a8ded968e389de3"
}
},
"results": {
"version": "0.5.1",
"vcsRevision": "df8113dda4c2d74d460a8fa98252b0b771bf1f27",
"url": "https://github.com/arnetheduck/nim-results",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "a9c011f74bc9ed5c91103917b9f382b12e82a9e7"
}
},
"unittest2": {
"version": "0.2.4",
"vcsRevision": "8b51e99b4a57fcfb31689230e75595f024543024",
"url": "https://github.com/status-im/nim-unittest2",
"downloadMethod": "git",
"dependencies": [],
"checksums": {
"sha1": "746106a4dfefffce497f1693733f1c1513b5c62c"
}
},
"stew": {
"version": "0.4.1",
"vcsRevision": "e5740014961438610d336cd81706582dbf2c96f0",
"url": "https://github.com/status-im/nim-stew",
"downloadMethod": "git",
"dependencies": [
"results",
"unittest2"
],
"checksums": {
"sha1": "996d9c058ee078d0209a5f539424a0235683918c"
}
},
"stint": {
"version": "0.8.2",
"vcsRevision": "470b7892561b5179ab20bd389a69217d6213fe58",
"url": "https://github.com/status-im/nim-stint",
"downloadMethod": "git",
"dependencies": [
"stew",
"unittest2"
],
"checksums": {
"sha1": "d8f871fd617e7857192d4609fe003b48942a8ae5"
}
}
},
"tasks": {}
}

View File

@ -1,3 +0,0 @@
switch("path", "..")
when (NimMajor, NimMinor, NimPatch) >= (1, 6, 11):
switch("warning", "BareExcept:off")

View File

@ -20,7 +20,7 @@ proc example*[T](_: type seq[T], len = 0..5): seq[T] =
newSeqWith(chosenlen, T.example)
proc example*(T: type StUint): T =
T.fromBytesBE(array[sizeof(T), byte].example)
T.fromBytes(array[sizeof(T), byte].example)
proc example*(T: type StInt): T =
cast[T](StUint[T.bits].example)

View File

@ -0,0 +1 @@
--path:"../.."

View File

@ -2,94 +2,30 @@ import std/unittest
import pkg/questionable/results
import contractabi
type
Custom1 = object
a: uint16
b: string
Custom2 = object
a: uint16
b: string
Custom3 = object
a: uint16
b: string
Custom4 = object
a: uint16
b: string
type CustomType = object
a: uint16
b: string
func encode(encoder: var AbiEncoder, custom: Custom1) =
func encode(encoder: var AbiEncoder, custom: CustomType) =
encoder.write( (custom.a, custom.b) )
func decode(decoder: var AbiDecoder, T: type Custom1): ?!T =
func decode(decoder: var AbiDecoder, T: type CustomType): ?!T =
let (a, b) = ?decoder.read( (uint16, string) )
success Custom1(a: a, b: b)
func encode(encoder: var AbiEncoder, custom: Custom2) =
encoder.startTuple()
encoder.write(custom.a)
encoder.write(custom.b)
encoder.finishTuple()
func decode(decoder: var AbiDecoder, T: type Custom2): ?!T =
var custom: T
decoder.startTuple()
custom.a = ?decoder.read(uint16)
custom.b = ?decoder.read(string)
decoder.finishTuple()
success custom
func encode(encoder: var AbiEncoder, custom: Custom3) =
encoder.startTuple()
encoder.write(custom.a)
encoder.write(custom.b)
# missing: encoder.finishTuple()
func decode(decoder: var AbiDecoder, T: type Custom3): ?!T =
var custom: T
decoder.startTuple()
custom.a = ?decoder.read(uint16)
custom.b = ?decoder.read(string)
# missing: decoder.finishTuple()
success custom
func encode(encoder: var AbiEncoder, custom: Custom4) =
encoder.write( (custom.a, custom.b) )
func decode(decoder: var AbiDecoder, T: type Custom4): ?!T =
let custom = ?AbiDecoder.decodeRecord(bytes, Custom4)
success custom
success CustomType(a: a, b: b)
suite "custom types":
let custom1 = Custom1(a: 42, b: "ultimate answer")
let custom2 = Custom2(a: 42, b: "ultimate answer")
let custom3 = Custom3(a: 42, b: "ultimate answer")
let custom4 = Custom4(a: 42, b: "ultimate answer")
let custom = CustomType(a: 42, b: "ultimate answer")
test "can be encoded":
check:
AbiEncoder.encode(custom1) == AbiEncoder.encode( (custom1.a, custom1.b) )
check AbiEncoder.encode(custom) == AbiEncoder.encode( (custom.a, custom.b) )
test "can be decoded":
let encoding = AbiEncoder.encode(custom1)
check AbiDecoder.decode(encoding, Custom1) == success custom1
let encoding = AbiEncoder.encode(custom)
check AbiDecoder.decode(encoding, CustomType) == success custom
test "can be embedded in tuples, arrays and sequences":
let embedding = (custom1, [custom1], @[custom1])
let embedding = (custom, [custom], @[custom])
let encoding = AbiEncoder.encode(embedding)
let decoded = AbiDecoder.decode(encoding, typeof(embedding))
check !decoded == embedding
test "can use startTuple() and finishTuple()":
let encoding = AbiEncoder.encode(custom2)
check AbiDecoder.decode(encoding, Custom2) == success custom2
test "fail when finishTuple() is missing":
expect Exception:
discard AbiEncoder.encode(custom3)
let encoding = AbiEncoder.encode( (custom3.a, custom3.b) )
expect Exception:
discard AbiDecoder.decode(encoding, Custom3)
test "generic decode works":
let encoding = AbiEncoder.encode(custom4)
check AbiDecoder.decodeRecord(encoding, Custom4) == success custom4

View File

@ -3,10 +3,6 @@ import pkg/questionable/results
import contractabi
import ./examples
type SomeDistinctType = distinct uint16
func `==`*(a, b: SomeDistinctType): bool =
uint16(a) == uint16(b)
suite "ABI decoding":
proc checkDecode[T](value: T) =
@ -144,6 +140,3 @@ suite "ABI decoding":
test "decodes strings":
checkDecode("hello!☺")
test "decodes distinct types as their base type":
checkDecode(SomeDistinctType(0xAABB'u16))

View File

@ -138,11 +138,6 @@ suite "ABI encoding":
test "encodes strings as UTF-8 byte sequence":
check AbiEncoder.encode("hello!☺") == AbiEncoder.encode("hello!☺".toBytes)
test "encodes distinct types as their base type":
type SomeDistinctType = distinct uint16
let value = 0xAABB'u16
check AbiEncoder.encode(SomeDistinctType(value)) == AbiEncoder.encode(value)
test "can determine whether types are dynamic or static":
check static AbiEncoder.isStatic(uint8)
check static AbiEncoder.isDynamic(seq[byte])

View File

@ -4,10 +4,6 @@ import pkg/contractabi
suite "function selector":
type SomeEnum = enum
One
Two
test "translates nim types into solidity types":
check solidityType(uint8) == "uint8"
check solidityType(uint16) == "uint16"
@ -33,11 +29,6 @@ suite "function selector":
check solidityType(array[4, string]) == "string[4]"
check solidityType(seq[string]) == "string[]"
check solidityType((Address, string, bool)) == "(address,string,bool)"
check solidityType(SomeEnum) == "uint8"
test "translates distinct type into base type":
type SomeDistinctType = distinct uint16
check solidityType(SomeDistinctType) == solidityType(uint16)
test "calculates solidity function selector":
check $selector("transfer", (Address, UInt256)) == "0xa9059cbb"