I’ve been working on generic immutability solutions for a while. However, a few months ago, I decided to "forget" everything I learned previously and focus on generating very specific products that solve real and daily problems.
For that purpose, I decided to review existing technologies and arm myself with a new toolbox to build solutions. After researching and testing different technologies I stumbled upon Lisk and found its possibilities and community fascinating. This is a short story of how I built my first Lisk PoC, and how I’m going to turn it into a real application for a company that wants to transform the way healthy food is marketed.
Consumers are progressively adopting healthier habits, which include nutrient-rich food, with a critical eye on sustainability. To satisfy this growing demand, new products and services are emerging, ranging from locally produced organic food stores to personalized diet services based on physique and daily activity.
One of the biggest problems facing the entire supply chain (not just the consumer), is the lack of transparency. From the moment a grain is sown in the ground until the time it's consumed, it goes through a number of steps (harvest, collection, transformation, packaging, delivery), that are mostly opaque to both the consumer, as well as each intermediary in the chain.
Each actor performs internal control on their domain, however this information is usually not shared. This type of fractional and dispersed information does not allow traceability to be generated in a simple way, and also complicates the participation of new actors in the production process.
More importantly, the information that reaches the consumer is generally only the one that the last link in the chain deemed to be important, which is reflected on the information printed on the rear of the packaging.
Although we as technologists assume that all actors in our society have access to unimaginable amounts of technology and tools, reality has shown me that the healthy food sector, and especially small producers (at least in our region), have few alternatives to reach out to their customers with a more detailed narrative regarding how their products are sourced, processed, and brought to market; and specifically how they differ from other producers.
I understood that the solution was to find an anchor point and try to build from there. Luckily it transpired that a certain store had the same idea, promoting all of this information themselves, and guaranteeing to their customers that the producers they select are working in a correct and transparent manner. For this they decided to become "oracles" of processes and began to share this information in an open network between the producers and the end customers.
Fit is an e-commerce project for a healthy food store (FitMarket Argentina), aiming to offer more transparency to its consumers regarding food, sustainability, and processes, and a new information channel to its suppliers using a network and a mobile app.
We started by defining certain types of "tasks" (which are going to coincide in a certain way with the transactions), that Fit was going to administer, and these tasks can be seen listed below:
It is quite evident that many of the issues described appear to be quite obvious and simple. Nevertheless, the fact remains that a store that is trying to generate a chain of transparency, in order to differentiate itself from other stores, whilst simultaneously collaborating with producers, and evangelizing the benefits of technologies is quite fascinating.
The Fit Market proof of concept consists of the following seven custom transactions:
1. Register Market
The register market custom transaction lets Fit Market register a new distribution center, entering its name and location, which will later be displayed on a map to trace a product's journey from field to the customer:
const market = store.account.get(this.asset.marketId);
if (!market.asset.name) {
const updatedMarketAccount = {
...market,
...{
asset: {
name: this.asset.name,
latitude: this.asset.latitude,
longitude: this.asset.longitude,
type: "Market"
}
}
};
store.account.set(market.address, updatedMarketAccount);
}
2. Register Producer
The register producer custom transaction lets Fit Market register a new approved healthy food provider, entering its name and location. It will later be possible to add the producer's history, thus creating a direct communication channel between producers and customers.
const producer = store.account.get(this.asset.producerId);
if (!producer.asset.name) {
const updatedProducerAccount = {
...producer,
...{
asset: {
name: this.asset.name,
latitude: this.asset.latitude,
longitude: this.asset.longitude,
type: "Producer"
}
}
};
store.account.set(producer.address, updatedProducerAccount);
}
3. Register Product
This allows a producer to register a new product batch. The required barcode and batch ID will enable clients to search a product's information by scanning them with Fit Market's app.
const product = store.account.get(this.asset.productId);
if (!product.asset.name) {
const updatedProductAccount = {
...product,
...{
asset: {
barcode: this.asset.barcode,
batch: this.asset.batch,
name: this.asset.name,
produced_quantity: this.asset.produced_quantity,
remaining_quantity: this.asset.produced_quantity,
produced_date: this.asset.produced_date,
due_date: this.asset.due_date,
type: "Product"
}
}
};
store.account.set(product.address, updatedProductAccount);
}
4. Register a Pallet
Products are usually shipped to Fit Market distribution centers in bulk. This custom transaction will enable producers to prepare a batch and have it ready for shipment.
const pallet = store.account.get(this.asset.palletId);
if (!pallet.asset.status) {
/*
* Update the sender account:
* - Deduct the postage from senders' account balance
*/
const sender = store.account.get(this.senderId);
const senderBalancePostageDeducted = new utils.BigNum(sender.balance).sub(
new utils.BigNum(this.asset.postage));
const updatedSender = {
...sender,
balance: senderBalancePostageDeducted.toString(),
};
store.account.set(sender.address, updatedSender);
/*
* Update the product account:
* - Deduct product_quantity from product's remaining_quantity
*/
const product = store.account.get(this.asset.productId);
const productRemainingQuantity = product.asset.remaining_quantity - this.asset.product_quantity;
product.asset.remaining_quantity = productRemainingQuantity;
store.account.set(product.address, product);
/*
* Update the pallet account:
* - Add the postage to the pallet account balance
* - Add all important data about the pallet inside the asset field:
* - recipient: ID of the pallet recipient
* - sender: ID of the pallet sender
* - carrier: ID of the pallet carrier
* - security: Number of tokens the carrier needs to lock during the transport of the pallet
* - postage: Number of tokens the sender needs to pay for transportation of the pallet
* - status: Status of the transport (pending|ongoing|success|fail)
* - product: ID of the product
* - product_quantity: amount of products carried on this pallet
*/
const palletBalanceWithPostage = new utils.BigNum(pallet.balance).add(
new utils.BigNum(this.asset.postage));
const updatedPalletAccount = {
...pallet,
...{
balance: palletBalanceWithPostage.toString(),
asset: {
recipient: this.asset.recipientId,
sender: this.senderId,
security: this.asset.security,
postage: this.asset.postage,
status: 'pending',
carrier: null,
product: this.asset.productId,
product_quantity: this.asset.product_quantity
}
}
};
store.account.set(pallet.address, updatedPalletAccount);
}
5. Start Transport
Similar to Lisk's supply chain example, the start transport transaction is launched by a carrier, who will transport the pallet from the producer to Fit Market. It will lock the security fee from the carrier's balance while the shipment is ongoing.
const pallet = store.account.get(this.asset.palletId);
if (pallet.asset.status === "pending"){
const carrier = store.account.get(this.senderId);
const carrierBalance = new utils.BigNum(carrier.balance);
const palletSecurity = new utils.BigNum(pallet.asset.security);
if (carrierBalance.gte(palletSecurity)) {
/*
* Update the Carrier account:
* - Lock security inside the account
* - Remove the security form balance
*/
const carrierBalanceWithoutSecurity = carrierBalance.sub(palletSecurity);
const updatedCarrier = {
...carrier,
...{
balance: carrierBalanceWithoutSecurity.toString(),
asset: {
lockedSecurity: pallet.asset.security,
}
}
};
store.account.set(carrier.address, updatedCarrier);
/*
* Update the Packet account:
* - Set status to "ongoing"
* - set carrier to ID of the carrier
*/
pallet.asset.status = "ongoing";
pallet.asset.carrier = carrier.address;
store.account.set(pallet.address, pallet);
}
}
6. Finish Transport
Similar to Lisk's supply chain example, the finish transport transaction lets Fit Market announce the success or failure of the shipment after the delivered product's inspection.
let pallet = store.account.get(this.asset.palletId);
let carrier = store.account.get(pallet.asset.carrier);
let sender = store.account.get(pallet.asset.sender);
let recipient = store.account.get(this.senderId);
// if the transaction has been signed by the pallet recipient
if (recipient.address === pallet.asset.recipient) {
// if the pallet status isn't "ongoing"
if (pallet.asset.status !== "ongoing") {
errors.push(
new TransactionError(
'FinishTransport can only be triggered, if pallet status is "ongoing"',
this.id,
'ongoing',
this.asset.status
)
);
return errors;
}
// if the transport was a success
if ( this.asset.status === "success") {
/*
* Update the Carrier account:
* - Unlock security
* - Add postage & security to balance
*/
const carrierBalanceWithSecurityAndPostage = new utils.BigNum(carrier.balance).add(
new utils.BigNum(pallet.asset.security)).add(new utils.BigNum(pallet.asset.postage));
carrier.balance = carrierBalanceWithSecurityAndPostage.toString();
carrier.asset.lockedSecurity = null;
store.account.set(carrier.address, carrier);
/*
* Update the Packet account:
* - Remove postage from balance
* - Change status to "success"
*/
pallet.balance = '0';
pallet.asset.status = 'success';
store.account.set(pallet.address, pallet);
return errors;
}
// if the transport failed
/*
* Update the Sender account:
* - Add postage and security to balance
*/
const senderBalanceWithSecurityAndPostage = new utils.BigNum(sender.balance).add(new utils.BigNum(pallet.asset.security)).add(new utils.BigNum(pallet.asset.postage));
sender.balance = senderBalanceWithSecurityAndPostage.toString();
store.account.set(sender.address, sender);
/*
* Update the Carrier account:
* - Set lockedSecurity to 0
*/
carrier.asset.lockedSecurity = null;
store.account.set(carrier.address, carrier);
/*
* Update the Packet account:
* - set status to "fail"
* - Remove postage from balance
*/
pallet.balance = '0';
pallet.asset.status = 'fail';
store.account.set(pallet.address, pallet);
return errors;
}
7. Update Product Info
Small local producers usually don't have the required skills or tools to give additional information about their products directly to their customers. This transaction allows Fit Market to fill in the gaps.
This information helps Fit Market's clients have actionable information about their products, especially for those who have specific dietary needs.
const product = store.account.get(this.asset.productId);
if (!product.asset.fitinfo) {
/*
* Update the product account:
* - organic: true | false | unknown
* - noTACC: true | false | unknown
* - transFat: true | false | unknown
* - daily_units: MAX daily units according to ingredients
*/
product.asset.fitinfo = {};
product.asset.fitinfo.organic = this.asset.organic;
product.asset.fitinfo.noTACC = this.asset.noTACC;
product.asset.fitinfo.transFat = this.asset.transFat;
product.asset.fitinfo.daily_units = this.asset.daily_units;
product.asset.fitinfo.market = this.senderId;
store.account.set(product.address, product);
}
From the beginning I wanted to separate the project from the PoC and the front-end from the back-end. It has really been quite complex to establish this, especially when the PoC is mutating to a "real project". However, I am sure that one of the keys to overall user adoption is related to usability. That's why I'm still working on the final interfaces, but a reference to them can be seen below:
GitHub: https://github.com/wozalabs/fit_market
DEMO: http://169.57.173.116:3000/
My conclusions consist of the following observations, one regarding the technical side and about Lisk in particular, and a general one about food traceability.
From a technical viewpoint, the possibilities that Lisk offers and especially its community and its constant desire to be a useful tool to develop solutions do not cease to impress me. I think great things can only be expected from this technology together with the team behind it, and I will continue to become more and more involved in it.
Regarding the case of food traceability, on which I will surely continue working, I can only state that there is a distinct lack of knowledge by the general public, and also in some cases professionals and technologists, regarding new technologies, and also how to take advantage of them to change the processes of production, consumption, information and therefore our daily lives. Beyond the great statements that we continually read and consult, the penetration that this originates in other sectors is minimal and I believe we have a great possibility of working to improve in the future.
Luckily this project continues on and is growing larger and larger having many participants wanting to be part of it. Furthermore, Fitmarket will open its first branch in Switzerland in 2 weeks.
Regarding the technical issues, I intend to add information on sustainability by crossing not only information provided by the relevant actors, but also its crossing with geospatial information. In addition, I do not rule out the use of remote sensors to automate part of the processes. I will need to evangelize about technologies, improve the frontend, and begin to integrate chains of different types of information on the same source. I think I have an interesting challenge ahead of me, although fortunately I have found technologies and tools that provide me with viable alternatives as well as a very active community.
Disclaimer: This blog post was written by our community member, Sebastian Priolo as part of his participation in the Lisk Builders program.