A supplychain smart contract written in Solidity and deployed on the Rinkeby test network. This Smart contract demonstrates how supplychains can improve authenticity, efficiency and privacy between seller and buyer.
The smart Contracts simulates the Parmigiano Reggiano supplychain.
Ethereum_SupplyChian--|
|--- Diagrams
| |
| |---Parmigiano_Reggiano_Activity_Diagram.png
| |
| |---Parmigiano_Reggiano_DataModel_Diagram.png
| |
| |---Parmigiano_Reggiano_Sequence_Diagram.png
| |
| |---Parmigiano_Reggiano_State_Diagram.png
|
|
|--- build/contracts (compiled contracts)
| |
| |---ConsumerRole.json
| |
| |---DistributorRole.json
| |
| |---FarmerRole.json
| |
| |---Mirgations.json
| |
| |---Ownable.json
| |
| |---RetailerRole.json
| |
| |---Roles.json
| |
| |---SupplyChain.json
|
|
|--- contracts
| |
| |---parmigianoaccesscontrol
| | |
| | |---ConsumerRole.sol
| | |
| | |---Distributor.sol
| | |
| | |---FarmerRole.sol
| | |
| | |---RetailerRole.sol
| | |
| | |---Roles.sol
| |
| |---parmigianobase
| | |
| | |---SupplyChain.sol
| |
| |---parmigianocore
| | |
| | |---Ownable.sol
| |
| |---Migrations.sol
|
|--- migrations
| |
| |---1_initial_migration.js
| |
| |---2_deploy_contracts.js
|
|--- js
| |
| |--app.js
| |
| |---truffle-contract.js
|
|
|--- test
| |
| |---TestSupplychain.js
|
|
|--- index.html
|
|
|--- package-lock.json
|
|
|--- package.json
|
|
|--- readme.md
|
|
|--- style.css
|
|
|--- truffle.js
|
Modifiers can be split in to three groups,
- Checking ownership and values payed
// Define a modifer that checks to see if msg.sender == owner of the contract
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
// Define a modifer that verifies the Caller
modifier verifyCaller (address _address) {
require(msg.sender == _address);
_;
}
// Define a modifier that checks if the paid amount is sufficient to cover the price
modifier paidEnough(uint _price) {
require(msg.value >= _price);
_;
}
// Define a modifier that checks the price and refunds the remaining balance
modifier checkValue(uint _upc, address payable addressToFund) { // ADDED address payable
uint _price = items[_upc].productPrice;
uint amountToReturn = msg.value - _price;
addressToFund.transfer(amountToReturn);
_;
}
- Check the item has passed the previous step of the supplychain.
// itemState : 0
modifier producedByFarmer(uint _upc) {
require(items[_upc].itemState == State.ProduceByFarmer);
_;
}
// State : 1
modifier forSaleByFarmer(uint _upc) {
require(items[_upc].itemState == State.ForSaleByFarmer);
_;
}
// State : 2
modifier purchasedByDistributor(uint _upc) {
require(items[_upc].itemState == State.PurchasedByDistributor);
_;
}
// State : 3
modifier shippedByFarmer(uint _upc) {
require(items[_upc].itemState == State.ShippedByFarmer);
_;
}
// State : 4
modifier receivedByDistributor(uint _upc) {
require(items[_upc].itemState == State.ReceivedByDistributor);
_;
}
// State : 5
modifier processByDistributor(uint _upc) {
require(items[_upc].itemState == State.ProcessedByDistributor);
_;
}
// State : 6
modifier packagedByDistributor(uint _upc) {
require(items[_upc].itemState == State.PackageByDistributor);
_;
}
// State : 7
modifier forSaleByDistributor(uint _upc) {
require(items[_upc].itemState == State.ForSaleByDistributor);
_;
}
// State : 8
modifier shippedByDistributor(uint _upc) {
require(items[_upc].itemState == State.ShippedByDistributor);
_;
}
// State : 9
modifier purchasedByRetailer(uint _upc) {
require(items[_upc].itemState == State.PurchasedByRetailer);
_;
}
// State : 10
modifier receivedByRetailer(uint _upc) {
require(items[_upc].itemState == State.ReceivedByRetailer);
_;
}
// State : 11
modifier forSaleByRetailer(uint _upc) {
require(items[_upc].itemState == State.ForSaleByRetailer);
_;
}
// State : 12
modifier purchasedByConsumer(uint _upc) {
require(items[_upc].itemState == State.PurchasedByConsumer);
_;
}
- Role based modifiers inherited from other contracts.
Note: Used to implement Access Control
// FarmerRole.sol
modifier onlyFarmer() {
require(isFarmer(msg.sender));
_;
}
// DistributorRole.sol
modifier onlyDistributor() {
require(isDistributor(msg.sender));
_;
}
// RetailerRole.sol
modifier onlyRetailer() {
require(isRetailer(msg.sender));
_;
}
// ConsumerRole.sol
modifier onlyConsumer() {
require(isConsumer(msg.sender));
_;
}
Each supplychain function emits its own event.
event ProduceByFarmer(uint upc); //1
event ForSaleByFarmer(uint upc); //2
event PurchasedByDistributor(uint upc); //3
event ShippedByFarmer(uint upc); //4
event ReceivedByDistributor(uint upc); //5
event ProcessedByDistributor(uint upc); //6
event PackagedByDistributor(uint upc); //7
event ForSaleByDistributor(uint upc); //8
event PurchasedByRetailer(uint upc); //9
event ShippedByDistributor(uint upc); //10
event ReceivedByRetailer(uint upc); //11
event ForSaleByRetailer(uint upc); //12
event PurchasedByConsumer(uint upc); //13
Access control is implemented by contracts found within the "parmigianoaccesscontrol" directory that are inherited by the supplychain. Consisting of four contracts for each actor of the supplychain (Farmer,Distributor,Retailer and Consumer). Each contract contains a function that allows an address to be added to that role and is only permitted by the contract owner. Contract modifiers are used to enforce access controls within the supplychain.
16 functions make up the Parmigiano Reggiano supplychain, including the validation functions.
SupplyChain Function Modifiers and Events
SupplyChain Functions | Modifiers | Event |
---|---|---|
produceItemByFarmer() | OnlyFarmer() | ProduceByFarmer(_upc) |
SellItemByFarmer() | OnlyFarmer() producedByFarmer() verifyCaller(items[_upc].ownerID) |
ForSaleByFarmer(_upc) |
purchaseItemByDistributor() | onlyDistributor() forSaleByFarmer(_upc) paidEnough(items[_upc].productPrice) checkValue(_upc, msg.sender) |
PurchasedByDistributor(_upc) |
shippedItemByFarmer() | onlyFarmer() purchasedByDistributor(_upc) verifyCaller(items[_upc].originFarmerID) |
ShippedByFarmer(_upc) |
receivedItemByDistributor() | onlyDistributor() shippedByFarmer(_upc) verifyCaller(items[_upc].ownerID) |
ReceivedByDistributor(_upc) |
processedItemByDistributor() | onlyDistributor() receivedByDistributor(_upc) verifyCaller(items[_upc].ownerID) |
ProcessedByDistributor(_upc) |
packageItemByDistributor() | onlyDistributor() processByDistributor(_upc) verifyCaller(items[_upc].ownerID) |
PackagedByDistributor(_upc) |
sellItemByDistributor() | onlyDistributor() packagedByDistributor(_upc) verifyCaller(items[_upc].ownerID) |
ForSaleByDistributor(upc) |
purchaseItemByRetailer | onlyRetailer() forSaleByDistributor(_upc) paidEnough(items[_upc].productPrice) checkValue(_upc, msg.sender) |
PurchasedByRetailer(_upc) |
shippedItemByDistributor | onlyDistributor() purchasedByRetailer(_upc) verifyCaller(items[_upc].distributorID) |
ShippedByDistributor(_upc) |
receivedItemByRetailer() | onlyRetailer() shippedByDistributor(_upc) verifyCaller(items[_upc].ownerID) |
ReceivedByRetailer(_upc) |
sellItemByRetailer() | onlyRetailer() receivedByRetailer(_upc) verifyCaller(items[_upc].ownerID) |
ForSaleByRetailer(_upc) |
purchaseItemByConsumer() | onlyConsumer() forSaleByRetailer(_upc) paidEnough(items[_upc].productPrice) checkValue(_upc, msg.sender) |
PurchasedByConsumer(_upc) |
fetchItemBufferOne() | Any | None |
fetchItemBufferTwo() | Any | None |
fetchItemHistory() | Any | None |
The consumer can check authenticity by calling out fetchItemBufferOne() with the upc as input, this will return essential consumer information.
Additional information can be received by calling out fetchItemBufferTwo this will return information essential to the supplychain.
ItemHistory returns three block numbers of where ownership of the item changed.
Used to test the all 16 supplychain functions, all test pass the requirements.
I do find my UI a little difficult to use, but settled on the third revision.
Note must be sent from the contract owner
Compiler: solc: 0.5.0+commit.1d4f565a.Emscripten.clang
Truffle: v5.0.14
Node: v11.3.0
Contract Address: https://rinkeby.etherscan.io/address/0xfd5f80e2a7cd15b011c7f1ce7e74a89e2c97fbd8
Contract Creator: https://rinkeby.etherscan.io/address/0x49d15e7c94b1ae3c273e29bd8faf863157b2cf92
Tx Hash of contract creation :https://rinkeby.etherscan.io/tx/0xec2a4f9210ff68ff6b3227575ce9719a02198e0d64612263263b005f94b8f3a3
-
cd into project repro
cd Ethereum_SupplyChain
-
download node libraries
npm install
-
Download/Start ganache
https://truffleframework.com/ganache
-
Compiling contracts
truffle compile
-
Migrating to ganache
Note depending on ganache cli/ui you my need to change truffle.js port settings Current listing on port : 7545
truffle migrate --network development --reset --all
-
Testing on ganache
truffle test
-
Start FrontEnd DApp on ganache
npm run dev
-
Migrating to Rinkeby
Note Change truffle settings to your Contract Creator address within the "from" rinkeby configuration
truffle migrate --network rinkeby --reset --all
-
Start FrontEnd DApp on Rinkeby
npm run dev
Understanding Parmigiano Reggiano supplychain https://www.academia.edu/2722756/The_supply_chain_for_Parmigiano-Reggiano_cheese_in_the_United_States
Creating diagrams https://www.draw.io/