Skip to content

ContractListener

A contract listener configures FireFly to stream events from the blockchain, from a specific location on the blockchain, according to a given definition of the interface for that event.

Check out the Custom Contracts Tutorial for a walk-through of how to set up listeners for the events from your smart contracts.

See below for a deep dive into the format of contract listeners and important concepts to understand when managing them.

Event filters

Multiple filters

From v1.3.1 onwards, a contract listener can be created with multiple filters under a single topic, when supported by the connector. Each filter contains:

  • a reference to a specific blockchain event to listen for
  • (optional) a specific location/address to listen from
  • a connector-specific signature (generated from the event and the location)

In addition to this list of multiple filters, the listener specifies a single topic to identify the stream of events.

Creating a single listener that listens for multiple events will allow for the easiest management of listeners, and for strong ordering of the events that they process.

Single filter

Before v1.3.1, each contract listener would only support listening to one specific event from a contract interface. Each listener would be comprised of:

  • a reference to a specific blockchain event to listen for
  • (optional) a specific location/address to listen from
  • a connector-specific signature (generated from the event), which allows you to easily identify and search for the contact listener for an event
  • a topic which determines the ordered stream that these events are part of

For backwards compatibility, this format is still supported by the API.

Signature strings

String format

Each filter is identified by a generated signature that matches a single event, and each contract listener is identified by a signature computed from its filters.

Ethereum provides a string standard for event signatures, of the form EventName(uint256,bytes). Prior to v1.3.1, the signature of each Ethereum contract listener would exactly follow this Ethereum format.

As of v1.3.1, Ethereum signature strings have been changed, because this format does not fully describe the event - particularly because each top-level parameter can in the ABI definition be marked as indexed. For example, while the following two Solidity events have the same signature, they are serialized differently due to the different placement of indexed parameters, and thus a listener must define both individually to be able to process them:

  • ERC-20 Transfer
event Transfer(address indexed _from, address indexed _to, uint256 _value)
  • ERC-721 Transfer
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

The two above are now expressed in the following manner by the Ethereum blockchain connector:

Transfer(address,address,uint256) [i=0,1]
Transfer(address,address,uint256) [i=0,1,2]

The [i=] listing at the end of the signature indicates the position of all parameters that are marked as indexed.

Building on the blockchain-specific signature format for each event, FireFly will then compute the final signature for each filter and each contract listener as follows:

  • Each filter signature is a combination of the location and the specific connector event signature, such as 0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1:Changed(address,uint256) [i=0]
  • Each contract listener signature is a concatenation of all the filter signatures, separated by ;

Duplicate filters

FireFly restricts the creation of a contract listener containing duplicate filters.

This includes the special case where one filter is a superset of another filter, due to a wildcard location.

For example, if two filters are listening to the same event, but one has specified a location and the other hasn't, then the latter will be a superset, and already be listening to all the events matching the first filter. Creation of duplicate or superset filters within a single listener will be blocked.

Duplicate listeners

As noted above, each listener has a generated signature. This signature - containing all the locations and event signatures combined with the listener topic - will guarantee uniqueness of the contract listener. If you tried to create the same listener again, you would receive HTTP 409. This combination can allow a developer to assert that their listener exists, without the risk of creating duplicates.

Note: Prior to v1.3.1, FireFly would detect duplicates simply by requiring a unique combination of signature + topic + location for each listener. The updated behavior for the listener signature is intended to preserve similar functionality, even when dealing with listeners that contain many event filters.

Backwards compatibility

As noted throughout this document, the behavior of listeners is changed in v1.3.1. However, the following behaviors are retained for backwards-compatibility, to ensure that code written prior to v1.3.1 should continue to function.

  • The response from all query APIs of listeners will continue to populate top-level event and location fields
  • The first entry from the filters array is duplicated to these fields
  • On input to create a new listener, the event and location fields are still supported
  • They function identically to supplying a filters array with a single entry
  • The signature field is preserved at the listener level
  • The format has been changed as described above

Input formats

The two input formats supported when creating a contract listener are shown below.

Muliple Filters

{
  "filters": [
    {
      "interface": {
        "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
      },
      "location": {
        "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
      },
      "eventPath": "Changed"
    },
    {
      "interface": {
        "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
      },
      "location": {
        "address": "0xa4ea5d0b6b2eaf194716f0cc73981939dca27da1"
      },
      "eventPath": "AnotherEvent"
    }
  ],
  "options": {
    "firstEvent": "newest"
  },
  "topic": "simple-storage"
}

One filter (old format)

{
  "interface": {
    "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
  },
  "location": {
    "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
  },
  "eventPath": "Changed",
  "options": {
    "firstEvent": "newest"
  },
  "topic": "simple-storage"
}

Example

{
    "id": "d61980a9-748c-4c72-baf5-8b485b514d59",
    "interface": {
        "id": "ff1da3c1-f9e7-40c2-8d93-abb8855e8a1d"
    },
    "namespace": "ns1",
    "name": "contract1_events",
    "backendId": "sb-dd8795fc-a004-4554-669d-c0cf1ee2c279",
    "location": {
        "address": "0x596003a91a97757ef1916c8d6c0d42592630d2cf"
    },
    "created": "2022-05-16T01:23:15Z",
    "event": {
        "name": "Changed",
        "description": "",
        "params": [
            {
                "name": "x",
                "schema": {
                    "type": "integer",
                    "details": {
                        "type": "uint256",
                        "internalType": "uint256"
                    }
                }
            }
        ]
    },
    "signature": "0x596003a91a97757ef1916c8d6c0d42592630d2cf:Changed(uint256)",
    "topic": "app1_topic",
    "options": {
        "firstEvent": "newest"
    },
    "filters": [
        {
            "event": {
                "name": "Changed",
                "description": "",
                "params": [
                    {
                        "name": "x",
                        "schema": {
                            "type": "integer",
                            "details": {
                                "type": "uint256",
                                "internalType": "uint256"
                            }
                        }
                    }
                ]
            },
            "location": {
                "address": "0x596003a91a97757ef1916c8d6c0d42592630d2cf"
            },
            "signature": "0x596003a91a97757ef1916c8d6c0d42592630d2cf:Changed(uint256)"
        }
    ]
}

Field Descriptions

Field Name Description Type
id The UUID of the smart contract listener UUID
interface Deprecated: Please use 'interface' in the array of 'filters' instead FFIReference
namespace The namespace of the listener, which defines the namespace of all blockchain events detected by this listener string
name A descriptive name for the listener string
backendId An ID assigned by the blockchain connector to this listener string
location Deprecated: Please use 'location' in the array of 'filters' instead JSONAny
created The creation time of the listener FFTime
event Deprecated: Please use 'event' in the array of 'filters' instead FFISerializedEvent
signature A concatenation of all the stringified signature of the event and location, as computed by the blockchain plugin string
topic A topic to set on the FireFly event that is emitted each time a blockchain event is detected from the blockchain. Setting this topic on a number of listeners allows applications to easily subscribe to all events they need string
options Options that control how the listener subscribes to events from the underlying blockchain ContractListenerOptions
filters A list of filters for the contract listener. Each filter is made up of an Event and an optional Location. Events matching these filters will always be emitted in the order determined by the blockchain. ListenerFilter[]

FFIReference

Field Name Description Type
id The UUID of the FireFly interface UUID
name The name of the FireFly interface string
version The version of the FireFly interface string

FFISerializedEvent

Field Name Description Type
name The name of the event string
description A description of the smart contract event string
params An array of event parameter/argument definitions FFIParam[]
details Additional blockchain specific fields about this event from the original smart contract. Used by the blockchain plugin and for documentation generation. JSONObject

FFIParam

Field Name Description Type
name The name of the parameter. Note that parameters must be ordered correctly on the FFI, according to the order in the blockchain smart contract string
schema FireFly uses an extended subset of JSON Schema to describe parameters, similar to OpenAPI/Swagger. Converters are available for native blockchain interface definitions / type systems - such as an Ethereum ABI. See the documentation for more detail JSONAny

ContractListenerOptions

Field Name Description Type
firstEvent A blockchain specific string, such as a block number, to start listening from. The special strings 'oldest' and 'newest' are supported by all blockchain connectors. Default is 'newest' string

ListenerFilter

Field Name Description Type
event The definition of the event, either provided in-line when creating the listener, or extracted from the referenced FFI FFISerializedEvent
location A blockchain specific contract identifier. For example an Ethereum contract address, or a Fabric chaincode name and channel JSONAny
interface A reference to an existing FFI, containing pre-registered type information for the event FFIReference
signature The stringified signature of the event and location, as computed by the blockchain plugin string