diff --git a/docs/_config.yml b/docs/_config.yml
index 6ab8a44d69..1194179b7d 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,5 +1,6 @@
+repository: hyperledger/firefly
remote_theme: pmarsceill/just-the-docs
-logo: "firefly_logo_36h.png"
+logo: "images/hyperledger-firefly_color.svg"
title: Hyperledger FireFly Docs
baseurl: /firefly
aux_links:
diff --git a/docs/gettingstarted/custom_blockchain.md b/docs/gettingstarted/custom_blockchain.md
deleted file mode 100644
index ed1e392acb..0000000000
--- a/docs/gettingstarted/custom_blockchain.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-layout: default
-title: Invoke smart contracts
-parent: Getting Started
-nav_order: 10
----
-
-# Invoke smart contracts
-{: .no_toc }
-
-## Table of contents
-{: .no_toc .text-delta }
-
-1. TOC
-{:toc}
-
----
-
-## Work in progress
-
-The passthrough APIs to invoke on-chain smart contracts direct, and subscribe to the events from
-those transactions in your application, are currently under development.
-
-You can access the APIs of the blockchain interface plugins directly, which perform the heavy lifting
-to add an API/Event gateway in front of the low level blockchain RPC interfaces.
-
-See [Key Concepts: Blockchain protocols](/firefly/keyconcepts/blockchain_protocols.html) for more information.
diff --git a/docs/gettingstarted/custom_contracts.md b/docs/gettingstarted/custom_contracts.md
new file mode 100644
index 0000000000..64169db938
--- /dev/null
+++ b/docs/gettingstarted/custom_contracts.md
@@ -0,0 +1,815 @@
+---
+layout: default
+title: Work with custom smart contracts
+parent: Getting Started
+nav_order: 10
+---
+
+# Work with custom smart contracts
+{: .no_toc }
+
+FireFly includes robust support for custom smart contracts with an easy to use API to work with them. FireFly's unified API creates a consistent application experience regardless of the specific underlying blockchain implementation. It also provides developer-friendly features like automatic OpenAPI Specification generation for smart contracts, plus a Swagger UI built-in.
+
+## Table of contents
+{: .no_toc .text-delta }
+
+1. TOC
+{:toc}
+
+---
+
+> **NOTE:** This guide assumes that you are running a local FireFly stack with at least 2 members and an Ethereum blockchain created by the FireFly CLI. If you need help getting that set up, please see the [Getting Started guide to Start your environment](https://nguyer.github.io/firefly/gettingstarted/setup_env.html).
+
+## Example smart contract
+
+For this tutorial, we will be using a well known, but slightly modified smart contract called `SimpleStorage`, and will be using this contract on an Ethereum blockchain. As the name implies, it's a very simple contract which stores an unsigned 256 bit integer, emits and event when the value is updated, and allows you to retrieve the current value.
+
+Here is the source for this contract:
+
+```solidity
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.10;
+
+// Declares a new contract
+contract SimpleStorage {
+ // Storage. Persists in between transactions
+ uint256 x;
+
+ // Allows the unsigned integer stored to be changed
+ function set(uint256 newValue) public {
+ x = newValue;
+ emit Changed(msg.sender, newValue);
+ }
+
+ // Returns the currently stored unsigned integer
+ function get() public view returns (uint256) {
+ return x;
+ }
+
+ event Changed(address indexed from, uint256 value);
+}
+```
+
+## Contract deployment
+
+For the this guide, we will assume that the SimpleStorage contract is deployed at the Ethereum address of: `0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1`
+
+**Deployment of smart contracts is not currently within the scope of responsibility for FireFly.** You can use your standard blockchain specific tools to deploy your contract to whichever blockchain you are using. For Ethereum blockchains you could use [Truffle](https://trufflesuite.com/) or [Hardhat](https://hardhat.org/).
+
+### Using Truffle
+
+If you're using Truffle, you'll need to set your `truffle-config.js` file to point to the locally running blockchain node that the FireFly CLI created. Make sure your `networks` section looks like this:
+
+```javascript
+networks: {
+ development: {
+ host: "127.0.0.1",
+ port: 5100,
+ network_id: "*"
+ }
+}
+```
+
+### Using the FireFly CLI
+
+The FireFly CLI also has a function to deploy an already-compiled smart contract to a local FireFly stack.
+
+> **NOTE:** The contract deployment function of the FireFly CLI is a convenience function to speed up local development, and not intended for production applications
+
+We will use the `solc` compiler to compile our smart contract. For details on how to install `solc` on your system, please see the [Solidity Compiler Documentation](https://docs.soliditylang.org/en/v0.8.9/installing-solidity.html).
+
+If you take the smart contract source code in the example above, and save that to a file called `simple_storage.sol`, here is the command to compile the contract:
+
+```
+$ solc --combined-json abi,bin simple_storage.sol > simple_storage.json
+```
+
+Next, we'll tell the FireFly to deploy the compiled contract to a running stack named `dev`. If your stack name is different, update the command accordingly:
+
+```
+$ ff deploy dev simple_storage.json
+reading stack config... done
+deploying simple_storage.sol:SimpleStorage... done
+
+contract address: 0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1
+```
+
+The FireFly CLI tells us that it has successfully deployed the contract with an address of `0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1`. We will use this contract address for the rest of this guide.
+
+
+## The FireFly Interface Format
+
+Before we jump into using our contract with FireFly, it's helpful to understand a couple of key concepts. One of those is the FireFly Interface format. FireFly defines a common, blockchain agnostic way to describe smart contracts. This is referred to as a **Contract Interface**, and it is written in the FireFly Interface (FFI) format. It is a simple JSON document that has a name, a namespace, a version, a list of methods, and a list of events.
+
+For more details, you can also have a look at the [Reference page for the FireFly Interface Format](../reference/firefly_interface_format).
+
+If you have an Ethereum ABI for an existing smart contract, there is an HTTP endpoint on the FireFly API that will take the ABI as input and automatically generate the FireFly Interface for you. Rather than handcrafting our FFI, we'll let FireFly generate it for us using that endpoint now.
+
+### Request
+
+Here we will take the JSON ABI generated by `truffle` or `solc` and `POST` that to FireFly to have it automatically generate the FireFly Interface for us. Copy the `abi` from the compiled JSON file, and put that inside an `input` object like the example below:
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/interfaces/generate`
+
+```json
+{
+ "input": {
+ "abi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Changed",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "get",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "newValue",
+ "type": "uint256"
+ }
+ ],
+ "name": "set",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+ ]
+ }
+}
+```
+
+### Response
+
+FireFly generates and returns the the full FireFly Interface for the SimpleStorage contract in the response body:
+
+```json
+{
+ "namespace": "default",
+ "name": "",
+ "description": "",
+ "version": "",
+ "methods": [
+ {
+ "name": "get",
+ "pathname": "",
+ "description": "",
+ "params": [],
+ "returns": [
+ {
+ "name": "",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "set",
+ "pathname": "",
+ "description": "",
+ "params": [
+ {
+ "name": "newValue",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ],
+ "returns": []
+ }
+ ],
+ "events": [
+ {
+ "name": "Changed",
+ "description": "",
+ "params": [
+ {
+ "name": "from",
+ "schema": {
+ "type": "string",
+ "details": {
+ "type": "address",
+ "internalType": "address",
+ "indexed": true
+ }
+ }
+ },
+ {
+ "name": "value",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Broadcast the contract interface
+
+Now that we have a FireFly Interface representation of our smart contract, we want to broadcast that to the entire network. This broadcast will be pinned to the blockchain, so we can always refer to this specific name and version, and everyone in the network will know exactly which contract interface we are talking about.
+
+> **NOTE**: Contract interfaces are scoped to a namespace. Within a namespace each contract interface must have a unique name and version combination. The same name and version combination can exist in *different* namespaces simultaneously.
+
+We will take the output from the previous HTTP response above, **fill in the name and version** and then `POST` that to the `/contracts/interfaces` API endpoint.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/interfaces`
+
+```json
+{
+ "namespace": "default",
+ "name": "SimpleStorage",
+ "version": "v1.0.0",
+ "description": "",
+ "methods": [
+ {
+ "name": "get",
+ "pathname": "",
+ "description": "",
+ "params": [],
+ "returns": [
+ {
+ "name": "",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "set",
+ "pathname": "",
+ "description": "",
+ "params": [
+ {
+ "name": "newValue",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ],
+ "returns": []
+ }
+ ],
+ "events": [
+ {
+ "name": "Changed",
+ "description": "",
+ "params": [
+ {
+ "name": "from",
+ "schema": {
+ "type": "string",
+ "details": {
+ "type": "address",
+ "internalType": "address",
+ "indexed": true
+ }
+ }
+ },
+ {
+ "name": "value",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+### Response
+
+```json
+{
+ "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3",
+ "message": "3cd0dde2-1e39-4c9e-a4a1-569e87cca93a",
+ "namespace": "default",
+ "name": "SimpleStorage",
+ "description": "",
+ "version": "v1.0.0",
+ "methods": [
+ {
+ "id": "56467890-5713-4463-84b8-4537fcb63d8b",
+ "contract": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3",
+ "name": "get",
+ "namespace": "default",
+ "pathname": "get",
+ "description": "",
+ "params": [],
+ "returns": [
+ {
+ "name": "",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "6b254d1d-5f5f-491e-bbd2-201e96892e1a",
+ "contract": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3",
+ "name": "set",
+ "namespace": "default",
+ "pathname": "set",
+ "description": "",
+ "params": [
+ {
+ "name": "newValue",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ],
+ "returns": []
+ }
+ ],
+ "events": [
+ {
+ "id": "aa1fe67b-b2ac-41af-a7e7-7ad54a30a78d",
+ "contract": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3",
+ "namespace": "default",
+ "pathname": "Changed",
+ "name": "Changed",
+ "description": "",
+ "params": [
+ {
+ "name": "from",
+ "schema": {
+ "type": "string",
+ "details": {
+ "type": "address",
+ "internalType": "address",
+ "indexed": true
+ }
+ }
+ },
+ {
+ "name": "value",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+## Create an HTTP API for the contract
+
+Now comes the fun part where we see some of the powerful, developer-friendly features of FireFly. The next thing we're going to to is tell FireFly to build an HTTP API for this smart contract, complete with an OpenAPI Specification and Swagger UI. As part of this, we'll also tell FireFly where the contract is on the blockchain. Like the interface broadcast above, this will also generate a broadcast which will be pinned to the blockchain so all the members of the network will be aware of and able to interact with this API.
+
+We need to copy the `id` field we got in the response from the previous step to the `interface.id` field in the request body below. We will also pick a name that will be part of the URL for our HTTP API, so be sure to pick a name that is URL friendly. In this case we'll call it `simple-storage`. Lastly, in the `location.address` field, we're telling FireFly where an instance of the contract is deployed on-chain.
+
+>**NOTE**: The `location` field is optional here, but if it is omitted, it will be required in every request to invoke or query the contract. This can be useful if you have multiple instances of the same contract deployed to different addresses.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/apis`
+
+```json
+{
+ "name": "simple-storage",
+ "interface": {
+ "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
+ },
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "9a681ec6-1dee-42a0-b91b-61d23a814b0f",
+ "namespace": "default",
+ "interface": {
+ "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
+ },
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ },
+ "name": "simple-storage",
+ "message": "d90d0386-8874-43fb-b7d3-485c22f35f47",
+ "urls": {
+ "openapi": "http://127.0.0.1:5000/api/v1/namespaces/default/apis/simple-storage/api/swagger.json",
+ "ui": "http://127.0.0.1:5000/api/v1/namespaces/default/apis/simple-storage/api"
+ }
+}
+```
+
+## View OpenAPI spec for the contract
+
+You'll notice in the response body that there are a couple of URLs near the bottom. If you navigate to the one labeled `ui` in your browser, you should see the Swagger UI for your smart contract.
+
+
+
+## Smart contracts async programming in FireFly
+
+Before we start using our smart contract, it's worth taking a moment to understand the programming model when working with smart contracts in FireFly. Like the rest of FireFly, smart contracts are implemented with an asynchronous programming model. The key concepts here are:
+
+- Transactions are submitted to FireFly and an ID is returned. This is the **Operation ID**.
+- The transaction itself happens asynchronously from the HTTP request that initiated it
+- Blockchain events emitted by smart contracts will be stored in FireFly's database if FireFly has a **Listener** set up for that specific type of event. FireFly will also emit an event of type `blockchain_event_received` when this happens.
+
+
+
+
+## Invoke the smart contract
+
+Now that we've got everything set up, it's time to use our smart contract! We're going to make a `POST` request to the `invoke/set` endpoint to set the integer value on-chain. Let's set it to the value of `3` right now.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/apis/simple-storage/invoke/set`
+```json
+{
+ "input": {
+ "newValue": 3
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "41c67c63-52cf-47ce-8a59-895fe2ffdc86"
+}
+```
+
+You'll notice that we just get an ID back here, and that's expected due to the asynchronous programming model of working with smart contracts in FireFly. To see what the value is now, we can query the smart contract. In a little bit, we'll also subscribe to the events emitted by this contract so we can know when the value is updated in realtime.
+
+## Query the current value
+
+To make a read-only request to the blockchain to check the current value of the stored integer, we can make a `POST` to the `query/get` endpoint.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/apis/simple-storage/query/get`
+```json
+{}
+```
+
+### Response
+
+```json
+{
+ "output": "3"
+}
+```
+
+> **NOTE:** Some contracts may have queries that require input parameters. That's why the query endpoint is a `POST`, rather than a `GET` so that parameters can be passed as JSON in the request body. This particular function does not have any parameters, so we just pass an empty JSON object.
+
+## Create a blockchain event listener
+
+Now that we've seen how to submit transactions and preform read-only queries to the blockchain, let's look at how to receive blockchain events so we know when things are happening in realtime.
+
+If you look at the source code for the smart contract we're working with above, you'll notice that it emits an event when the stored value of the integer is set. In order to receive these events, we first need to instruct FireFly to listen for this specific type of blockchain event. To do this, we create a **Listener**. The `/contracts/listeners` endpoint is RESTful so there are `POST`, `GET`, and `DELETE` methods available on it. To create a new listener, we will make a `POST` request. We are going to tell FireFly to listen to events with name `"Changed"` from the FireFly Interface we defined earlier, referenced by its ID. We will also tell FireFly which contract address we expect to emit these events.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/listeners`
+```json
+{
+ "interface": {
+ "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
+ },
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ },
+ "event": {
+ "name": "Changed"
+ },
+ "options": {
+ "firstEvent": "oldest"
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "1bfa3b0f-3d90-403e-94a4-af978d8c5b14",
+ "interface": {
+ "id": "8bdd27a5-67c1-4960-8d1e-7aa31b9084d3"
+ },
+ "namespace": "default",
+ "name": "sb-66209ffc-d355-4ac0-7151-bc82490ca9df",
+ "protocolId": "sb-66209ffc-d355-4ac0-7151-bc82490ca9df",
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ },
+ "created": "2022-02-17T22:02:36.34549538Z",
+ "event": {
+ "name": "Changed",
+ "description": "",
+ "params": [
+ {
+ "name": "from",
+ "schema": {
+ "type": "string",
+ "details": {
+ "type": "address",
+ "internalType": "address",
+ "indexed": true
+ }
+ }
+ },
+ {
+ "name": "value",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ },
+ "options": {
+ "firstEvent": "oldest"
+ }
+}
+```
+
+We can see in the response, that FireFly pulls all the schema information from the FireFly Interface that we broadcasted earlier and creates the listener with that schema. This is useful so that we don't have to enter all of that data again.
+
+## Subscribe to events from our contract
+
+Now that we've told FireFly that it should listen for specific events on the blockchain, we can set up a **Subscription** for FireFly to send events to our app. This is exactly the same as listening for any other events from FireFly. For more details on how Subscriptions work in FireFly you can read the [Getting Started guide to Listen for events](./events.md). To set up our subscription, we will make a `POST` to the `/subscriptions` endpoint.
+
+We will set a friendly name `simple-storage` to identify the Subscription when we are connecting to it in the next step.
+
+We're also going to set up a filter to only send events blockchain events from our listener that we created in the previous step. To do that, we'll **copy the listener ID** from the step above (`1bfa3b0f-3d90-403e-94a4-af978d8c5b14`) and set that as the value of the `listener` field in the example below:
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/subscriptions`
+```json
+{
+ "namespace": "default",
+ "name": "simple-storage",
+ "transport": "websockets",
+ "filter": {
+ "events": "blockchain_event_received",
+ "blockchainevent": {
+ "listener": "1bfa3b0f-3d90-403e-94a4-af978d8c5b14"
+ }
+ },
+ "options": {
+ "firstEvent": "oldest"
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "f826269c-65ed-4634-b24c-4f399ec53a32",
+ "namespace": "default",
+ "name": "simple-storage",
+ "transport": "websockets",
+ "filter": {
+ "events": "blockchain_event_received",
+ "message": {},
+ "transaction": {},
+ "blockchainevent": {
+ "listener": "1bfa3b0f-3d90-403e-94a4-af978d8c5b14"
+ }
+ },
+ "options": {
+ "firstEvent": "-1",
+ "withData": false
+ },
+ "created": "2022-03-15T17:35:30.131698921Z",
+ "updated": null
+}
+```
+
+## Receive custom smart contract events
+
+ The last step is to connect a WebSocket client to FireFly to receive the event. You can use any WebSocket client you like, such as [Postman](https://www.postman.com/) or a command line app like [`websocat`](https://github.com/vi/websocat).
+
+Connect your WebSocket client to `ws://localhost:5000`
+
+After connecting the WebSocket client, send a message to tell FireFly to:
+
+- Start sending events
+- For the Subscription named `simple-storage`
+- On the `default` namespace
+- Automatically "ack" each event which will let FireFly immediately send the next event when available
+
+```json
+{
+ "type": "start",
+ "name": "simple-storage",
+ "namespace": "default",
+ "autoack": true
+}
+```
+
+
+### WebSocket event
+
+After creating the subscription, you should see an event arrive on the connected WebSocket client that looks something like this:
+
+```json
+{
+ "id": "0f4a31d6-9743-4537-82df-5a9c76ccbd1e",
+ "sequence": 24,
+ "type": "blockchain_event_received",
+ "namespace": "default",
+ "reference": "dd3e1554-c832-47a8-898e-f1ee406bea41",
+ "created": "2022-03-15T17:32:27.824417878Z",
+ "blockchainevent": {
+ "id": "dd3e1554-c832-47a8-898e-f1ee406bea41",
+ "sequence": 7,
+ "source": "ethereum",
+ "namespace": "default",
+ "name": "Changed",
+ "listener": "1bfa3b0f-3d90-403e-94a4-af978d8c5b14",
+ "protocolId": "000000000010/000000/000000",
+ "output": {
+ "from": "0xb7e6a5eb07a75a2c81801a157192a82bcbce0f21",
+ "value": "3"
+ },
+ "info": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1",
+ "blockNumber": "10",
+ "logIndex": "0",
+ "signature": "Changed(address,uint256)",
+ "subId": "sb-724b8416-786d-4e67-4cd3-5bae4a26eb0e",
+ "timestamp": "1647365460",
+ "transactionHash": "0xd5b5c716554097b2868d8705241bb2189bb76d16300f702ad05b0b02fccc4afb",
+ "transactionIndex": "0x0"
+ },
+ "timestamp": "2022-03-15T17:31:00Z",
+ "tx": {
+ "type": ""
+ }
+ },
+ "subscription": {
+ "id": "f826269c-65ed-4634-b24c-4f399ec53a32",
+ "namespace": "default",
+ "name": "simple-storage"
+ }
+}
+```
+
+You can see in the event received over the WebSocket connection, the blockchain event that was emitted from our first transaction, which happened in the past. We received this event, because when we set up both the Listener, and the Subscription, we specified the `"firstEvent"` as `"oldest"`. This tells FireFly to look for this event from the beginning of the blockchain, and that your app is interested in FireFly events since the beginning of FireFly's event history.
+
+In the event, we can also see the `blockchainevent` itself, which has an `output` object. These are the `params` in our FireFly Interface, and the actual output of the event. Here we can see the `value` is `3` which is what we set the integer to in our original transaction.
+
+**You've reached the end of the main guide to working with custom smart contracts in FireFly**. Hopefully this was helpful and gives you what you need to get up and running with your own contracts. There are several additional ways to invoke or query smart contracts detailed below, so feel free to keep reading if you're curious.
+
+## Appendix I: Work with a custom contract without creating a named API
+
+FireFly aims to offer a developer-friendly and flexible approach to using custom smart contracts. The guide above has detailed the most robust and feature-rich way to use custom contracts with FireFly, but there are several alternative API usage patterns available as well.
+
+It is possible to broadcast a contract interface and use a smart contract that implements that interface without also broadcasting a named API as above. There are several key differences (which may or may not be desirable) compared to the method outlined in the full guide above:
+
+- OpenAPI Spec and Swagger UI are not available
+- Each HTTP request to invoke/query the contract will need to include the contract location
+- The contract location will *not* have been broadcasted to all other members of the network
+- The URL to invoke/query the contract will be different (described below)
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/interfaces/8bdd27a5-67c1-4960-8d1e-7aa31b9084d3/invoke/set`
+```json
+{
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ },
+ "input": {
+ "newValue": 7
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "f310fa4a-73d8-4777-9f9d-dfa5012a052f"
+}
+```
+
+All of the same invoke, query, and subscribe endpoints are available on the contract interface itself.
+
+## Appendix II: Work directly with contracts with inline requests
+
+The final way of working with custom smart contracts with FireFly is to just put everything FireFly needs all in one request, each time a contract is invoked or queried. This is the most lightweight, but least feature-rich way of using a custom contract.
+
+To do this, we will need to put both the contract location, and a subset of the FireFly Interface that describes the method we want to invoke in the request body, in addition to the function input.
+
+### Request
+
+`POST` `http://localhost:5000/api/v1/namespaces/default/contracts/invoke`
+```json
+{
+ "location": {
+ "address": "0xa5ea5d0a6b2eaf194716f0cc73981939dca26da1"
+ },
+ "method": {
+ "name": "set",
+ "params": [
+ {
+ "name": "x",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256"
+ }
+ }
+ }
+ ],
+ "returns": []
+ },
+ "input": {
+ "x": 42
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "id": "386d3e23-e4bc-4a9b-bc1f-452f0a8c9ae5"
+}
+```
\ No newline at end of file
diff --git a/docs/images/hyperledger-firefly_color.svg b/docs/images/hyperledger-firefly_color.svg
new file mode 100644
index 0000000000..395e207f49
--- /dev/null
+++ b/docs/images/hyperledger-firefly_color.svg
@@ -0,0 +1,142 @@
+
+
+
diff --git a/docs/images/simple_storage_swagger.png b/docs/images/simple_storage_swagger.png
new file mode 100644
index 0000000000..95b422ec17
Binary files /dev/null and b/docs/images/simple_storage_swagger.png differ
diff --git a/docs/images/smart_contracts_async_flow.svg b/docs/images/smart_contracts_async_flow.svg
new file mode 100644
index 0000000000..89634efa79
--- /dev/null
+++ b/docs/images/smart_contracts_async_flow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/api_post_syntax.md b/docs/reference/api_post_syntax.md
index e2e1a58bbf..b18f4b18b4 100644
--- a/docs/reference/api_post_syntax.md
+++ b/docs/reference/api_post_syntax.md
@@ -18,14 +18,14 @@ nav_order: 2
## Syntax Overview
-Endpoints that allow submitting a transaction allow an optional query parameter called `confirm`. When `confirm=true` is set in the query string, FireFly will wait to send an HTTP response until the message has been confirmed. This means, where a blockchain transaciton is involved, the HTTP request will not return until the blockchain transaction is complete.
+Endpoints that allow submitting a transaction allow an optional query parameter called `confirm`. When `confirm=true` is set in the query string, FireFly will wait to send an HTTP response until the message has been confirmed. This means, where a blockchain transaction is involved, the HTTP request will not return until the blockchain transaction is complete.
-This is useful for endpoints such as registration, where the client app cannot proceed until the transaciton is complete and the member/node is registered. Rather than making a request to register a member/node and then repeatedly polling the API to check to see if it succeeded, an HTTP client can use this query parameter and block until registration is complete.
+This is useful for endpoints such as registration, where the client app cannot proceed until the transaction is complete and the member/node is registered. Rather than making a request to register a member/node and then repeatedly polling the API to check to see if it succeeded, an HTTP client can use this query parameter and block until registration is complete.
-> **NOTE**: This does *not* mean that any other member of the network has received, processed, or responded to the message. It just means that the transaction is complete from the perspective of the FireFly node to which the transaciton was submitted.
+> **NOTE**: This does *not* mean that any other member of the network has received, processed, or responded to the message. It just means that the transaction is complete from the perspective of the FireFly node to which the transaction was submitted.
## Example API Call
`POST` `/api/v1/messages/broadcast?confirm=true`
-This will broadcast a message and wait for the messsage to be confirmed before returning.
\ No newline at end of file
+This will broadcast a message and wait for the message to be confirmed before returning.
\ No newline at end of file
diff --git a/docs/reference/firefly_interface_format.md b/docs/reference/firefly_interface_format.md
new file mode 100644
index 0000000000..a83659f01f
--- /dev/null
+++ b/docs/reference/firefly_interface_format.md
@@ -0,0 +1,190 @@
+---
+layout: default
+title: FireFly Interface Format
+parent: Reference
+nav_order: 3
+---
+
+# FireFly Interface Format
+{: .no_toc }
+
+FireFly defines a common, blockchain agnostic way to describe smart contracts. This is referred to as a **Contract Interface**, and it is written in the FireFly Interface (FFI) format. It is a simple JSON document that has a name, a namespace, a version, a list of methods, and a list of events.
+
+## Table of contents
+{: .no_toc .text-delta }
+
+1. TOC
+{:toc}
+
+---
+
+## Overview
+
+There are four required fields when broadcasting a contract interface in FireFly: a `name`, a `version`, a list of `methods`, and a list of `events`. A `namespace` field will also be filled in automatically based on the URL path parameter. Here is an example of the structure of the required fields:
+
+```json
+{
+ "name": "example",
+ "version": "v1.0.0",
+ "methods": [],
+ "events": []
+}
+```
+
+> **NOTE**: Contract interfaces are scoped to a namespace. Within a namespace each contract interface must have a unique name and version combination. The same name and version combination can exist in *different* namespaces simultaneously.
+
+## Method
+
+Let's look at a what goes inside the `methods` array now. It is also a JSON object that has a `name`, a list of `params` which are the arguments the function will take and a list of `returns` which are the return values of the function. Optionally, it also has a `description` which can be helpful in OpenAPI Spec generation.
+
+```json
+{
+ "name": "add",
+ "description": "Add two numbers together",
+ "params": [],
+ "returns": []
+}
+```
+
+## Event
+
+What goes into the `events` array is very similar. It is also a JSON object that has a `name` and a list of `params`. The difference is that `events` don't have `returns`. Arguments that are passed to the event when it is emitted are in `params`. Optionally, it also has a `description` which can be helpful in OpenAPI Spec generation.
+
+```json
+{
+ "name": "added",
+ "description": "An event that occurs when numbers have been added",
+ "params": []
+}
+```
+
+## Param
+
+Both `methods`, and `events` have lists of `params` or `returns`, and the type of JSON object that goes in each of these arrays is the same. It is simply a JSON object with a `name` and a `schema`. There is also an optional `details` field that is passed to the blockchain plugin for blockchain specific requirements.
+
+```json
+{
+ "name": "x",
+ "schema": {
+ "type": "integer",
+ "details": {}
+ }
+}
+```
+
+### Schema
+
+The param `schema` is an important field which tells FireFly the type information about this particular field. This is used in several different places, such as OpenAPI Spec generation, API request validation, and blockchain request preparation.
+
+The `schema` field accepts [JSON Schema (version 2020-12)](https://json-schema.org/specification-links.html#2020-12) with several additional requirements:
+
+- A `type` field is always mandatory
+- The list of valid types is:
+ - `boolean`
+ - `integer`
+ - `string`
+ - `object`
+ - `array`
+- Blockchain plugins can add their own specific requirements to this list of validation rules
+
+> **NOTE**: Floats or decimals are not currently accepted because certain underlying blockchains (e.g. Ethereum) only allow integers
+
+The type field here is the JSON input type when making a request to FireFly to invoke or query a smart contract. This type can be different from the actual blockchain type, usually specified in the `details` field, if there is a compatible type mapping between the two.
+
+### Schema details
+
+The details field is quite important in some cases. Because the `details` field is passed to the blockchain plugin, it is used to encapsulate blockchain specific type information about a particular field. Additionally, because each blockchain plugin can add rules to the list of schema requirements above, a blockchain plugin can enforce that certain fields are always present within the `details` field.
+
+For example, the Ethereum plugin always needs to know what Solidity type the field is. It also defines several optional fields. A full Ethereum details field may look like:
+
+```json
+{
+ "type": "uint256",
+ "internalType": "uint256",
+ "indexed": false
+}
+```
+
+## Automated generation of FireFly Interfaces
+
+A convenience endpoint exists on the API to facilitate converting from native blockchain interface formats such as an Ethereum ABI to the FireFly Interface format. For details, please see the [API documentation for the contract interface generation endpoint](../swagger/swagger.html#/default/postGenerateContractInterface).
+
+For an example of using this endpoint with a specific Ethereum contract, please see the [Getting Started guide to Work with custom smart contracts](../gettingstarted/custom_contracts.html#the-firefly-interface-format).
+
+## Full Example
+
+Putting it all together, here is a full example of the FireFly Interface format with all the fields filled in:
+
+```json
+{
+ "namespace": "default",
+ "name": "SimpleStorage",
+ "description": "A simple smart contract that stores and retrieves an integer on-chain",
+ "version": "v1.0.0",
+ "methods": [
+ {
+ "name": "get",
+ "description": "Retrieve the value of the stored integer",
+ "params": [],
+ "returns": [
+ {
+ "name": "output",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "set",
+ "description": "Set the stored value on-chain",
+ "params": [
+ {
+ "name": "newValue",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ],
+ "returns": []
+ }
+ ],
+ "events": [
+ {
+ "name": "Changed",
+ "description": "An event that is fired when the stored integer value changes",
+ "params": [
+ {
+ "name": "from",
+ "schema": {
+ "type": "string",
+ "details": {
+ "type": "address",
+ "internalType": "address",
+ "indexed": true
+ }
+ }
+ },
+ {
+ "name": "value",
+ "schema": {
+ "type": "integer",
+ "details": {
+ "type": "uint256",
+ "internalType": "uint256"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 84ca77df8b..4d78349bc0 100644
--- a/go.mod
+++ b/go.mod
@@ -27,6 +27,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.10
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/microcosm-cc/bluemonday v1.0.16
+ github.com/miracl/conflate v1.2.1 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/onsi/ginkgo v1.16.1 // indirect
github.com/onsi/gomega v1.11.0 // indirect
diff --git a/go.sum b/go.sum
index 515d1239eb..9ef3aabe22 100644
--- a/go.sum
+++ b/go.sum
@@ -810,6 +810,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/miracl/conflate v1.2.1 h1:QlB+Hjh8vnPIjimCK2VKEvtLVxVGIVxNQ4K95JRpi90=
+github.com/miracl/conflate v1.2.1/go.mod h1:F85f+vrE7SwfRoL31EpLZFa1sub0SDxzcwxDBxFvy7k=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -1083,8 +1085,13 @@ github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@@ -1233,6 +1240,7 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=