Building the Fit Project

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.

By Lisk

20 Oct 2020


Global Context

Consumers are progressively adopting healthier habits, which includes 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.

What is “Fit”?

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 in the healthy food sector, and specially 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 a 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:

1. Select the best possible producers / suppliers based upon how they treat the product, respect for the environment, etc. If we think about this from a technical point of view, they would register the producer. (Custom transaction: Register Producer)

2. For some time they would observe the way in which the producer develops his activity and they would generate stamps or reports (this is not modeled yet, as it has just started !!)

3. The producer sends the product to Fit (Register Product, where information that both consider important is loaded).

4. Fit receives the product, checks it, stores it, seals it. (Finish Transport & Update Product Info)

5. A customer acquires the product and Fit sends it, (Not in the demo but really similar to the Supply Chain example:

6. The customer receives the package (it is registered, this is open and gives information to the entire chain! Check it out:

7. The customer requires information about what was purchased or about a product of interest.

It is quite evident that many of the issues described t appear to be quite obvious and simple. Nevertheless, the fact remains that a store which 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.

So how does it work?

Implemented Custom Transactions

The Fit Market proof of concept consists of the following seven custom transactions:

  • Register Market
  • Register Producer
  • Register Product
  • Register Pallet
  • Start Transport
  • Finish Transport
  • Update Product Info

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 customer:

File name
1const market = store.account.get(this.asset.marketId); if (! { const updatedMarketAccount = {, ...{ 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.

File name
1const producer = store.account.get(this.asset.producerId); if (! { const updatedProducerAccount = { ...producer, ...{ 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.

File name
1const product = store.account.get(this.asset.productId); if (! { const updatedProductAccount = { ...product, ...{ asset: { barcode: this.asset.barcode, batch: this.asset.batch, 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.

File name
1const 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:, 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.

File name
1const 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(; 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:, } } }; 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 let's Fit Market announce the success or failure of the shipment after the delivered product's inspection.

File name
1let 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"',, '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( 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( 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 help's Fit Market's clients have actionable information about their products, specially for those who have specific dietary needs.

File name
1const 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.noTACC = this.asset.noTACC; product.asset.fitinfo.transFat = this.asset.transFat; product.asset.fitinfo.daily_units = this.asset.daily_units; = this.senderId; store.account.set(product.address, product); }

Components of the PoC

  • Back-end: Working custom transactions that can run on Lisk.
  • Front-end: a minimal ExpressJS front-end to register markets, producers, products & pallets, start and finish transports, and update key product information (interact with transactions).


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:



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, in 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.

Future developments

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.

If you feel inspired and want to build your own proof of concept blockchain application, check out the Lisk Builders program. More information about the program and the application procedure can be found on the Lisk webpage for theLisk Builders program.

Disclaimer: This blog post was written by our community member, Sebastian Priolo as part of his participation in the Lisk Builders program.