A Decentralized Application (dApp) with React that demonstrates how to implement IPFS file uploads and store the IPFS hash in an Ethereum smart contract.
IPFS and the Blockchain are a perfect match. Why? You can address large amounts of data with IPFS and place the immutable, permanent IPFS links into a blockchain transaction. This will timestamp and secure your content, without having to put the data on the chain itself. You now have undisputable proof that your image existed at that time it was uploaded.
This project is an Ethereum Decentralized Application (dApp) using React, Redux, React Router and Bootstrap. It demonstrates how to implement IPFS file uploads and store the IPFS hash on the blockchain.
In this application, the main page displays a list of image cards filtered by owner i.e. MetaMask account. Each image card displays the image, title, description, tags, upload timestamp and IPFS hash.
Click Upload Image to upload an image to IPFS and the blockchain. You are required to enter an image title, optional description and appropriate tags. Click Upload to submit.
Note: You can find sample images in the assets/sample-images
folder.
Click Details to view the image and blockchain transaction details.
Note: The current version of the application does not persist the blockchain transaction information in a permanent store such as MongoDB or PostgreSQL. Instead, we chose to store it in browser local storage keyed by the account address so to provide a better user experience. Otherwise, this information is lost when you refresh the browser or login as another user. Keep in mind that this information is transient when running Ganache. Be sure to clear local storage before restarting the app by following these instructions.
For this project, we used the following stack:
Install Truffle Framework and Ganache CLI globally. If you prefer, the graphical version of Ganache works as well.
npm install -g truffle
npm install -g ganache-cli
Note: The graphical version of Ganache seems to be more stable on Mac whereas Ganache CLI works fine on Ubuntu.
Run the development blockchain.
// no blocktime specified so transaction will be mined instantly
ganache-cli
You may want to pass in a blocktime. Otherwise, it’s difficult to track things like loading indicators because Ganache will mine instantly.
Note: We’ve noticed that using a blocktime while running truffle test
causes issues.
// 3 second blocktime
ganache-cli -b 3
Open another terminal, clone this repo and install its dependencies.
git clone https://github.com/iwaldman/ipfs-image-dapp.git
cd ipfs-image-dapp
npm install
Note: If you get an error on install, don’t panic. It should still work.
Compile and migrate the smart contracts.
truffle compile
truffle migrate
# You can combine into one command
truffle migrate --reset ---compile-all
Start the application
npm run start
Navigate to http://localhost:3000/ in your browser.
Remember to connect MetaMask to one of your local Ganache Ethereum accounts
http://127.0.0.1:8545
), thenImport a new account and use the account seed phrase provided by Ganache
Open a terminal and run truffle test
or npm run sol:test
.
$ truffle test
Using network 'development'.
Contract: ImageRegister
✓ has an owner
✓ should selfdestruct (59ms)
✓ should store an image (75ms)
✓ should emit a LogImageUploaded event when storing an image (83ms)
✓ should return image details (103ms)
✓ should return image count (139ms)
✓ should store images for any number of owners (255ms)
✓ should require a valid IPFS hash when uploading an image (42ms)
✓ should require a valid title when uploading an image (44ms)
✓ should require a valid description when uploading an image (76ms)
✓ should require tags when uploading an image (42ms)
✓ should require a valid address when retrieving image count
✓ should require a valid index when retrieving image details
✓ should allow the owner to perform an emergency stop
✓ should disallow a non-owner to perform an emergency stop
✓ should disallow uploading an image when there is an emergency stop (43ms)
✓ should emit a LogEmergencyStop event when performing an emergency stop
17 passing (2s)
Open a terminal and run npm run coverage:solidity
.
$ npm run sol:coverage
> ipfs-image-app@0.1.0 coverage:solidity /Users/irvin/dev/ipfs-image-dapp
> solidity-coverage
Generating coverage environment
Running: truffle compile
(this can take a few seconds)...
Compiling ./contracts/ImageRegister.sol...
Compiling ./contracts/Migrations.sol...
Compiling openzeppelin-solidity/contracts/lifecycle/Destructible.sol...
Compiling openzeppelin-solidity/contracts/ownership/Ownable.sol...
Writing artifacts to ./build/contracts
Instrumenting ./coverageEnv/contracts/ImageRegister.sol
Skipping instrumentation of ./coverageEnv/contracts/Migrations.sol
Running: truffle compile
(this can take a few seconds)...
Compiling ./contracts/ImageRegister.sol...
Compiling ./contracts/Migrations.sol...
Compiling openzeppelin-solidity/contracts/lifecycle/Destructible.sol...
Compiling openzeppelin-solidity/contracts/ownership/Ownable.sol...
Writing artifacts to ./build/contracts
Launched testrpc on port 8555
Running: truffle test
(this can take a few seconds)...
Using network 'development'.
Contract: ImageRegister
✓ has an owner
✓ should selfdestruct
✓ should store an image (134ms)
✓ should emit a LogImageUploaded event when storing an image (114ms)
✓ should return image details (162ms)
✓ should return image count (220ms)
✓ should store images for any number of owners (432ms)
✓ should require a valid IPFS hash when uploading an image (70ms)
✓ should require a valid title when uploading an image (72ms)
✓ should require a valid description when uploading an image (131ms)
✓ should require tags when uploading an image (88ms)
✓ should require a valid address when retrieving image count
✓ should require a valid index when retrieving image details (83ms)
✓ should allow the owner to perform an emergency stop
✓ should disallow a non-owner to perform an emergency stop
✓ should disallow uploading an image when there is an emergency stop (58ms)
✓ should emit a LogEmergencyStop event when performing an emergency stop (40ms)
17 passing (3s)
--------------------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
--------------------|----------|----------|----------|----------|----------------|
contracts/ | 100 | 88.89 | 83.33 | 100 | |
ImageRegister.sol | 100 | 88.89 | 83.33 | 100 | |
--------------------|----------|----------|----------|----------|----------------|
All files | 100 | 88.89 | 83.33 | 100 | |
--------------------|----------|----------|----------|----------|----------------|
Istanbul coverage reports generated
Cleaning up...
Shutting down testrpc-sc (pid 94037)
Done.
Steps to deploy our smart contract directly from Truffle with Infura to the Rinkeby TestNet.
cd ipfs-image-dapp
touch .env
MNENOMIC = '<Your MetaMask recovery words>'
INFURA_API_KEY = '<Your Infura API Key after its registration>'
Deploy to Rinkeby with truffle migrate --reset --compile-all --network rinkeby
$ truffle migrate --reset --compile-all --network rinkeby
Compiling ./contracts/ImageRegister.sol...
Compiling ./contracts/Migrations.sol...
Compiling openzeppelin-solidity/contracts/lifecycle/Destructible.sol...
Compiling openzeppelin-solidity/contracts/ownership/Ownable.sol...
Writing artifacts to ./build/contracts
Using network 'rinkeby'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xb2d3cebfca0c1a2e0d271c07740112460d82ce4469ba14d7b92f9993314af50c
Migrations: 0x4ed3265ed135a4c85669f32ca662bd2aba3e5db3
Saving successful migration to network...
... 0xde1d86d1efbeae9d086e0d1d170a20bbe1f570e92816d231265874f2a8afe556
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ImageRegister...
... 0xcfbe99781c8c0cd77dd208eb445b2c12381704441e3827b2308a88d9c9b29079
ImageRegister: 0x107aaa697293b44376de69ad4b87579e3b1e50d8
Saving successful migration to network...
... 0x46ad7dbe55f412a55c76e48bf7553603c0826a19cda92f45f319699b8eb5a203
Saving artifacts...
Run the application as described above.
Check out the awesome tutorial Deploy Your Smart Contract Directly from Truffle with Infura by Hyungsuk Kang.
truffle compile
and truffle migrate
whenever starting your local network or making changes to your smart contract?truffle migrate
showing stale settings? truffle migrate --reset
This application is a marriage of Truffle and a React project created with create-react-app. Either one would be a great place to start.
You can also check out the official sites for the Ethereum Project, OpenZeppelin library and IPFS.
There seem to be a number of issues hosting a create-react-app
on IPFS. Take a look at the following links if you are interested in exploring this:
This project uses Bootstrap 4.
MIT