A simple ABI structure to replace JSON's object when passed between contracts. It is especially useful when interacting with outside oracles, where a JSON would be converted into this notation.
A JSON supports the following object types:
- number (double precision floating point)
- string (double quoted unicode string)
- boolean (true or false)
- array (ordered sequence)
- null
- object (another JSON object)
ETON supports the following types:
- any elementary type supported by the [https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI](Contract ABI)
- special embedded
ETON
element type
Compared to JSON, there is no support for null or untyped arrays.
Note: although the structure shares similarities with the Contract ABI and while it aims to be consistent as closely as possible, it is still optimised towards a serialised representation of larger data. This structure is considered a type as opposed to the elements of it. In short this means that passing an ETON
object in a contract will have to be considered a binary blob and handled similarly to bytes
.
The ETON element type:
- is a key-value mapping
- key is always a string
- value can be of variable type
The basic layout following the ABI is:
<number of elements, uint256>
<key 1, string>
<type 1, uint256>
<value 1, variable>
...
<key N, string>
<type N, uint256>
<value N, variable>
All elementary types are encoded as per the ABI specification. In case the ETON
type is stored, a sub tree is created:
<number of elements, uint256>
<key 1, string>
<type 1, uint256>
<number of elements, uint256>
<key 1, string>
<type 1, uint256>
<value 1, variable>
...
<key N, string>
<type N, uint256>
<value N, variable>
...
<key N, string>
<type N, uint256>
<value N, variable>
There is a depth limit of 4.
The contract ABI does not assign a numerical identifier to the elementary types as they are only referred to in a string format. For ETON
such numerical identifiers must be defined as follows:
id encoding | corresponding type |
---|---|
0x1000000 | embedded ETON |
0x2000000 | bool |
0x3000000 | address |
0x4000000 | bytes |
0x5000000 | string |
0x60000XX | bytes |
0x70000XX | uint |
0x80000XX | int |
0x900XXYY | realx |
0xA00XXYY | urealx |
id | type |
---|---|
0x1 | embedded ETON |
0x2 | bool |
0x3 | address |
0x4 | bytes |
0x5 | string |
0x20 | bytes1 |
0x21 | bytes2 |
... | ... |
0x40 | bytes32 |
0x200 | uint8 |
0x201 | uint16 |
... | ... |
0x220 | uint256 |
0x300 | int8 |
0x301 | int16 |
... | ... |
0x320 | int256 |
0x400 | real8x8 |
0x401 | real8x16 |
... | ... |
0x420 | real16x8 |
... | ... |
0x4ff | real128x128 |
0x500 | ureal8x8 |
0x501 | ureal8x16 |
... | ... |
0x520 | ureal16x8 |
... | ... |
0x5ff | ureal128x128 |
There are two special cases:
- Variable-length array types
For these, the top bit (255th) is set in the identifier. Processing happens according to the ABI: the first data to follow is the array length encoded as uint256.
- Fixed-size array types
For these, the second topmost bit (254th) is set in the identifier. Processing happens exactly as with variable-length arrays: the first data to follow is the array length encoded as uint256.
FIXME: probably only one such bit is needed and it should only treat them one way.
Note that an embedded ETON
cannot be an array at the same time, setting any of the two top bits is invalid.
Examples:
- string:
0000000000000000000000000000000000000000000000000000000000000003
- string[]:
8000000000000000000000000000000000000000000000000000000000000003
- string[N]:
4000000000000000000000000000000000000000000000000000000000000003
- eton[] (invalid!):
8000000000000000000000000000000000000000000000000000000000000001
The above structure is reminiscent of the struct
in Solidity.
The following example construct in Solidity:
struct Backer {
address addr;
uint amount;
}
struct Project {
uint raising;
Backer[] backers;
}
Assuming there is one project raising 2 ETH, and two backers each donating 1 ETH, it can be translated into ETON
as follows:
0000000000000000000000000000000000000000000000000000000000000001 (eton elements)
0000000000000000000000000000000000000000000000000000000000000007 (key 1 - length of string)
aabbccddee000000000000000000000000000000000000000000000000000007 (key 1 - "raising")
00000000000000000000000000000000000000000000000000000000070000ff (type 1 - uint256)
0000000000000000000000000000000000000000200000000000000000000000 (value 1 - 2 eth)
0000000000000000000000000000000000000000000000000000000000000007 (key 1 - length of string)
aabbccddee000000000000000000000000000000000000000000000000000007 (key 1 - "backers")
0000000000000000000000000000000000000000000000000000000001000000 (type 1 - eton)
0000000000000000000000000000000000000000000000000000000000000002 (value 1 - 2 eth)
Take the following simple JSON:
{
"ip": "8.8.8.8",
"google": true
}
We assume that the value of ip is a string and the value of google is boolean. The resulting ETON
is as follows (a healthy 320 bytes):
00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002697000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000007382e382e382e38000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006676f6f676c65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000001
Let's dissect it:
0000000000000000000000000000000000000000000000000000000000000002 (number of eton elements)
0000000000000000000000000000000000000000000000000000000000000002 (key 1 - length of string)
6970000000000000000000000000000000000000000000000000000000000000 (key 1 - "ip")
0000000000000000000000000000000000000000000000000000000004000000 (type 1 - string)
0000000000000000000000000000000000000000000000000000000000000007 (value 1 - length of string)
382e382e382e3800000000000000000000000000000000000000000000000000 (value 1 - "8.8.8.8")
0000000000000000000000000000000000000000000000000000000000000006 (key 2 - length of string)
676f6f676c650000000000000000000000000000000000000000000000000000 (key 2 - "google")
0000000000000000000000000000000000000000000000000000000002000000 (type 2 - bool)
0000000000000000000000000000000000000000000000000000000000000001 (value 2 - true)
In this case both key and values are restricted to strings. The encoding is reminiscent how the Solidity mapping
construct could be encoded:
<number of elements, uint256>
<key 1, string>
<value 1, variable>
...
<key N, string>
<value N, variable>
In this option, the embedded ETON
type cannot be supported, thus limiting the structure to one level.
I find this option less useful and versatile, than the above, but at the same time it is much simpler to parse and uses less memory space.