The ShopKeeper is a project to demonstrate learnings in the Consensys Academy Blockchain Developer bootcamp.
It serves to show, creation, compilation, testing and interface interaction with the Ethereum network via wallets such as MetaMask.
Technologies used include:
- Truffle for testing, compilation and migration
- Web3 and MetaMask integration
- Additional plugins for gas consumption and code coverage have been added.
The premise of the dApp is to allow people to add their own shop in order to sell products. Limitations at this point are that it is very text based with little interaction with visual elements such as images (security considerations).
Shop owners will be given a multi-sig wallet (ManagedOwnerLite) there was a more complex version which if required I can show - it was to give function level access vs. entire contract/state access
that will allow them to add/remove owners, withdraw funds, allocate funds to owners sent to the contract via a fallback or direct receive as well as activate or deactivate the shop while they add products/complete logistics etc.
There is no integration with emailing or delivery etc. at present.
The owner of the site (ShopKeeper admin
) will be able to activate/deactivate the site. At present each "Shop" is
owned and controlled in its own contract instance. The ShopFactory of ShopKeeper creates that instance. The idea is
that should a Shop owner wish to host/move their contract elsewhere for other integration they should be able to.
Currently however (due to my limited time), I haven't made the shop UI available independently.
Some important things to note. While the site/shop itself has some text input validation, if you find some text
is showing up as "* * *" ( or not at all ) it is due to invalid unsupported characters being validated by the contracts
to prevent site viewers indirectly being attacked with a Drive by Injection Attack. A write up and sample solution (logic is also implemented in this project) can be seen at my other repository. Dom XSS Attack via smart contract
Anyone can register a shop and manage their products.
An example walkthrough without the XSS Attack prevention can be seen at Google drive walkthrough Or via Youtube
Another walkthrough video is available at: Walkthrough video 1 - that shows the functionality (MetaMask wasn't showing)
There are two things to note:
- The video in video 1 hides MetaMask from view
- There is a point that funds (2.5 ETH) are send directly to the main site contract - similar functionality applies to the Shops themselves
- Video 2 does not include the security showcase
The folders are as follows:
- build - generally not committed but when compile is run, is the json contract code (ABI etc.)
- configs - this is where the site configuration is for the
lite-server
web server hosting the html and javascript - coverage - not committed, but when
truffle run coverage
is run outputs the reports there - contracts - all the soldity contracts
- docs - additional readme documents ( Design pattern decisions , Avoiding common attacks and Deployed addresses )
- gasCosts - In there, there is a gasReporterOutput.html file that is generated when running tests - this will be used in future learnings to optimise gas usage
- migrations - Truffle instructions on how to deploy the contracts
- node_modules - not committed - created when
npm install
is run downloading components - src - all the source code for the UI
- css - styling for the site
- js - all the javascript libraries (some not in modules)
- index.html file for the main page
- test - all the javascript - mocha/chai tests for the contracts
- Library - an example safe text library (coded manually in the contracts for ease of use and cost saving)
- OwnerManagedLite - all the multi-sig tests for ownership, activation and financial actions. There are already existing solutions for this, but took it as a learning exercise to write one.
- Shop - all the tests for the shop creation, product management and purchasing
- Note: the
OwnerManagedLite is inherited
by the ShopFactory as well as Shop to set ownership per contract instance
- Ubuntu Virtual machine (I used)
- Node JS is installed - (everything was tested under
version 12.18.3
) - Git ( it is assumed you know how to use Git and have relevant HTTPS/SSH capability to clone)
- A Browser with the MetaMask extension installed (and knowledge on how to use it)
- Test Ether on the Goerli network
- Open a terminal window
- Clone and pull down this repository into a branch via git -
git clone url.ToRepository
- Go to the directory you cloned into -
cd ConsensysAssignment
- In the terminal window type in the command
npm install
- This should install a fair amount of components
- Type in
npm audit fix
to fix known vulnerabilities in packages
- Type in the terminal windows
ganache-cli --version
- This should produce a response like
Ganache CLI v6.10.2 (ganache-core: 2.11.3)
- This should produce a response like
- In the
node_modules
folder there should be the following- @openzeppelin
- @truffle
- @trufflesuite
- web3 (and many others with
web3-
prefix) - eth-gas-reporter (for gas tweaking)
- solidity-coverage (for checking code coverage)
If some of these components are not installed you may need to run:
npm install -g @openzeppelin/contracts
npm install -g @openzeppelin/truffle-upgrades
npm install -g truffle
npm install -g @truffle/hdwallet-provider
npm install -g solidity-coverage
npm install -g ganache-cli
npm install -g web3
- In the terminal window in the root folder of the site, run
truffle compile --all
in order to have the ABI code available for the site - In the main folder of the project run the following command
npm run dev
- This should open a browser with the main site hosted on the
lite-server
package- Note: you should not have something else bound to port 8000
- The site is pre-configured to use the Goerli deployed address (
/src/js/app.js
) as the current network specified in MetaMask.- You may see an error message indicating an incorrect network - swap the network in MetaMask to Goerli
- Swapping networks should change the network and refresh the dApp to the correct network
- Note: if there is no injected web3 or
window.ethereum
it will fallback to the Goerli infura http address
- Open 2 terminal windows
- In the first one run the following command
ganache-cli -l 10000000
- this sets a similar block gas limit to the mainnet - In the second terminal run truffle
compile --all
and wait for compilation to complete - Run a command to make sure there is a gasCosts folder from the root folder -
mkdir gasCosts
- Also create a folder for coverage testing from the root folder -
mkdir coverage
- In the second terminal window run
truffle test --network development
to run against your ganache-cli instance in terminal 1 - You will note the gas used per test - this includes a new instance of each of the contracts plus the command call
- There are currently 128 tests that cover all aspects of the code including some particular edge cases and attacks. I prefer to test everything I possibly can.
- Open a terminal window
- Make sure a folder
coverage
exists from the root of the project folder - Make sure code coverage plugins are install by executing
npm install -g solidity-coverage
- In the terminal window run
truffle run coverage
- this will spin up an instance of the ganache-cli itself and run the tests - You will notice an output at the bottom of the terminal window when complete
- A more comprehensive view is visible in the coverage folder - navigate there and view the index.html file in a browser for a better view
- Open 2 terminal windows
- In the first one run the following command
ganache-cli -l 10000000
- this sets a similar block gas limit to the mainnet ( take note/copy of the mnemonic in the output window to import into MetaMask ) - In the second terminal run truffle
compile --all
and wait for compilation to complete - In the second terminal window run
truffle migrate --network development --reset
to deploy the contracts locally - Take note of of the Contract address for
ShopKeeper
in the output, copy it and replace theshopKeeperAddress
near the top of the/src/js/app.js
file - Comment line one and uncomment line 2 for the network fallback
- Similar to
Running the solution
section - in a terminal window runnpm run dev
- Lock MetaMask if already open
- Import account using mnemonic copied in point 2
- Create a custom network (if not already there) pointing to
http://127.0.0.1:8545
- Switch to this network
- This should change the network and refresh the dApp to the correct network
- Make sure to connect your first account to the dApp
- Create a second account in MetaMask (don't use a new seed phrase)
- Switch between accounts and note the change at the top of the page in the dApp (also connect this account)
- This will allow you to test non-owner scenarios such as purchasing
- Remember to give this account some ETH to work with
- See the demo video for some use cases
- Play around, have fun. My UI skills are average, so there may need to be an occasional refresh if running on the testnet
Thanks for reading - and remember - not your keys, not your crypto (credit: Andreas Antonopoulos)