mirror of
https://github.com/logos-storage/nim-contract-abi.git
synced 2026-01-30 19:33:05 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7b4cecce | ||
|
|
deccf3e1be | ||
|
|
977d6f5fa3 | ||
|
|
b9696af998 | ||
|
|
524bda044b | ||
|
|
c0c0832676 | ||
|
|
36dd1086ab | ||
|
|
9ea526814d | ||
|
|
5a215dc1c2 | ||
|
|
4da6e9cddd | ||
|
|
87894f69aa | ||
|
|
c076aca82c | ||
|
|
1e72c2f10e | ||
|
|
754a59e145 | ||
|
|
c82a70b353 | ||
|
|
a5f676eecc | ||
|
|
61f8f59b39 | ||
|
|
f23f3e8aba | ||
|
|
1ac5eca540 | ||
|
|
1d025ef11e | ||
|
|
df55276f1d | ||
|
|
f4ea25cb57 | ||
|
|
b111c27b61 | ||
|
|
8a170e3261 | ||
|
|
01c11eecb0 | ||
|
|
fd377e59da | ||
|
|
8e46b6b5f3 | ||
|
|
d179f9a11d | ||
|
|
b386a18e3e |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
nim: [stable, 1.4.8, 1.2.14]
|
nim: [2.0.14, stable]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: iffy/install-nim@v3
|
- uses: iffy/install-nim@v3
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
*
|
*
|
||||||
!*/
|
!*/
|
||||||
!*.*
|
!*.*
|
||||||
|
nimble.develop
|
||||||
|
nimble.paths
|
||||||
|
nimbledeps/
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
nim 1.6.0
|
|
||||||
@ -11,7 +11,7 @@ Use the [Nimble][2] package manager to add `contractabi` to an existing project.
|
|||||||
Add the following to its .nimble file:
|
Add the following to its .nimble file:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
requires "contractabi >= 0.4.2 & < 0.5.0"
|
requires "contractabi >= 0.7.1 & < 0.8.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|||||||
4
config.nims
Normal file
4
config.nims
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# begin Nimble config (version 1)
|
||||||
|
when fileExists("nimble.paths"):
|
||||||
|
include "nimble.paths"
|
||||||
|
# end Nimble config
|
||||||
@ -1,10 +1,9 @@
|
|||||||
version = "0.4.2"
|
version = "0.7.3"
|
||||||
author = "Contract ABI Authors"
|
author = "Contract ABI Authors"
|
||||||
description = "ABI Encoding for Ethereum contracts"
|
description = "ABI Encoding for Ethereum contracts"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
requires "stint"
|
requires "stint >= 0.8.1 & < 0.9.0"
|
||||||
requires "stew"
|
requires "stew >= 0.2.0"
|
||||||
requires "nimcrypto >= 0.5.4 & < 0.6.0"
|
requires "nimcrypto >= 0.6.2 & < 0.8.0"
|
||||||
requires "questionable >= 0.10.0 & < 0.11.0"
|
requires "questionable >= 0.10.10 & < 0.11.0"
|
||||||
requires "upraises >= 0.1.0 & < 0.2.0"
|
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import pkg/stew/byteutils
|
import pkg/stew/byteutils
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/upraises
|
|
||||||
|
|
||||||
push: {.upraises: [].}
|
{.push raises:[].}
|
||||||
|
|
||||||
type
|
type
|
||||||
Address* = distinct array[20, byte]
|
Address* = distinct array[20, byte]
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
import std/typetraits
|
||||||
import pkg/stint
|
import pkg/stint
|
||||||
import pkg/stew/endians2
|
import pkg/stew/endians2
|
||||||
import pkg/stew/byteutils
|
import pkg/stew/byteutils
|
||||||
import pkg/questionable/results
|
import pkg/questionable/results
|
||||||
import pkg/upraises
|
|
||||||
import ./encoding
|
import ./encoding
|
||||||
import ./integers
|
import ./integers
|
||||||
import ./address
|
import ./address
|
||||||
@ -10,7 +10,7 @@ import ./address
|
|||||||
export stint
|
export stint
|
||||||
export address
|
export address
|
||||||
|
|
||||||
push: {.upraises:[].}
|
{.push raises:[].}
|
||||||
|
|
||||||
type
|
type
|
||||||
AbiDecoder* = object
|
AbiDecoder* = object
|
||||||
@ -43,11 +43,10 @@ func index(decoder: var AbiDecoder): var int =
|
|||||||
func `index=`(decoder: var AbiDecoder, value: int) =
|
func `index=`(decoder: var AbiDecoder, value: int) =
|
||||||
decoder.currentTuple.index = value
|
decoder.currentTuple.index = value
|
||||||
|
|
||||||
func startTuple(decoder: var AbiDecoder): ?!void =
|
func startTuple*(decoder: var AbiDecoder) =
|
||||||
decoder.stack.add(Tuple.init(decoder.index))
|
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"
|
doAssert decoder.stack.len > 1, "unable to finish a tuple that hasn't started"
|
||||||
let tupl = decoder.stack.pop()
|
let tupl = decoder.stack.pop()
|
||||||
decoder.index = tupl.index
|
decoder.index = tupl.index
|
||||||
@ -134,7 +133,7 @@ func decode(decoder: var AbiDecoder, T: type seq[byte]): ?!T =
|
|||||||
|
|
||||||
func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
|
func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
|
||||||
var tupl: T
|
var tupl: T
|
||||||
?decoder.startTuple()
|
decoder.startTuple()
|
||||||
for element in tupl.fields:
|
for element in tupl.fields:
|
||||||
element = ?decoder.read(typeof(element))
|
element = ?decoder.read(typeof(element))
|
||||||
decoder.finishTuple()
|
decoder.finishTuple()
|
||||||
@ -143,7 +142,7 @@ func decode[T: tuple](decoder: var AbiDecoder, _: typedesc[T]): ?!T =
|
|||||||
func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
|
func decode[T](decoder: var AbiDecoder, _: type seq[T]): ?!seq[T] =
|
||||||
var sequence: seq[T]
|
var sequence: seq[T]
|
||||||
let len = ?decoder.read(uint64)
|
let len = ?decoder.read(uint64)
|
||||||
?decoder.startTuple()
|
decoder.startTuple()
|
||||||
for _ in 0..<len:
|
for _ in 0..<len:
|
||||||
sequence.add(?decoder.read(T))
|
sequence.add(?decoder.read(T))
|
||||||
decoder.finishTuple()
|
decoder.finishTuple()
|
||||||
@ -151,7 +150,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] =
|
func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
|
||||||
var arr: array[I, T]
|
var arr: array[I, T]
|
||||||
?decoder.startTuple()
|
decoder.startTuple()
|
||||||
for i in 0..<arr.len:
|
for i in 0..<arr.len:
|
||||||
arr[i] = ?decoder.read(T)
|
arr[i] = ?decoder.read(T)
|
||||||
decoder.finishTuple()
|
decoder.finishTuple()
|
||||||
@ -160,6 +159,9 @@ func decode[I,T](decoder: var AbiDecoder, _: type array[I,T]): ?!array[I,T] =
|
|||||||
func decode(decoder: var AbiDecoder, T: type string): ?!T =
|
func decode(decoder: var AbiDecoder, T: type string): ?!T =
|
||||||
success string.fromBytes(?decoder.read(seq[byte]))
|
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 =
|
func readOffset(decoder: var AbiDecoder): ?!int =
|
||||||
let offset = ?decoder.read(uint64)
|
let offset = ?decoder.read(uint64)
|
||||||
success decoder.currentTuple.start + offset.int
|
success decoder.currentTuple.start + offset.int
|
||||||
@ -190,3 +192,13 @@ func decode*(_: type AbiDecoder, bytes: seq[byte], T: type): ?!T =
|
|||||||
var value = ?decoder.decode(T)
|
var value = ?decoder.decode(T)
|
||||||
?decoder.finish()
|
?decoder.finish()
|
||||||
success value
|
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
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
|
import std/typetraits
|
||||||
import pkg/stint
|
import pkg/stint
|
||||||
import pkg/upraises
|
|
||||||
import pkg/stew/byteutils
|
import pkg/stew/byteutils
|
||||||
|
import pkg/stew/endians2
|
||||||
import ./integers
|
import ./integers
|
||||||
import ./address
|
import ./address
|
||||||
|
|
||||||
export stint
|
export stint
|
||||||
export address
|
export address
|
||||||
|
|
||||||
push: {.upraises:[].}
|
{.push raises:[].}
|
||||||
|
|
||||||
type
|
type
|
||||||
AbiEncoder* = object
|
AbiEncoder* = object
|
||||||
@ -57,7 +58,7 @@ func postpone(encoder: var AbiEncoder, bytes: seq[byte]) =
|
|||||||
func setDynamic(encoder: var AbiEncoder) =
|
func setDynamic(encoder: var AbiEncoder) =
|
||||||
encoder.stack[^1].dynamic = true
|
encoder.stack[^1].dynamic = true
|
||||||
|
|
||||||
func startTuple(encoder: var AbiEncoder) =
|
func startTuple*(encoder: var AbiEncoder) =
|
||||||
encoder.stack.add(Tuple())
|
encoder.stack.add(Tuple())
|
||||||
|
|
||||||
func encode(encoder: var AbiEncoder, tupl: Tuple) =
|
func encode(encoder: var AbiEncoder, tupl: Tuple) =
|
||||||
@ -67,7 +68,7 @@ func encode(encoder: var AbiEncoder, tupl: Tuple) =
|
|||||||
else:
|
else:
|
||||||
encoder.append(tupl.finish())
|
encoder.append(tupl.finish())
|
||||||
|
|
||||||
func finishTuple(encoder: var AbiEncoder) =
|
func finishTuple*(encoder: var AbiEncoder) =
|
||||||
encoder.encode(encoder.stack.pop())
|
encoder.encode(encoder.stack.pop())
|
||||||
|
|
||||||
func pad(encoder: var AbiEncoder, len: int, padding=0'u8) =
|
func pad(encoder: var AbiEncoder, len: int, padding=0'u8) =
|
||||||
@ -86,7 +87,12 @@ func padright(encoder: var AbiEncoder, bytes: openArray[byte], padding=0'u8) =
|
|||||||
func encode(encoder: var AbiEncoder, value: SomeUnsignedInt | StUint) =
|
func encode(encoder: var AbiEncoder, value: SomeUnsignedInt | StUint) =
|
||||||
encoder.padleft(value.toBytesBE)
|
encoder.padleft(value.toBytesBE)
|
||||||
|
|
||||||
func encode(encoder: var AbiEncoder, value: SomeSignedInt | StInt) =
|
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) =
|
||||||
let bytes = value.unsigned.toBytesBE
|
let bytes = value.unsigned.toBytesBE
|
||||||
let padding = if value.isNegative: 0xFF'u8 else: 0x00'u8
|
let padding = if value.isNegative: 0xFF'u8 else: 0x00'u8
|
||||||
encoder.padleft(bytes, padding)
|
encoder.padleft(bytes, padding)
|
||||||
@ -131,25 +137,29 @@ func encode(encoder: var AbiEncoder, tupl: tuple) =
|
|||||||
encoder.write(element)
|
encoder.write(element)
|
||||||
encoder.finishTuple()
|
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) =
|
func write*[T](encoder: var AbiEncoder, value: T) =
|
||||||
var writer = AbiEncoder.init()
|
var writer = AbiEncoder.init()
|
||||||
writer.encode(value)
|
writer.encode(value)
|
||||||
encoder.encode(writer.stack[0])
|
encoder.encode(writer.finish())
|
||||||
|
|
||||||
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] =
|
func encode*[T](_: type AbiEncoder, value: T): seq[byte] =
|
||||||
var encoder = AbiEncoder.init()
|
var encoder = AbiEncoder.init()
|
||||||
encoder.write(value)
|
encoder.write(value)
|
||||||
encoder.finish()
|
encoder.finish().bytes
|
||||||
|
|
||||||
proc isDynamic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
|
proc isDynamic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
|
||||||
var encoder = AbiEncoder.init()
|
var encoder = AbiEncoder.init()
|
||||||
encoder.encode(T.default)
|
encoder.write(T.default)
|
||||||
encoder.stack[^1].dynamic
|
encoder.finish().dynamic
|
||||||
|
|
||||||
proc isStatic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
|
proc isStatic*(_: type AbiEncoder, T: type): bool {.compileTime.} =
|
||||||
not AbiEncoder.isDynamic(T)
|
not AbiEncoder.isDynamic(T)
|
||||||
|
|||||||
@ -14,4 +14,4 @@ func unsigned*(value: SomeSignedInt): SomeUnsignedInt =
|
|||||||
cast[typeof(value).unsigned](value)
|
cast[typeof(value).unsigned](value)
|
||||||
|
|
||||||
func unsigned*[bits](value: StInt[bits]): StUint[bits] =
|
func unsigned*[bits](value: StInt[bits]): StUint[bits] =
|
||||||
value.stuint(bits)
|
cast[typeof(value).unsigned](value)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import std/strutils
|
import std/strutils
|
||||||
|
import std/typetraits
|
||||||
import pkg/nimcrypto
|
import pkg/nimcrypto
|
||||||
import pkg/stint
|
import pkg/stint
|
||||||
import pkg/stew/byteutils
|
import pkg/stew/byteutils
|
||||||
@ -37,10 +38,14 @@ solidityType Int256, "int256"
|
|||||||
solidityType bool, "bool"
|
solidityType bool, "bool"
|
||||||
solidityType string, "string"
|
solidityType string, "string"
|
||||||
solidityType Address, "address"
|
solidityType Address, "address"
|
||||||
|
solidityType enum, "uint8"
|
||||||
|
|
||||||
func solidityType*[N: static int, T](_: type array[N, T]): string =
|
func solidityType*[N: static int, T](_: type array[N, T]): string =
|
||||||
when T is byte:
|
when T is byte:
|
||||||
"bytes" & $N
|
when 0 < N and N <= 32:
|
||||||
|
"bytes" & $N
|
||||||
|
else:
|
||||||
|
"bytes1[" & $N & "]"
|
||||||
else:
|
else:
|
||||||
solidityType(T) & "[" & $N & "]"
|
solidityType(T) & "[" & $N & "]"
|
||||||
|
|
||||||
@ -56,6 +61,9 @@ func solidityType*(Tuple: type tuple): string =
|
|||||||
names.add(solidityType(typeof parameter))
|
names.add(solidityType(typeof parameter))
|
||||||
"(" & names.join(",") & ")"
|
"(" & names.join(",") & ")"
|
||||||
|
|
||||||
|
func solidityType*[T: distinct](_: type T): string =
|
||||||
|
solidityType(distinctBase T)
|
||||||
|
|
||||||
func signature*(function: string, Parameters: type tuple = ()): string =
|
func signature*(function: string, Parameters: type tuple = ()): string =
|
||||||
function & solidityType(Parameters)
|
function & solidityType(Parameters)
|
||||||
|
|
||||||
|
|||||||
72
nimble.lock
Normal file
72
nimble.lock
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
3
tests/contractabi/config.nims
Normal file
3
tests/contractabi/config.nims
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
switch("path", "..")
|
||||||
|
when (NimMajor, NimMinor, NimPatch) >= (1, 6, 11):
|
||||||
|
switch("warning", "BareExcept:off")
|
||||||
@ -20,7 +20,7 @@ proc example*[T](_: type seq[T], len = 0..5): seq[T] =
|
|||||||
newSeqWith(chosenlen, T.example)
|
newSeqWith(chosenlen, T.example)
|
||||||
|
|
||||||
proc example*(T: type StUint): T =
|
proc example*(T: type StUint): T =
|
||||||
T.fromBytes(array[sizeof(T), byte].example)
|
T.fromBytesBE(array[sizeof(T), byte].example)
|
||||||
|
|
||||||
proc example*(T: type StInt): T =
|
proc example*(T: type StInt): T =
|
||||||
cast[T](StUint[T.bits].example)
|
cast[T](StUint[T.bits].example)
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
--path:"../.."
|
|
||||||
@ -2,30 +2,94 @@ import std/unittest
|
|||||||
import pkg/questionable/results
|
import pkg/questionable/results
|
||||||
import contractabi
|
import contractabi
|
||||||
|
|
||||||
type CustomType = object
|
type
|
||||||
a: uint16
|
Custom1 = object
|
||||||
b: string
|
a: uint16
|
||||||
|
b: string
|
||||||
|
Custom2 = object
|
||||||
|
a: uint16
|
||||||
|
b: string
|
||||||
|
Custom3 = object
|
||||||
|
a: uint16
|
||||||
|
b: string
|
||||||
|
Custom4 = object
|
||||||
|
a: uint16
|
||||||
|
b: string
|
||||||
|
|
||||||
func encode(encoder: var AbiEncoder, custom: CustomType) =
|
func encode(encoder: var AbiEncoder, custom: Custom1) =
|
||||||
encoder.write( (custom.a, custom.b) )
|
encoder.write( (custom.a, custom.b) )
|
||||||
|
|
||||||
func decode(decoder: var AbiDecoder, T: type CustomType): ?!T =
|
func decode(decoder: var AbiDecoder, T: type Custom1): ?!T =
|
||||||
let (a, b) = ?decoder.read( (uint16, string) )
|
let (a, b) = ?decoder.read( (uint16, string) )
|
||||||
success CustomType(a: a, b: b)
|
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
|
||||||
|
|
||||||
suite "custom types":
|
suite "custom types":
|
||||||
|
|
||||||
let custom = CustomType(a: 42, b: "ultimate answer")
|
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")
|
||||||
|
|
||||||
test "can be encoded":
|
test "can be encoded":
|
||||||
check AbiEncoder.encode(custom) == AbiEncoder.encode( (custom.a, custom.b) )
|
check:
|
||||||
|
AbiEncoder.encode(custom1) == AbiEncoder.encode( (custom1.a, custom1.b) )
|
||||||
|
|
||||||
test "can be decoded":
|
test "can be decoded":
|
||||||
let encoding = AbiEncoder.encode(custom)
|
let encoding = AbiEncoder.encode(custom1)
|
||||||
check AbiDecoder.decode(encoding, CustomType) == success custom
|
check AbiDecoder.decode(encoding, Custom1) == success custom1
|
||||||
|
|
||||||
test "can be embedded in tuples, arrays and sequences":
|
test "can be embedded in tuples, arrays and sequences":
|
||||||
let embedding = (custom, [custom], @[custom])
|
let embedding = (custom1, [custom1], @[custom1])
|
||||||
let encoding = AbiEncoder.encode(embedding)
|
let encoding = AbiEncoder.encode(embedding)
|
||||||
let decoded = AbiDecoder.decode(encoding, typeof(embedding))
|
let decoded = AbiDecoder.decode(encoding, typeof(embedding))
|
||||||
check !decoded == 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
|
||||||
|
|||||||
@ -3,6 +3,10 @@ import pkg/questionable/results
|
|||||||
import contractabi
|
import contractabi
|
||||||
import ./examples
|
import ./examples
|
||||||
|
|
||||||
|
type SomeDistinctType = distinct uint16
|
||||||
|
func `==`*(a, b: SomeDistinctType): bool =
|
||||||
|
uint16(a) == uint16(b)
|
||||||
|
|
||||||
suite "ABI decoding":
|
suite "ABI decoding":
|
||||||
|
|
||||||
proc checkDecode[T](value: T) =
|
proc checkDecode[T](value: T) =
|
||||||
@ -140,3 +144,6 @@ suite "ABI decoding":
|
|||||||
|
|
||||||
test "decodes strings":
|
test "decodes strings":
|
||||||
checkDecode("hello!☺")
|
checkDecode("hello!☺")
|
||||||
|
|
||||||
|
test "decodes distinct types as their base type":
|
||||||
|
checkDecode(SomeDistinctType(0xAABB'u16))
|
||||||
|
|||||||
@ -138,6 +138,11 @@ suite "ABI encoding":
|
|||||||
test "encodes strings as UTF-8 byte sequence":
|
test "encodes strings as UTF-8 byte sequence":
|
||||||
check AbiEncoder.encode("hello!☺") == AbiEncoder.encode("hello!☺".toBytes)
|
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":
|
test "can determine whether types are dynamic or static":
|
||||||
check static AbiEncoder.isStatic(uint8)
|
check static AbiEncoder.isStatic(uint8)
|
||||||
check static AbiEncoder.isDynamic(seq[byte])
|
check static AbiEncoder.isDynamic(seq[byte])
|
||||||
|
|||||||
@ -4,6 +4,10 @@ import pkg/contractabi
|
|||||||
|
|
||||||
suite "function selector":
|
suite "function selector":
|
||||||
|
|
||||||
|
type SomeEnum = enum
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
|
||||||
test "translates nim types into solidity types":
|
test "translates nim types into solidity types":
|
||||||
check solidityType(uint8) == "uint8"
|
check solidityType(uint8) == "uint8"
|
||||||
check solidityType(uint16) == "uint16"
|
check solidityType(uint16) == "uint16"
|
||||||
@ -22,10 +26,18 @@ suite "function selector":
|
|||||||
check solidityType(Address) == "address"
|
check solidityType(Address) == "address"
|
||||||
check solidityType(array[4, byte]) == "bytes4"
|
check solidityType(array[4, byte]) == "bytes4"
|
||||||
check solidityType(array[16, byte]) == "bytes16"
|
check solidityType(array[16, byte]) == "bytes16"
|
||||||
|
check solidityType(array[32, byte]) == "bytes32"
|
||||||
|
check solidityType(array[0, byte]) == "bytes1[0]"
|
||||||
|
check solidityType(array[33, byte]) == "bytes1[33]"
|
||||||
check solidityType(seq[byte]) == "bytes"
|
check solidityType(seq[byte]) == "bytes"
|
||||||
check solidityType(array[4, string]) == "string[4]"
|
check solidityType(array[4, string]) == "string[4]"
|
||||||
check solidityType(seq[string]) == "string[]"
|
check solidityType(seq[string]) == "string[]"
|
||||||
check solidityType((Address, string, bool)) == "(address,string,bool)"
|
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":
|
test "calculates solidity function selector":
|
||||||
check $selector("transfer", (Address, UInt256)) == "0xa9059cbb"
|
check $selector("transfer", (Address, UInt256)) == "0xa9059cbb"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user