Link Search Menu Expand Document

Use ERC-721 tokens

Table of contents

  1. Previous steps: Start your environment
  2. About the sample token contracts
  3. Use the Sandbox (optional)
  4. Create a pool (using default token factory)
    1. Request
    2. Response
    3. Get the address of the deployed contract
      1. Request
      2. Response
  5. Create a pool (from a deployed token contract)
    1. Request
  6. Mint a token
    1. Request
    2. Response
  7. Transfer a token
    1. Request
    2. Response
  8. Sending data with a transfer
    1. Broadcast message
    2. Private message
  9. Burn tokens
  10. Token approvals
    1. Request
    2. Response
  11. Use Metamask
    1. Configure a new network
    2. Import tokens
    3. Transfer tokens

Previous steps: Start your environment

If you haven’t started a FireFly stack already, please go to the Getting Started guide on how to Start your environment. This will set up a token connector that works with both ERC-20 and ERC-721 by default.

← ② Start your environment

About the sample token contracts

If you are using the default ERC-20 / ERC-721 token connector, when the FireFly CLI set up your FireFly stack, it also deployed a token factory contract. When you create a token pool through FireFly’s token APIs, the token factory contract will automatically deploy an ERC-20 or ERC-721 contract, based on the pool type in the API request.

⚠️ WARNING: The default token contract that was deployed by the FireFly CLI is only provided for the purpose of learning about FireFly. It is not a production grade contract. If you intend to deploy a production application using tokens on FireFly, you should research token contract best practices. For details, please see the source code for the contract that was deployed.

Use the Sandbox (optional)

At this point you could open the Sandbox at http://127.0.0.1:5109/home?action=tokens.pools and perform the functions outlined in the rest of this guide. Or you can keep reading to learn how to build HTTP requests to work with tokens in FireFly. Tokens Sandbox

Create a pool (using default token factory)

After your stack is up and running, the first thing you need to do is create a token pool. Every application will need at least one token pool. At a minimum, you must always specify a name and type for the pool.

If you’re using the default ERC-20 / ERC-721 token connector and its sample token factory, it will automatically deploy a new ERC-721 contract instance.

Request

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/pools

{
    "type": "nonfungible",
    "name": "nfts"
}

Response

{
    "id": "a92a0a25-b886-4b43-931f-4add2840258a",
    "type": "nonfungible",
    "namespace": "default",
    "name": "nfts",
    "key": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "connector": "erc20_erc721",
    "tx": {
        "type": "token_pool",
        "id": "00678116-89d2-4295-990c-bd5ffa6e2434"
    }
}

Other parameters:

  • You must specify a connector if you have configured multiple token connectors
  • You may pass through a config object of additional parameters, if supported by your token connector
  • You may specify a key understood by the connector (i.e. an Ethereum address) if you’d like to use a non-default signing identity

Get the address of the deployed contract

To lookup the address of the new contract, you can lookup the token pool by its ID on the API. Creating the token pool will also emit an event which will contain the address. To query the token pool you can make a GET request to the pool’s ID:

Request

GET http://127.0.0.1:5000/api/v1/namespaces/default/tokens/pools/5811e8d5-52d0-44b1-8b75-73f5ff88f598

Response

{
    "id": "a92a0a25-b886-4b43-931f-4add2840258a",
    "type": "nonfungible",
    "namespace": "default",
    "name": "nfts",
    "standard": "ERC721",
    "locator": "address=0xc4d02efcfab06f18ec0a68e00b98ffecf6bf7e3c&schema=ERC721WithData&type=nonfungible",
    "connector": "erc20_erc721",
    "message": "53d95dda-e8ca-4546-9226-a0fdc6ec03ec",
    "state": "confirmed",
    "created": "2022-04-29T12:03:51.971349509Z",
    "info": {
        "address": "0xc4d02efcfab06f18ec0a68e00b98ffecf6bf7e3c",
        "name": "nfts",
        "schema": "ERC721WithData"
    },
    "tx": {
        "type": "token_pool",
        "id": "00678116-89d2-4295-990c-bd5ffa6e2434"
    }
}

Create a pool (from a deployed token contract)

If you wish to index and use a contract that is already on the chain, it is recommended that you first upload the ABI for your specific contract by creating a FireFly contract interface. This step is optional if you’re certain that your ERC-721 ABI conforms to the default expectations of the token connector, but is generally recommended.

See the README of the token connector for details on what contract variants can currently be understood.

You can pass a config object with an address and blockNumber when you make the request to create the token pool, and if you created a contract interface, you can include the interface ID as well.

Request

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/pools

{
  "name": "testpool",
  "type": "fungible",
  "interface": {
    "id": "b9e5e1ce-97bb-4a35-a25c-52c7c3f523d8"
  },
  "config": {
    "address": "0xb1C845D32966c79E23f733742Ed7fCe4B41901FC",
    "blockNumber": "0"
  }
}

Mint a token

Once you have a token pool, you can mint tokens within it. When using the sample contract deployed by the CLI, the following are true:

  • only the creator of a pool is allowed to mint within that pool
  • the tokenIndex must be set to a unique value
  • the amount must be 1

A different ERC-721 contract may define its own requirements.

Request

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/mint

{
  "amount": "1",
  "tokenIndex": "1"
}

Response

{
    "type": "mint",
    "localId": "2de2e05e-9474-4a08-a64f-2cceb076bdaa",
    "pool": "a92a0a25-b886-4b43-931f-4add2840258a",
    "connector": "erc20_erc721",
    "key": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "from": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "to": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "amount": "1",
    "tx": {
        "type": "token_transfer",
        "id": "0fad4581-7cb2-42c7-8f78-62d32205c2c2"
    }
}

Other parameters:

  • You must specify a pool name if you’ve created more than one pool
  • You may specify a key understood by the connector (i.e. an Ethereum address) if you’d like to use a non-default signing identity
  • You may specify to if you’d like to send the minted tokens to a specific identity (default is the same as key)

Transfer a token

You may transfer tokens within a pool by specifying an amount and a destination understood by the connector (i.e. an Ethereum address). With the default sample contract, only the owner of the tokens or another approved account may transfer their tokens, but a different contract may define its own permission model.

When transferring an NFT, you must also specify the tokenIndex that you wish to transfer. The tokenIndex is simply the ID of the specific NFT within the pool that you wish to transfer.

NOTE: When transferring NFTs the amount must be 1. If you wish to transfer more NFTs, simply call the endpoint multiple times, specifying the token index of each token to transfer.

Request

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/transfers

{
  "amount": "1",
  "tokenIndex": "1",
  "to": "0xa4222a4ae19448d43a338e6586edd5fb2ac398e1"
}

Response

{
    "type": "transfer",
    "localId": "f5fd0d13-db13-4d70-9a99-6bcd747f1e42",
    "pool": "a92a0a25-b886-4b43-931f-4add2840258a",
    "tokenIndex": "1",
    "connector": "erc20_erc721",
    "key": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "from": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "to": "0xa4222a4ae19448d43a338e6586edd5fb2ac398e1",
    "amount": "1",
    "tx": {
        "type": "token_transfer",
        "id": "63c1a89b-240c-41eb-84bb-323d56f4ba5a"
    }
}

Other parameters:

  • You must specify a pool name if you’ve created more than one pool
  • You may specify a key understood by the connector (i.e. an Ethereum address) if you’d like to use a non-default signing identity
  • You may specify from if you’d like to send tokens from a specific identity (default is the same as key)

Sending data with a transfer

All transfers (as well as mint/burn operations) support an optional message parameter that contains a broadcast or private message to be sent along with the transfer. This message follows the same convention as other FireFly messages, and may be comprised of text or blob data, and can provide context, metadata, or other supporting information about the transfer. The message will be batched, hashed, and pinned to the primary blockchain.

The message ID and hash will also be sent to the token connector as part of the transfer operation, to be written to the token blockchain when the transaction is submitted. All recipients of the message will then be able to correlate the message with the token transfer.

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/transfers

Broadcast message

{
  "amount": 1,
  "tokenIndex": "1",
  "to": "0x07eab7731db665caf02bc92c286f51dea81f923f",
  "message": {
    "data": [{
      "value": "payment for goods"
    }]
  }
}

Private message

{
  "amount": 1,
  "tokenIndex": "1",
  "to": "0x07eab7731db665caf02bc92c286f51dea81f923f",
  "message": {
    "header": {
      "type": "transfer_private",
    },
    "group": {
      "members": [{
          "identity": "org_1"
      }]
    },
    "data": [{
      "value": "payment for goods"
    }]
  }
}

Note that all parties in the network will be able to see the transfer (including the message ID and hash), but only the recipients of the message will be able to view the actual message data.

Burn tokens

You may burn a token by specifying the token’s tokenIndex. With the default sample contract, only the owner of a token or another approved account may burn it, but a different contract may define its own permission model.

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/burn

{
  "amount": 1,
  "tokenIndex": "1"
}

Other parameters:

  • You must specify a pool name if you’ve created more than one pool
  • You may specify a key understood by the connector (i.e. an Ethereum address) if you’d like to use a non-default signing identity
  • You may specify from if you’d like to burn tokens from a specific identity (default is the same as key)

Token approvals

You can also approve other wallets to transfer tokens on your behalf with the /approvals API. The important fields in a token approval API request are as follows:

  • approved: Sets whether another account is allowed to transfer tokens out of this wallet or not. If not specified, will default to true. Setting to false can revoke an existing approval.
  • operator: The other account that is allowed to transfer tokens out of the wallet specified in the key field
  • config.tokenIndex: The specific token index within the pool that the operator is allowed to transfer. If 0 or not set, the approval is valid for all tokens.
  • key: The wallet address for the approval. If not set, it defaults to the address of the FireFly node submitting the transaction

Here is an example request that would let the signing account 0x634ee8c7d0894d086c7af1fc8514736aed251528 transfer tokenIndex 2 from my wallet.

Request

POST http://127.0.0.1:5000/api/v1/namespaces/default/tokens/approvals

{
  "operator": "0x634ee8c7d0894d086c7af1fc8514736aed251528",
  "config": {
      "tokenIndex": "2"
  }
}

Response

{
    "localId": "46fef50a-cf93-4f92-acf8-fae161b37362",
    "pool": "e1477ed5-7282-48e5-ad9d-1612296bb29d",
    "connector": "erc20_erc721",
    "key": "0x14ddd36a0c2f747130915bf5214061b1e4bec74c",
    "operator": "0x634ee8c7d0894d086c7af1fc8514736aed251528",
    "approved": true,
    "tx": {
        "type": "token_approval",
        "id": "00faa011-f42c-403d-a047-2df7318967cd"
    },
    "config": {
       "tokenIndex": "2"
    }
}

Use Metamask

Now that you have an ERC-721 contract up and running, you may be wondering how to use Metamask (or some other wallet) with this contract. This section will walk you through how to connect Metamask to the blockchain and token contract that FireFly is using.

Configure a new network

The first thing we need to do is tell Metamask how to connect to our local blockchain node. To do that:

  • Click your account icon
  • In the drop down menu, click Settings Metamask Settings

  • On the left hand side of the page, click Networks
  • Click the Add a network button Metamask Add Network

  • Fill in the network details:
    • Network Name: FireFly (could be any name)
    • New RPC URL: http://127.0.0.1:5100
    • Chain ID: 2021
    • Currency Symbol: ETH
  • Click Save Metamask Network Details

Import tokens

Metamask won’t know about our custom ERC-721 contract until we give it the Ethereum address for the contract, so that’s what we’ll do next.

  • Click on Import tokens Metamask Import Tokens

  • Enter the Ethereum address of the contract
  • Enter a Token Symbol (can be anything you want)
  • Click Add Custom Token

NOTE: You can find the address of your contract from the response to the request to create the token pool above. You can also do a GET to http://127.0.0.1:5000/api/v1/namespaces/default/tokens/pools to lookup your configured token pools.

Metamask Import Tokens

Transfer tokens

Now you can copy your account address from your Metamask wallet, and perform a transfer from FireFly’s API (as described above) to your Metamask address.

Metamask Account Address

After a couple seconds, you should see your token show up in your Metamask wallet.

Metamask Tokens Received

NOTE: While the NFT token balance can be viewed in Metamask, it does not appear that Metamask supports sending these tokens to another address at this time.