Appendix C.1: Application Binary Interface (ABI) Specification
In this chapter, we will cover the specification of the Application Binary Interface (ABI) and provide an example that showcase how the final ABI looks like in JSON format.
JSON ABI Specification
The ABI of a contract is represented as a JSON object containing the following properties:
"storage"
This is an array that describes every storage variable in the contract, i.e., every variable
declared in the storage { .. }
block. Each entry in this array is a JSON object that contains the
following properties:
"name"
: a string representing the name of the storage variable."ty"
: a JSON object representing the type of the storage variable. This is further explained in JSON Representation of Types.
"predicates"
This is an array that describes every predicate in the contract. Each entry in this array is a JSON object that contains the following properties:
"name"
: a string representing the name of the predicate."params"
: an array that contains the parameters of the predicate. Each entry in this array is a JSON object that contains the following properties:"name"
: a string representing the name of the parameter."ty"
: a JSON object representing the type of the parameter. This is further explained in JSON Representation of Types.
Note: The order in which the predicate parameters show up in the JSON is important and must match the order in which they are declared in the Pint code. When constructing a solution, that same order should also be respected.
JSON Representation of Types
Each possible Pint type is represented in the ABI as a JSON object with properties that depend on the type. Below is a list of the JSON objects for each possible type:
int
"Int"
bool
"Bool"
b256
"B256"
Union
{
"Union": {
"name": <union_name>,
"variants": [
{
"name": <variant1_name>,
"ty": <variant1_ty>
},
{
"name": <variant2_name>,
"ty": <variant2_ty>
},
...
]
}
}
In the above, <variant1_name>
, <variant2_name>
, ... are strings representing the names of the
union variants. <variant1_ty>
, <variant2_ty>
, ... are JSON objects representing the types of the
tuple fields, formatted according to the rules of this section. These are optional, that is, they
can be set to null
if the corresponding variants don't hold any values.
Tuple
{
"Tuple": [
{
"name": <field1_name>,
"ty": <field1_ty>
}
{
"name": <field2_name>,
"ty": <field2_ty>
}
...
]
}
In the above, <field1_name>
, <field2_name>
, ... are strings representing the names of the tuple
fields. These are optional, that is, they can be set to null
if the corresponding tuple fields
have no names. <field1_ty.
, <field2_ty>
, ... are JSON objects representing the types of the
tuple fields, formatted according to the rules of this section.
Array
{
"Array": {
"ty": <element_ty>,
"size": <array_size>
}
}
In the above, <element_ty>
is a JSON object representing the type of each element in the array,
formatted according to the rules of this section. <array_size>
is an integer representing the size
of the array.
Storage Map
{
"Map": {
"ty_from": <ty_from>,
"ty_to": <ty_to>,
}
}
In the above, <ty_from>
and <ty_to>
are JSON objects representing the "from" type and the "to"
type in the map, formatted according to the rules of this section.
Example
Here's an example contract and its corresponding JSON ABI:
union U = A(int) | B | C(int[3]);
storage {
s0: b256,
s1: { int, int },
my_map: ( int => { int, int } ),
my_union: U
}
predicate foo(
v0: int,
v1: bool[5],
v2: U,
v3: { int, int }[5],
) {
}
{
"predicates": [
{
"name": "::foo",
"params": [
{
"name": "::v0",
"ty": "Int"
},
{
"name": "::v1",
"ty": {
"Array": {
"ty": "Bool",
"size": 5
}
}
},
{
"name": "::v2",
"ty": {
"Union": {
"name": "::U",
"variants": [
{
"name": "U::A",
"ty": "Int"
},
{
"name": "U::B",
"ty": null
},
{
"name": "U::C",
"ty": {
"Array": {
"ty": "Int",
"size": 3
}
}
}
]
}
}
},
{
"name": "::v3",
"ty": {
"Array": {
"ty": {
"Tuple": [
{
"name": null,
"ty": "Int"
},
{
"name": null,
"ty": "Int"
}
]
},
"size": 5
}
}
}
]
}
],
"storage": [
{
"name": "s0",
"ty": "B256"
},
{
"name": "s1",
"ty": {
"Tuple": [
{
"name": null,
"ty": "Int"
},
{
"name": null,
"ty": "Int"
}
]
}
},
{
"name": "my_map",
"ty": {
"Map": {
"ty_from": "Int",
"ty_to": {
"Tuple": [
{
"name": null,
"ty": "Int"
},
{
"name": null,
"ty": "Int"
}
]
}
}
}
},
{
"name": "my_union",
"ty": {
"Union": {
"name": "::U",
"variants": [
{
"name": "U::A",
"ty": "Int"
},
{
"name": "U::B",
"ty": null
},
{
"name": "U::C",
"ty": {
"Array": {
"ty": "Int",
"size": 3
}
}
}
]
}
}
}
]
}
Here's how we would interpret this JSON ABI:
- This contract has a single predicate called
::foo
, which is the full path of thefoo
predicate in the contract above. - Predicate
::foo
has three parameters:- At index 0, we have
::v0
of typeint
. - At index 1, we have
::v1
of typebool[5]
. - At index 2, we have
::v2
of typeU
which is a union with three variantsU::A
,U::B
, andU::C
. - At index 3, we have
::v3
. It's an array of 5 tuples, where each tuple contains twoint
s with no field names.
- At index 0, we have
- The contract also has four storage variables:
- The first is called
s0
and is of typeb256
. - The second is called
s1
and is a tuple of twoint
s. - The third is called
my_map
and is a storage map fromint
to a tuple of twoint
s. - The fourth is called
my_union
and is a union with three variantsU::A
,U::B
, andU::C
.
- The first is called