fftokens¶
Overview¶
fftokens is a protocol that can be implemented by token connector runtimes in order to be usable by the fftokens plugin in FireFly.
The connector runtime must expose an HTTP and websocket server, along with a minimum set of HTTP APIs and websocket events. Each connector will be strongly coupled to a specific ledger technology and token standard(s), but no assumptions are made in the fftokens spec about what these technologies must be, as long as they can satisfy the basic requirements laid out here.
Note that this is an internal protocol in the FireFly ecosystem - application developers working against FireFly should never need to care about or directly interact with a token connector runtime. The audience for this document is only developers interested in creating new token connectors (or editing/forking existing ones).
Two implementations of this specification have been created to date (both based on common Ethereum token standards) - firefly-tokens-erc1155 and firefly-tokens-erc20-erc721.
HTTP APIs¶
This is the minimum set of APIs that must be implemented by a conforming token connector. A connector may choose to expose other APIs for its own purposes. All requests and responses to the APIs below are encoded as JSON. The APIs are currently understood to live under a /api/v1
prefix.
POST /createpool
¶
Create a new token pool. The exact meaning of this is flexible - it may mean invoking a contract or contract factory to actually define a new set of tokens via a blockchain transaction, or it may mean indexing a set of tokens that already exists (depending on the options a connector accepts in config
).
In a multiparty network, this operation will only be performed by one of the parties, and FireFly will broadcast the result to the others.
FireFly will store a "pending" token pool after a successful creation, but will replace it with a "confirmed" token pool after a successful activation (see below).
Request
{
"type": "fungible",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"namespace": "default",
"name": "FFCoin",
"symbol": "FFC",
"data": "pool-metadata",
"requestId": "1",
"config": {}
}
Parameter | Type | Description |
---|---|---|
type | string enum | The type of pool to create. Currently supported types are "fungible" and "nonfungible". It is recommended (but not required) that token connectors support both. Unrecognized/unsupported types should be rejected with HTTP 400. |
signer | string | The signing identity to be used for the blockchain transaction, in a format understood by this connector. |
namespace | string | The namespace of the token pool |
name | string | (OPTIONAL) If supported by this token contract, this is a requested name for the token pool. May be ignored at the connector's discretion. |
symbol | string | (OPTIONAL) If supported by this token contract, this is a requested symbol for the token pool. May be ignored at the connector's discretion. |
requestId | string | (OPTIONAL) A unique identifier for this request. Will be included in the "receipt" websocket event to match receipts to requests. |
data | string | (OPTIONAL) A data string that should be returned in the connector's response to this creation request. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. Each connector may define its own valid options to influence how the token pool is created. |
Response
HTTP 200: pool creation was successful, and the pool details are returned in the response.
See Response Types: Token Pool
HTTP 202: request was accepted, but pool will be created asynchronously, with "receipt" and "token-pool" events sent later on the websocket.
See Response Types: Async Request
POST /activatepool
¶
Activate a token pool to begin receiving events. Generally this means the connector will create blockchain event listeners for transfer and approval events related to the set of tokens encompassed by this token pool.
In a multiparty network, this step will be performed by every member after a successful token pool broadcast. It therefore also serves the purpose of validating the broadcast info - if the connector does not find a valid pool given the poolLocator
and config
information passed in to this call, the pool should not get confirmed.
Request
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
poolData | string | (OPTIONAL) A data string that should be permanently attached to this pool and returned in all events. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. This should be the same config object that was passed when the pool was created. |
Response
HTTP 200: pool activation was successful, and the pool details are returned in the response.
See Response Types: Token Pool
HTTP 202: request was accepted, but pool will be activated asynchronously, with "receipt" and "token-pool" events sent later on the websocket.
See Response Types: Async Request
HTTP 204: activation was successful - no separate receipt will be delivered, but "token-pool" event will be sent later on the websocket.
No body
POST /deactivatepool
¶
Deactivate a token pool to stop receiving events and delete all blockchain listeners related to that pool.
Request
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
poolData | string | (OPTIONAL) The data string that was attached to this pool at activation. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. |
Response
HTTP 204: deactivation was successful, and one or more listeners were deleted.
No body
HTTP 404: no blockchain listeners were found for the given pool information.
No body
POST /checkinterface
¶
This is an optional (but recommended) API for token connectors. If implemented, support will be indicated by
the presence of the interfaceFormat
field in all Token Pool responses.
In the case that a connector supports multiple variants of a given token standard (such as many different ways to structure "mint" or "burn" calls on an underlying smart contract), this API allows the connector to be provided with a full description of the interface methods in use for a given token pool, so the connector can determine which methods it knows how to invoke.
Request
{
"poolLocator": "id=F1",
"format": "abi",
"methods": [
{
"name": "burn",
"type": "function",
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
...
]
}
Parameter | Type | Description |
---|---|---|
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
format | string enum | The format of the data in this payload. Should match the interfaceFormat as supplied by the output of the pool creation. |
methods | object array | A list of all the methods available on the interface underpinning this token pool, encoded in the format specified by format . |
Response
HTTP 200: interface was successfully parsed, and methods of interest are returned in the body.
The response body includes a section for each type of token operation (burn/mint/transfer/approval), which
specifies a subset of the input body useful to that operation. The caller (FireFly) can then store and
provide the proper subset of the interface for every future token operation (via the interface
parameter).
{
"burn": {
"format": "abi",
"methods": [
{
"name": "burn",
"type": "function",
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]
},
"mint": { ... },
"transfer": { ... },
"approval": { ... }
}
POST /mint
¶
Mint new tokens.
Request
{
"namespace": "default",
"poolLocator": "id=F1",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"to": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"amount": "10",
"tokenIndex": "1",
"uri": "ipfs://000000",
"requestId": "1",
"data": "transfer-metadata",
"config": {},
"interface": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
signer | string | The signing identity to be used for the blockchain transaction, in a format understood by this connector. |
to | string | The identity to receive the minted tokens, in a format understood by this connector. |
amount | number string | The amount of tokens to mint. |
tokenIndex | string | (OPTIONAL) For non-fungible tokens that require choosing an index at mint time, the index of the specific token to mint. |
uri | string | (OPTIONAL) For non-fungible tokens that support choosing a URI at mint time, the URI to be attached to the token. |
requestId | string | (OPTIONAL) A unique identifier for this request. Will be included in the "receipt" websocket event to match receipts to requests. |
data | string | (OPTIONAL) A data string that should be returned in the connector's response to this mint request. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. Each connector may define its own valid options to influence how the mint is carried out. |
interface | object | (OPTIONAL) Details on interface methods that are useful to this operation, as negotiated previously by a /checkinterface call. |
Response
HTTP 202: request was accepted, but mint will occur asynchronously, with "receipt" and "token-mint" events sent later on the websocket.
See Response Types: Async Request
POST /burn
¶
Burn tokens.
Request
{
"namespace": "default",
"poolLocator": "id=F1",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"from": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"amount": "10",
"tokenIndex": "1",
"requestId": "1",
"data": "transfer-metadata",
"config": {},
"interface": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
signer | string | The signing identity to be used for the blockchain transaction, in a format understood by this connector. |
from | string | The identity that currently owns the tokens to be burned, in a format understood by this connector. |
amount | number string | The amount of tokens to burn. |
tokenIndex | string | (OPTIONAL) For non-fungible tokens, the index of the specific token to burn. |
requestId | string | (OPTIONAL) A unique identifier for this request. Will be included in the "receipt" websocket event to match receipts to requests. |
data | string | (OPTIONAL) A data string that should be returned in the connector's response to this burn request. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. Each connector may define its own valid options to influence how the burn is carried out. |
interface | object | (OPTIONAL) Details on interface methods that are useful to this operation, as negotiated previously by a /checkinterface call. |
Response
HTTP 202: request was accepted, but burn will occur asynchronously, with "receipt" and "token-burn" events sent later on the websocket.
See Response Types: Async Request
POST /transfer
¶
Transfer tokens from one address to another.
Request
{
"namespace": "default",
"poolLocator": "id=F1",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"from": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"to": "0xb107ed9caa1323b7bc36e81995a4658ec2251951",
"amount": "1",
"tokenIndex": "1",
"requestId": "1",
"data": "transfer-metadata",
"config": {},
"interface": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
signer | string | The signing identity to be used for the blockchain transaction, in a format understood by this connector. |
from | string | The identity to be used for the source of the transfer, in a format understood by this connector. |
to | string | The identity to be used for the destination of the transfer, in a format understood by this connector. |
amount | number string | The amount of tokens to transfer. |
tokenIndex | string | (OPTIONAL) For non-fungible tokens, the index of the specific token to transfer. |
requestId | string | (OPTIONAL) A unique identifier for this request. Will be included in the "receipt" websocket event to match receipts to requests. |
data | string | (OPTIONAL) A data string that should be returned in the connector's response to this transfer request. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. Each connector may define its own valid options to influence how the transfer is carried out. |
interface | object | (OPTIONAL) Details on interface methods that are useful to this operation, as negotiated previously by a /checkinterface call. |
Response
HTTP 202: request was accepted, but transfer will occur asynchronously, with "receipt" and "token-transfer" events sent later on the websocket.
See Response Types: Async Request
POST /approval
¶
Approve another identity to manage tokens.
Request
{
"namespace": "default",
"poolLocator": "id=F1",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"operator": "0xb107ed9caa1323b7bc36e81995a4658ec2251951",
"approved": true,
"requestId": "1",
"data": "approval-metadata",
"config": {},
"interface": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
signer | string | The signing identity to be used for the blockchain transaction, in a format understood by this connector. |
operator | string | The identity to be approved (or unapproved) for managing the signer's tokens. |
approved | boolean | Whether to approve (the default) or unapprove. |
requestId | string | (OPTIONAL) A unique identifier for this request. Will be included in the "receipt" websocket event to match receipts to requests. |
data | string | (OPTIONAL) A data string that should be returned in the connector's response to this approval request. |
config | object | (OPTIONAL) An arbitrary JSON object where the connector may accept additional parameters if desired. Each connector may define its own valid options to influence how the approval is carried out. |
interface | object | (OPTIONAL) Details on interface methods that are useful to this operation, as negotiated previously by a /checkinterface call. |
Response
HTTP 202: request was accepted, but approval will occur asynchronously, with "receipt" and "token-approval" events sent later on the websocket.
See Response Types: Async Request
Websocket Commands¶
In order to start listening for events on a certain namespace, the client needs to send the start
command. Clients should send this command every time they connect, or after an automatic reconnect.
Websocket Events¶
A connector should expose a websocket at /api/ws
. All emitted websocket events are a JSON string of the form:
The event
name will match one of the names listed below, and the data
payload will correspond to the linked response object.
All events except the receipt event must be acknowledged by sending an ack of the form:
Many messages may also be batched into a single websocket event of the form:
{
"id": "event-id",
"event": "batch",
"data": {
"events": [
{
"event": "event-name",
"data": {}
},
...
]
}
}
Batched messages must be acked all at once using the ID of the batch.
receipt
¶
An asynchronous operation has completed.
token-pool
¶
A new token pool has been created or activated.
See Response Types: Token Pool
token-mint
¶
Tokens have been minted.
See Response Types: Token Transfer
token-burn
¶
Tokens have been burned.
See Response Types: Token Transfer
token-transfer
¶
Tokens have been transferred.
See Response Types: Token Transfer
token-approval
¶
Token approvals have changed.
See Response Types: Token Approval
Response Types¶
Async Request¶
Many operations may happen asynchronously in the background, and will return only a request ID. This may be a request ID that was passed in, or if none was passed, will be randomly assigned. This ID can be used to correlate with a receipt event later received on the websocket.
Receipt¶
Parameter | Type | Description |
---|---|---|
headers.type | string enum | The type of this response. Should be "TransactionSuccess", "TransactionUpdate", or "TransactionFailed". |
headers.requestId | string | The ID of the request to which this receipt should correlate. |
transactionHash | string | The unique identifier for the blockchain transaction which generated this receipt. |
errorMessage | string | (OPTIONAL) If this is a failure, contains details on the reason for the failure. |
Token Pool¶
{
"namespace": "default",
"type": "fungible",
"data": "pool-metadata",
"poolLocator": "id=F1",
"standard": "ERC20",
"interfaceFormat": "abi",
"symbol": "FFC",
"decimals": 18,
"info": {},
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"blockchain": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
type | string enum | The type of pool that was created. |
data | string | A copy of the data that was passed in on the creation request. |
poolLocator | string | A string to identify this pool, generated by the connector. Must be unique for each pool created by this connector. Will be passed back on all operations within this pool, and may be packed with relevant data about the pool for later usage (such as the address and type of the pool). |
standard | string | (OPTIONAL) The name of a well-defined token standard to which this pool conforms. |
interfaceFormat | string enum | (OPTIONAL) If this connector supports the /checkinterface API, this is the interface format to be used for describing the interface underpinning this pool. Must be "abi" or "ffi". |
symbol | string | (OPTIONAL) The symbol for this token pool, if applicable. |
decimals | number | (OPTIONAL) The number of decimals used for balances in this token pool, if applicable. |
info | object | (OPTIONAL) Additional information about the pool. Each connector may define the format for this object. |
signer | string | (OPTIONAL) If this operation triggered a blockchain transaction, the signing identity used for the transaction. |
blockchain | object | (OPTIONAL) If this operation triggered a blockchain transaction, contains details on the blockchain event in FireFly's standard blockchain event format. |
Token Transfer¶
Note that mint and burn operations are just specialized versions of transfer. A mint will omit the "from" field, while a burn will omit the "to" field.
{
"namespace": "default",
"id": "1",
"data": "transfer-metadata",
"poolLocator": "id=F1",
"poolData": "extra-pool-info",
"from": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"to": "0xb107ed9caa1323b7bc36e81995a4658ec2251951",
"amount": "1",
"tokenIndex": "1",
"uri": "ipfs://000000",
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"blockchain": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
id | string | An identifier for this transfer. Must be unique for every transfer within this pool. |
data | string | A copy of the data that was passed in on the mint/burn/transfer request. May be omitted if the token contract does not support a method of attaching extra data (will result in reduced ability for FireFly to correlate the inputs and outputs of the transaction). |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
poolData | string | The extra data associated with the pool at pool activation. |
from | string | The identity used for the source of the transfer. |
to | string | The identity used for the destination of the transfer. |
amount | number string | The amount of tokens transferred. |
tokenIndex | string | (OPTIONAL) For non-fungible tokens, the index of the specific token transferred. |
uri | string | (OPTIONAL) For non-fungible tokens, the URI attached to the token. |
signer | string | (OPTIONAL) If this operation triggered a blockchain transaction, the signing identity used for the transaction. |
blockchain | object | (OPTIONAL) If this operation triggered a blockchain transaction, contains details on the blockchain event in FireFly's standard blockchain event format. |
Token Approval¶
{
"namespace": "default",
"id": "1",
"data": "transfer-metadata",
"poolLocator": "id=F1",
"poolData": "extra-pool-info",
"operator": "0xb107ed9caa1323b7bc36e81995a4658ec2251951",
"approved": true,
"subject": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A:0xb107ed9caa1323b7bc36e81995a4658ec2251951",
"info": {},
"signer": "0x0Ef1D0Dd56a8FB1226C0EaC374000B81D6c8304A",
"blockchain": {}
}
Parameter | Type | Description |
---|---|---|
namespace | string | The namespace of the token pool |
id | string | An identifier for this approval. Must be unique for every approval within this pool. |
data | string | A copy of the data that was passed in on the approval request. May be omitted if the token contract does not support a method of attaching extra data (will result in reduced ability for FireFly to correlate the inputs and outputs of the transaction). |
poolLocator | string | The locator of the pool, as supplied by the output of the pool creation. |
poolData | string | The extra data associated with the pool at pool activation. |
operator | string | The identity that was approved (or unapproved) for managing tokens. |
approved | boolean | Whether this was an approval or unapproval. |
subject | string | A string identifying the scope of the approval, generated by the connector. Approvals with the same subject are understood replace one another, so that a previously-recorded approval becomes inactive. This string may be a combination of the identities involved, the token index, etc. |
info | object | (OPTIONAL) Additional information about the approval. Each connector may define the format for this object. |
signer | string | (OPTIONAL) If this operation triggered a blockchain transaction, the signing identity used for the transaction. |
blockchain | object | (OPTIONAL) If this operation triggered a blockchain transaction, contains details on the blockchain event in FireFly's standard blockchain event format. |