├── .gitignore ├── assets ├── FireFly.gif └── metadata.json ├── images ├── content-type.png ├── uncheck_boxes.png ├── polygon_faucet.png ├── remix_compiler.png ├── contracts_wizard.png ├── polygonscan_matic.png └── workshop_overview.svg └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /assets/FireFly.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/assets/FireFly.gif -------------------------------------------------------------------------------- /images/content-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/content-type.png -------------------------------------------------------------------------------- /images/uncheck_boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/uncheck_boxes.png -------------------------------------------------------------------------------- /images/polygon_faucet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/polygon_faucet.png -------------------------------------------------------------------------------- /images/remix_compiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/remix_compiler.png -------------------------------------------------------------------------------- /images/contracts_wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/contracts_wizard.png -------------------------------------------------------------------------------- /images/polygonscan_matic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nguyer/firefly-nft-workshop-2023-03-023/HEAD/images/polygonscan_matic.png -------------------------------------------------------------------------------- /assets/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Animated Hyperledger FireFly logo pixel art created by Nicko Guyer", 3 | "external_url": "https://github.com/hyperledger/firefly", 4 | "image": "https://ipfs.io/ipfs/QmWgiVuLF7JVrPkH7nkrEigZoZTQBkUoZwUi32PtPWQdp6", 5 | "name": "Hyperledger FireFly Logo Pixel Art" 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Use Hyperledger FireFly to launch an NFT collection on a public blockchain 2 | 3 | Welcome to the Hyperledger Meetup where we will launch our own NFT collection on a public blockchain! For this workshop we will use the Polygon Mumbai testnet so that we can experiment with a custom token contract and using FireFly to manage it. 4 | 5 | ## Prerequisites 6 | 7 | - An Ethereum wallet app. I recommend [Alpha Wallet](https://alphawallet.com/) as it is open source, supports connecting to testnets, and will show artwork for ERC-721 tokens. 8 | - [Docker](https://www.docker.com/) 9 | - [Docker Compose](https://docs.docker.com/compose/) 10 | - openssl 11 | 12 | ### Linux Users 13 | 14 | > **NOTE**: For Linux users, it is recommended that you add your user to the `docker` group so that you do not have to run `ff` or `docker` as root or with sudo. For more information about Docker permissions on Linux, please see [Docker’s documentation on the topic](https://docs.docker.com/engine/install/linux-postinstall/). 15 | 16 | ### Windows Users 17 | 18 | > **NOTE**: For Windows users, we recommend that you use [Windows Subsystem for Linux 2 (WSL2)](https://docs.microsoft.com/en-us/windows/wsl/). Binaries provided for Linux will work in this environment. 19 | 20 | ## High level overview of components of the workshop 21 | 22 | In this workshop we will: 23 | 24 | - Run FireFly on our local machines 25 | - Use FireFly to deploy an ERC-721 smart contract to the Polygon Mumbai testnet 26 | - Use FireFly to upload an image and `metadata.json` to public IPFS 27 | - Mint a token with our smart contract 28 | - Transfer that token to a wallet we can see on a smartphone app 29 | 30 | ![Workshop overview](./images/workshop_overview.svg) 31 | 32 | ## Install the FireFly CLI 33 | 34 | First you will need to get the FireFly CLI set up on your machine. You can follow through the [first page of the Getting Started Guide](https://hyperledger.github.io/firefly/gettingstarted/firefly_cli.html) from the FireFly docs to get it set up. 35 | 36 | ## Create a new FireFly stack 37 | 38 | With the FireFly CLI installed, we can run `ff init` to create your new stack. In order to configure your blockchain connector for use with a public chain, we will want to change a couple of settings. To do this, create a new file at `~/evmconnect.yml` and set the contents to the following: 39 | 40 | ```yaml 41 | confirmations: 42 | required: 4 # choose the number of confirmations you require 43 | policyengine.simple: 44 | fixedGasPrice: null 45 | gasOracle: 46 | mode: connector 47 | ``` 48 | 49 | You'll need to have a JSON RPC Node connected to the Polygon testnet. You can either run a node yourself, connect to a free public node, or use a node running service provider. For a list of the JSON RPC methods required by FireFly, please see the [Evmconnect README](https://github.com/hyperledger/firefly-evmconnect#blockchain-node-compatibility) 50 | 51 | Use the following command line to create your stack and use the custom blockchain connector config file that we created: 52 | 53 | ``` 54 | ff init ethereum workshop 1 \ 55 | --multiparty=false \ 56 | --ipfs-mode public \ 57 | --blockchain-node remote-rpc \ 58 | --remote-node-url \ 59 | --chain-id 80001 \ 60 | --connector-config ~/evmconnect.yml 61 | ``` 62 | 63 | Now you can start up your stack by running: 64 | 65 | ``` 66 | ff start workshop 67 | ``` 68 | 69 | ## Get some MATIC to fund your account 70 | 71 | While your stack is starting, you can go get some MATIC to be able to pay for transactions on the test chain. First, we need to know your wallet address that your FireFly node is using. We can use the FireFly to retrieve that by running: 72 | 73 | ### Look up your wallet address 74 | 75 | ``` 76 | ff accounts list workshop | grep address 77 | ``` 78 | 79 | > **NOTE**: We're using `grep` here to only log the address. Without this, the private key would also be logged, which can be useful if you need to know it. However, to not leak private keys for this workshop, we will not be displaying it. 80 | 81 | ### Use the Polygon Mumbai faucet 82 | 83 | Now open a browser and go to https://faucet.polygon.technology/. Paste your wallet address and click "Submit". 84 | 85 | Polygon Faucet 86 | 87 | ### Check your address on Polygonscan 88 | 89 | Now you should be able to go to https://mumbai.polygonscan.com/ and search for your wallet address and see some MATIC in your wallet. 90 | 91 | ![Polygonscan](./images/polygonscan_matic.png) 92 | 93 | ## Create a smart contract 94 | 95 | Now you can work on creating your own token smart contract. Probably the quickest and easiest way to get started with developing a custom token contract is with the [Open Zeppelin Contracts Wizard](https://docs.openzeppelin.com/contracts/4.x/wizard). 96 | 97 | ### Open Zeppelin Contracts Wizard 98 | 99 | - Select ERC-721 100 | - Select Mintable 101 | - Select Auto Increment Ids 102 | - Select URI Storage 103 | - Give your token a cool name and symbol 104 | 105 | ![Contracts Wizard](./images/contracts_wizard.png) 106 | 107 | At this point, you would likely want to customize the logic in the smart contract or add custom functions to meet the specific needs of your application. It's also really important to thoroughly test your smart contract 108 | 109 | ### Compile your smart contract 110 | 111 | Before you deploy your smart contract to the chain you need to compile it. The Open Zeppelin Contracts Wizard also makes this really easy to do in your browser by giving you the ability to open your smart contract in Remix, which is a fully featured Ethereum smart contract IDE, right in your browser. 112 | 113 | Click **Open in Remix**. 114 | 115 | Once your contract is opened in the Remix IDE, click the **Compile** button. After compilation, you should see two new small buttons appear at the bottom of that section: **ABI** and **Bytecode**. These will copy portions of the compiled contract to your clipboard. We will need these in the next step. 116 | 117 | ## Deploy your smart contract 118 | 119 | Now we can use FireFly to deploy our smart contract. We can do this right in our browser through the Swagger UI, using the [`/contracts/deploy`](http://127.0.0.1:5000/api#/Default%20Namespace/postContractDeploy) endpoint: 120 | 121 | Set the request body to the following, and replace the appropriate sections with the compiled output from Remix: 122 | 123 | ``` 124 | { 125 | "contract": "PASTE_BYTECODE_HERE", 126 | "definition": PASTE_ABI_HERE, 127 | "input": [] 128 | } 129 | ``` 130 | 131 | After the transaction has been confirmed we should get a long response. If you scroll down near the bottom, we should see the deployed contract address: 132 | 133 | ```json 134 | { 135 | ... 136 | "output": { 137 | "contractLocation": { 138 | "address": "0xfd403b063ecf959867973a4cf383c48b9a06e19e" 139 | }, 140 | }, 141 | ... 142 | } 143 | ``` 144 | 145 | You should also see the contract deployment event in your [FireFly Explorer web UI](http://127.0.0.1:5000/ui). We can go look up the block number by clicking in to the event and looking at the detailed output. We will need this in a future step. 146 | 147 | Alternatively, you should now be able to lookup your contract on [Polygonscan](https://mumbai.polygonscan.com/) and see the block number there as well. 148 | 149 | ## 150 | 151 | > **Additional reading**: If you are interested in more details on using FireFly with an ERC-721 contract, you can read the relevant docs page here: https://hyperledger.github.io/firefly/tutorials/tokens/erc721.html 152 | 153 | ## Create a contract interface 154 | 155 | So far we've only deployed the contract to the chain, which is great. The next step is to teach your FireFly node about your particular contract and all of the methods, parameters, events, and types that it defines. To do that, we need to create a FireFly Interface (FFI) and upload that to FireFly. 156 | 157 | ### Generate the contract interface 158 | 159 | FireFly also provides a convenience API to automatically generate an FFI from an Ethereum ABI. To do that, copy your ABI from Remix (above) and use it to fill in the following request body for the [`/contracts/interfaces/generate`](http://127.0.0.1:5000/api#/Default%20Namespace/postGenerateContractInterface) endpoint: 160 | 161 | ```json 162 | { 163 | "input": { 164 | "abi": PASTE_ABI_HERE 165 | } 166 | } 167 | ``` 168 | 169 | ### Save the contract interface 170 | 171 | Copy the entire output from the previous step and use that as the request body for the [`/contracts/interfaces`](http://127.0.0.1:5000/api#/Default%20Namespace/postNewContractInterface) endpoint: 172 | 173 | ```json 174 | { 175 | "name": "FILL_IN_A_NAME", 176 | "version": "FILL_IN_A_VERSION", 177 | ... 178 | } 179 | ``` 180 | 181 | Note the ID of the generated interface as you will need this in the next step: 182 | 183 | ```json 184 | { 185 | "id": "2fd5e1f7-0faf-4d6f-bd9b-9f9003165003" 186 | ... 187 | } 188 | ``` 189 | 190 | ## Create a Token Pool 191 | 192 | Now that you've told FireFly about the "shape" of your contract, it's time to tell it to start tracking transactions on this contract. To do that, you create a Token Pool. To do that, use the [`/tokens/pools`](http://127.0.0.1:5000/api#/Default%20Namespace/postTokenPool) endpoint: 193 | 194 | ```json 195 | { 196 | "config": { 197 | "address": "YOUR_DEPLOYED_CONTRACT_ADDRESS", 198 | "blockNumber": "YOUR_CONTRACT_DEPLOYMENT_BLOCK_NUMBER" 199 | }, 200 | "interface": { 201 | "id": "YOUR_INTERFACE_ID" 202 | }, 203 | "name": "YOUR_TOKEN_NAME", 204 | "symbol": "YOUR_TOKEN_SYMBOL", 205 | "type": "nonfungible" 206 | } 207 | ``` 208 | 209 | ## Upload token assets 210 | 211 | Before minting our first token, we need to upload some assets, namely an image and a `metadata.json`. We will upload the image first, and get the IPFS CID for the image. We will then reference the CID in the `metadata.json` file. 212 | 213 | ### Upload the image 214 | 215 | For this workshop I am going to upload this animated FireFly pixel art. You are welcome to use whatever image you want though. 216 | 217 |

218 | FireFly 219 |

220 | 221 | ## Use FireFly to upload your image 222 | 223 | Your local FireFly stack also has an IPFS node running locally. You can use FireFly's data APIs to upload your NFT image and metadata. You can use the [FireFly Swagger UI](http://127.0.0.1:5000/api#/Default%20Namespace/postData) to upload you image. 224 | 225 | > **NOTE**: To upload a file through the Swagger UI, make sure you select `multipart/form-data` from the drop down menu on that endpoint. 226 | > 227 | > Content-Type 228 | > 229 | > You will also need to uncheck the bottom two checkboxes for the `metadata` and `validator` fields. 230 | > 231 | > Uncheck these 232 | 233 | When you click **Execute** to upload the file, FireFly should return a payload like this: 234 | 235 | ```json 236 | { 237 | "blob": { 238 | "hash": "string", 239 | "name": "string", 240 | "public": "string", 241 | "size": 0 242 | }, 243 | "created": "2023-03-16T18:27:00.795Z", 244 | "datatype": { 245 | "name": "string", 246 | "version": "string" 247 | }, 248 | "hash": "string", 249 | "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", 250 | "namespace": "string", 251 | "public": "string", 252 | "validator": "string", 253 | "value": "string" 254 | } 255 | ``` 256 | 257 | At this point, FireFly has only stored the file in its _private_ data store. To tell FireFly that we would like to make this file available _publicly_, copy the UUID from the `"id"` field in the response above, and call the publish endpoint. You can use the [FireFly Swagger UI](http://127.0.0.1:5000/api#/Default%20Namespace/postDataBlobPublish) to publish the uploaded file to IPFS. 258 | 259 | > **NOTE:** Be sure to set the `dataid` field in the path with the ID returned in the previous response. Also clear out all of the `additionalProp` fields in the request body. Your request body should be an empty JSON object: `{}` 260 | 261 | FireFly will return a response like this; 262 | 263 | ```json 264 | { 265 | "id": "4c2f5ae7-f650-42f3-a586-c84605f333c3", 266 | "validator": "json", 267 | "namespace": "default", 268 | "hash": "5fda07bad90854830cce855ea7d950f6841f420e5aed3bc2f24be0f3fe0a0896", 269 | "created": "2023-03-16T18:26:44.71439596Z", 270 | "value": null, 271 | "blob": { 272 | "hash": "5fda07bad90854830cce855ea7d950f6841f420e5aed3bc2f24be0f3fe0a0896", 273 | "size": 27486, 274 | "name": "", 275 | "public": "QmWgiVuLF7JVrPkH7nkrEigZoZTQBkUoZwUi32PtPWQdp6" 276 | } 277 | } 278 | ``` 279 | 280 | Here I can see in the `"public"` field that FireFly has uploaded my image to my local IPFS node its CID is: 281 | 282 | ``` 283 | QmYM6ph25YpDZa2u4joPRhGEjnkHGp9JqZjQDpvdgPZ9Vj 284 | ``` 285 | 286 | Take note of this CID, because we will need to put it in your `metadata.json` file in the next step. 287 | 288 | > **NOTE**: It may take some time for your file to be readily accessible from public IPFS gateways. I've seen it take up to 15 minutes to replicate sometimes. 289 | 290 | ## Create your `metadata.json` file 291 | 292 | [EIP-721](https://eips.ethereum.org/EIPS/eip-721) defines an interface for retrieving a URI for a given NFT token index. A common practice is to store the `metadata.json` file on IPFS so it is immutable. The `metadata.json` file for this token looks like this: 293 | 294 | ```json 295 | { 296 | "description": "Animated Hyperledger FireFly logo pixel art created by Nicko Guyer", 297 | "external_url": "https://github.com/hyperledger/firefly", 298 | "image": "https://ipfs.io/ipfs/QmWgiVuLF7JVrPkH7nkrEigZoZTQBkUoZwUi32PtPWQdp6", 299 | "name": "Hyperledger FireFly Logo Pixel Art" 300 | } 301 | ``` 302 | 303 | You should create your own `metadata.json` document and be sure that the image URL points to the IPFS CID for your file that you uploaded in the previous step. 304 | 305 | ## Upload your `metadata.json` file to IPFS 306 | 307 | To upload your `metadata.json`, repeat the steps from the section above where you used FireFly's API to upload your image. Make note of the IPFS CID for your `metadata.json` file. In my case, mine was: 308 | 309 | ``` 310 | QmdXtBtB2hTTpop9cSCaTsGigUjiGjJ6TsimMauWKoaqq5 311 | ``` 312 | 313 | For more details on the `metadata.json` format you can refer to [EIP-721](https://eips.ethereum.org/EIPS/eip-721) 314 | 315 | ## Mint a token 316 | 317 | Now we're ready to mint our token! Use the [`/tokens/mint`](http://127.0.0.1:5000/api#/Default%20Namespace/postTokenMint) endpoint and fill in your wallet address and the IPFS CID that represents the metadata for this token. 318 | 319 | ```json 320 | { 321 | "amount": "1", 322 | "to": "YOUR_FIREFLY_WALLET_ADDRESS", 323 | "uri": "ipfs://YOUR_IPFS_CID" 324 | } 325 | ``` 326 | 327 | > **NOTE**: You could have FireFly mint the token to its own wallet, and then transfer it at a later time, or you could mint it directory to another wallet that you own. 328 | 329 | ## Transfer your token 330 | 331 | You can also use FireFly's API to easily transfer any tokens owned by your node's wallet. For example, if you transfer your token to your AlphaWallet address, you should be able to see your shiny new token in your wallet app. You can use the [`/tokens/transfers`](http://127.0.0.1:5000/api#/Default%20Namespace/postTokenTransfer) endpoint with a payload like this: 332 | 333 | ```json 334 | { 335 | "amount": "1", 336 | "from": "SENDER_ADDRESS", 337 | "to": "RECIPIENT_ADDRESS", 338 | "tokenIndex": "0" 339 | } 340 | ``` 341 | 342 | > **NOTE**: You will need to enable testnets in AlphaWallet and specifically enable Mumbai to see your token 343 | -------------------------------------------------------------------------------- /images/workshop_overview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
FireFly Core
FireFly Core
Polygon Mumbai Testnet
Polygon Mu...
Blockchain RPC Node
Blockchain RPC Node
Evmconnect
Evmconnect
Local IPFS Node
Local IPFS...
Remote IPFS Peers
Remote IPF...
Blockchain RPC Node
Blockchain RPC Node
Accounts / balances
Accounts / balances
Wallet App
Wallet App
Your local dev machine
Your local dev machine
Images and metadata
Images and metadata
Public Infrastructure
Public Infrastructure
Text is not SVG - cannot display
--------------------------------------------------------------------------------