Our journey into smart contracts – Trixta – Medium

0 22


To store the addresses and the phase that the contract is in we will use a mapping with an enum for the phase.

enum OrderPhase {
Buying,
Production,
Delivery,
Warehouse1,
Shipping,
Warehouse2,
QA,
Inspection,
Complete
}
mapping(uint => address payable) private providerForPhase;

The providerForPhase will store the phase with the corresponding address that made the call. To store the address for the buyer would look like this
providerForPhase[uint(OrderPhase.Buying)] = msg.sender

To return our address for the buyer would look like this
address buyerAddress = providerForPhase[uint(OrderPhase.Buying)];

For the order details, we will use a struct

struct Order {
uint quantity;
string name;
}
Order public order;

To keep track of the various costs to each participant, we will use a struct

struct ContractCosts {
uint purchaseOrder;
uint qualityRecord;
uint inspectionRecord;
uint delivery;
uint warehouse1;
uint warehouse2;
uint shipping;
uint developer;
uint producer;
uint inspector;
uint serviceCosts;
}

When you create a smart contract without passing an address, a new one will be deployed to the Ethereum chain, and there are costs involved as well to deploy a smart contract. However, what we want is that once a Quality Record Contract and Inspection Record Contract is deployed, for this Purchase Order Contract, we can call those same contracts again. To do this we need to store the address of the Quality Record Contract and Inspection Record Contract.

InspectionRecord private inspectionRecord;
QualityRecord private qualityRecord;

Meaning Quality Record Contract and Inspection Record Contract need to be deployed first and the addresses saved to the Purchase Order Contract.

qualityRecord = QualityRecord(_qualityRecordAddress);
inspectionRecord = InspectionRecord(_inspectionRecordAddress);

Now that we know what to store, we needed to come up with methods that can be called on the smart contract. We stubbed out each method and created todos for later implementation by the team.

Methods for contract:
In solidity, you can create your own modifiers, below is one modifier we created.

require is an error handling function in solidity to check a condition and if not met will revert the contract and output the message.

_; is to indicate that function code can continue

The modifier below is checking that the orderPhase passed in is the same as the order phase (providerForPhase) of the contract. We needed this modifier to keep a check that phases are in order when each of the relevant public methods on the contract is called.

modifier validOrderState(OrderPhase orderPhase){
require(providerForPhase[uint(orderPhase)] != address(0), “Order phase invalid”);
_;
}
function startProduction() public validOrderState(OrderPhase.Production) {
// TODO save msg.sender address as producer
// TODO update phase to OrderPhase.Production
}
function startDelivery() 
public validOrderState(OrderPhase.Delivery) {
// TODO save msg.sender address as delivery;
// TODO update phase to OrderPhase.Delivery
}
function warehouse1Received() public validOrderState(OrderPhase.Warehouse1) {
// TODO save msg.sender address for warehouse1
// TODO update phase to OrderPhase.Warehouse1
// TODO pay Delivery address in ProviderForPhase from contract balance
}
function qualityCheckDone(address payable qaAddress) public validOrderState(OrderPhase.QA) {
// TODO save qaAddress;
// TODO update phase to OrderPhase.QA
}
function startShipping(address payable shippingAddress) public validOrderState(OrderPhase.Shipping) {
// TODO save shipping address
// TODO update phase to OrderPhase.Shipping
// TODO pay Warehouse1 address in ProviderForPhase from contract balance
// TODO pay inspectionRecord contract from contract balance

}
function warehouse2Received() public validOrderState(OrderPhase.Warehouse2) {
// TODO save msg.sender address for warehouse2
// TODO update phase to OrderPhase.Warehouse2
}
function inspectionComplete(address inspectorAddress) public validOrderState(OrderPhase.Inspection) {
// TODO save inspectorAddress and phase
// TODO update phase to OrderPhase.Inspection
}

Quality Record Contract (B)

For this contract, we are going to need to store:

  • Purchase Order addresses, QA ( 4 ) attests quality ( B )

To store the purchase orders that have been tested for the quality we will use a map

mapping(address => bool) public purchaseOrderQualityRecords;

Methods for contract:

function qualityPassed(address purchaseOrderAddress) public {
    PurchaseOrder purchaseOrder = PurchaseOrder(purchaseOrderAddress);
//TODO update purchaseOrderQualityRecords map with purchaseOrderAddress
//TODO call purchaseOrder.qualityCheckDone method
purchaseOrder.qualityCheckDone(msg.sender);
//TODO save the msg.sender as the qaAttesterAddress
//TODO transfer funds from purchaseOrder to qaAttesterAddress
}

For this function, you will notice the modifier view , this is to indicate that the function does not alter any state and therefore just returns data and will not cost any Wei to call.

function hasPassedQualityCheck(address purchaseOrderAddress) public view returns (bool) {
// TODO return purchaseOrderQualityRecords mapping with purchaseOrderAddress
}

Inspection Record Contract (C)

For this contract, we are going to need to store:

  • Quality Record Contract address, Inspector ( 7 ) Attests to quality & quantity ( C ) from ( B )
  • Passed Inspections, keep track of purchase order addresses that have gone under inspection

To store the address of the Quality Record Contract, we will use the same approach as we did previously in the Purchase Order Contract.

QualityRecord qualityRecordContract;
qualityRecordContract = QualityRecord(_qualityRecordAddress);

For the passed inspections we will use a map containing a boolean with the address of the purchase order contract.

mapping(address => bool) passedInspections;

Methods for contract:

function hasInspectedPurchaseOrder(address purchaseOrderAddress) public view returns (bool){
// TODO return passedInspections mapping with purchaseOrderAddress
}
function passedInspection (address purchaseOrderAddress) public {
PurchaseOrder purchaseOrder = PurchaseOrder(purchaseOrderAddress);
//TODO use qualityRecordContract.hasPassedQualityCheck
//TODO if passed update passedInspections with purchaseOrderAddress
//TODO call purchaseOrder.inspectionComplete(msg.sender)
//TODO transfer funds from this contract to msg.sender(inspector address)
//TODO get shipper address from purchaseOrder and pay funds
}

Troubles along our journey

  • The scenario above introduced the use of more than one smart contract and interaction between them. This was something new that we had to investigate and took quite a bit of time to understand.
  • Testing smart contracts are a must but can be tricky if you don’t understand mocha or chai syntax.
  • Setting up your solidity compiler and environment for a windows user is quite a struggle.
  • We did not get a chance to fully test and understand the transferring of Wei from one smart contract to another.

Lessons learned from our journey

testing your smart contracts is a must — not only does it give an indication of how much Wei is required to run your smart contract but allows you to optimize the code doing more thorough tests. Knowing you have tested the functions of your smart contract and are executing their purpose correctly.

truffle doesn’t enable optimization by default — after reading the truffle config documentation we realized that truffle compile doesn’t enable contract optimization by default. Tweaking just a few settings saved us another ~20% gas in our tests.

how you store your state in smart contracts is important —Try to avoid arrays as it is more costly to save to an array than it is to a mapping . Remember the SSTORE operation is the most expensive operation in smart contracts.

Conclusion

Thank you for reading my first article, I hope this gave you some clarity on smart contract development. It’s not that intimidating to write on Ethereum and test them thanks to Truffle. What is intimidating, is to create a UI to interact with Ethereum.

It is a new way of thinking, to code smart contracts regarding the use of maps , interacting with other smart contracts and that everything costs Wei. Being aware that all your public functions are exposed, which could lead to hacks or abuse of your smart contract if you’re not writing strict checks and rules. Such as ourvalidOrderState modifier we created earlier. Other aspects of smart contracts to think about is, firstly, how to make a profit from your smart contract, and, secondly, how to benefit the users of your smart contract. If there is no benefit from using your smart contract, then why would users want to use them.

How do you get feedback from your smart contracts? Where did they go wrong? Why did ‘x’ not happen? Where did the Wei go that I paid? These questions are somewhat of a mystery, and how Trixta can assist. By making it easy to know how much Wei your smart contracts are using, errors that occurred, statistics on the data from your smart contract, you gain invaluable information from Trixta that is currently not easy to find out or measure. If you found this useful, give it a clap! Good luck building.

You might also like

Pin It on Pinterest

Share This

Share this post with your friends!

WhatsApp chat