HAL JSON Type

How we describe parameters, arguments and outputs

HAL JSON Scalar Type is a custom type used to represent functions' parameters, arguments or outputs, as well as Events' parameters or arguments. Using this type it is possible to describe the parameters used in triggers, matches or outcomes. Since from a GraphQL prospective this type accepts any valid JSON, some conventions are needed to enforce a valid data structure.

Describe on which element a basic filter needs to be applied within a TransactionsTrigger

A basic filter is a filter based on Ethereum transaction information such as from, to, nonce, gasPrice, gasLimit and value. In this case, the JSON Scalar Type is used to indicate the name and the type of the Ethereum transaction field that we want to verify.

{
name: 'To', // 'To', 'From', 'Value', 'Gas', 'GasPrice', 'Nonce',
}

Describe on which element a filter needs to be applied on a specific function (or an event) parameter within a TransactionsTrigger (or an EventsTrigger)

To describe a filter based on a particular parameter you need to indicate the parameter name and type the filter condition should be applied to:

{
name: 'tradeValue',
type: 'int16', // String, int, int16, int32, int256...
}

Take a look at this simple function:

function withdraw(address token, uint256 amount)

To describe a trigger targeting the second parameter (uint256 amount), data needs to be structured like:

{
name: 'amount',
type: 'uint256',
}

(Note: the condition to be applied is described outside this JSON, here we only have to specify against which parameter the condition needs to be evaluated)

Array and Tuple

Using the JSON field, it is also easy to identify values within an Array or a Tuple.

function trade(uint256[8] tradeValues, address[4] tradeAddresses, uint8[2] v, bytes32[4] rs)

In this case, it is possible to create a trigger based on the second value of the first parameter uint256[8] tradeValues in this way:

{
name: 'tradeValues',
type: 'uint256[8]',
index: 2,
}

Please note the index field to indicate the position of the value we want to apply the condition to inside the array starts with zero for the first element.

In the case of a Tuple, on the other hand, it is necessary to indicate the exact component that we want the trigger to be targeting:

Take a look at this function:

function fillOrder(Order order, uint256 takerAssetFillAmount, bytes memory signature)

The first parameter is this tuple:

inputs: [
{
components: [
{
name: 'makerAddress',
type: 'address',
},
{
name: 'takerAddress',
type: 'address',
},
{
name: 'feeRecipientAddress',
type: 'address',
},
{
name: 'senderAddress',
type: 'address',
},
{
name: 'makerAssetAmount',
type: 'uint256',
},
{
name: 'takerAssetAmount',
type: 'uint256',
},
{
name: 'makerFee',
type: 'uint256',
},
{
name: 'takerFee',
type: 'uint256',
},
{
name: 'expirationTimeSeconds',
type: 'uint256',
},
{
name: 'salt',
type: 'uint256',
},
{
name: 'makerAssetData',
type: 'byte',
},
{
name: 'takerAssetData',
type: 'byte',
}
],
name: 'orders',
type: 'tuple[]',
},
],

To describe a trigger based on the makerAddress component, the JSON needs to be:

{
type: 'tuple[]',
name: 'orders',
components: [
{
name: 'makerAddress',
type: 'address',
},
],
}

It is also possible to create combinations to handle more exotic cases like an array within a tuple:

{
type: 'tuple[]',
name: 'Example',
components: [
{
name: 'MyArray',
type: 'uint256[8]',
index: 2,
},
],
}

ContractsTrigger

A ContractsTrigger is a trigger that gets fired when a contract changes its status (e.g. the gambling contract's jackpot is now higher than 20 Ether). In order to retrieve contract status it is necessary to call a specific function with the right input parameters.

Take a look at this function:

function getAssetProxy(bytes4 assetProxyId)
returns (address proxyAddress);

This function returns the asset proxy registered to assetProxyId as an address. We can create a trigger targeting the returned value of this function (i.e. if the address is equal to '0x...').

To describe this trigger it is necessary to indicate both the input value(s) - the parameter(s) needed to call the function - as well as outputFilters - the conditions that the returned values need to satisfy for the trigger to be fired.

Inputs

In GraphQL the "Inputs" field is described as an array of parameters: inputs: [JSON!]. Each one of these JSONs describes an input. In this case:

{
name: 'assetProxyId',
type: 'bytes4',
value: '0x21',
}

Please note that the value field is necessary to call the function getAssetProxysince you are describing a function payload and not a filter like in the previous examples.

The same logic applies for arrays or tuples. Please note that it is necessary to describe all the values of a Tuple, since it needs to be passed as payload to the function:

[
{
components: [
{
name: 'makerAddress',
type: 'address',
value: '0x...',
},
{
name: 'takerAddress',
type: 'address',
value: '0x...',
},
{
name: 'feeRecipientAddress',
type: 'address',
value: '0x...',
},
{
name: 'senderAddress',
type: 'address',
value: '0x...',
},
{
name: 'makerAssetAmount',
type: 'uint256',
value: 20,
},
{
name: 'takerAssetAmount',
type: 'uint256',
value: 20,
},
{
name: 'makerFee',
type: 'uint256',
value: 20,
},
{
name: 'takerFee',
type: 'uint256',
value: 20,
},
{
name: 'expirationTimeSeconds',
type: 'uint256',
value: 20,
},
{
name: 'salt',
type: 'uint256',
value: 22232,
},
{
name: 'makerAssetData',
type: 'byte',
value: '0x0000000000000000000000000000000000000000000000000000000061626364',
},
{
name: 'takerAssetData',
type: 'byte',
value: '0x0000000000000000000000000000000000000000000000000000000061626364',
}
],
name: 'orders',
type: 'tuple[]',
},
],

The same goes for arrays:

{
type: 'int16[4]',
name: 'ExampleArray',
value: [15,16,17,18],
}

outputFilters

Within ContractsTrigger, any outputFilter must have the exact returned value it refers to (returnedIndex, of type int) as well as a Parameter field, of type Haljson (our custom json), that indicates which part of the Parameter the filter needs to be applied to.

In case of scalar types you will apply the condition on that scalar:

function getAssetProxy(bytes4 assetProxyId)
returns (address proxyAddress);

JSON:

{
name: 'proxyAddress',
type: 'address',
}

In the case of an array you will apply the condition on a specific Index:

function getAssetProxy(bytes4 assetProxyId)
returns (address[77] proxyAddresses);

JSON:

{
name: 'proxyAddresses',
type: 'address[77]',
index: '23'
}

Matches

Matches describe data about triggered triggers.

In the case of TransactionsTriggers or EventsTriggers, a match includes details about the decoded parameters for the triggered function or event.

From the GraphQL schema:

arguments: [JSON]

Any of the JSONs in that array is a JSON custom type. For example:

{
name: 'firstParameterName',
type: 'int16',
value: '10',
},

In the case of a ContractsTrigger, a match returns all the values returned by the function called by the trigger into two different arrays

  • matched, which includes only the returned values on which the trigger conditions have been applied successfully, and

  • all, which contains all the returned values

From the GraphQL schema:

matched: [JSON!]!

all: [JSON!]!

All JSONs in these array are of HAL JSON custom type. For example:

{
name: 'firstParameterName',
type: 'int16',
value: '10',
},

Please note that, within matches, a parameter is always described with name, type and value.