29 Dec A Symbol-Based Payment and Inventory Management System
Some people have been asking about the XYMPOSIUM event and whilst I can’t comment on other talks I thought that I should probably write mine up for the blog. I wanted to talk about developing a simple application on the Symbol blockchain and after a discussion with Hatchet she thought that perhaps prototyping her idea for a digital menu would be a good idea. You can read the original tweet thread below:
In this post, I will take you through the concept and how such a system might work. Keep in mind that this is a rough (but functional) prototype and not a finished product built using the latest Symbol Javascript SDK. You can download my presentation slides at the end of this post.
The concept
The application would be aimed at small businesses that need a simple system for tracking stock and accepting XYM payments without having the hassle of implementing a custom system to do this. The idea would be that scanning a barcode on the table of a bar or restaurant would take the user to a digital menu from which they could order and pay in XYM. When an order is accepted the number of items of each product would be subtracted from the total stock in the restaurant’s inventory thus giving them a way to track sales of each item and to manage their inventor, knowing when to order new stock.
How can we achieve this? Well, it is relatively easy to create such a system using Symbol mosaics, where one mosaic would represent one menu item and the supply of these items could be updated by the venue.
Background
I am sure that readers of the blog will already be familiar with Symbol’s features but I have included a quick refresher of some key features below.
Accounts
Accounts are associated with a Symbol address to which assets can be sent and received. Account restrictions can be set up to e.g. blacklist or whitelist certain accounts or mosaics transacting with a given address. Addresses can be linked to a namespace so that people can transact with a human-readable address rather than the long alphanumeric string that is a Symbol address. For example, the blog address NCAY26LEBPOXM7NPCNV4HL4EH5WM6UJ5UUN4UGA is aliased to the namespace symbolblog so if I want to send mosaics to Symbol Blog I can just send to the alias rather than having to remember the actual address.
Finally, and this is important for later sections, accounts can have metadata assigned to them. I will explain what metadata is later on.
Mosaics
We talk a lot about mosaics in Symbol and what we mean by the term is a token. Mosaics, or custom tokens, can be created with the click of a button in a Symbol wallet or a few lines of code in the Python or Javascript SDKs. One of the most powerful features of Symbol mosaics is that they have a comprehensive set of flags which control their behaviour and allow them to be used to achieve different goals.
0x0 NONE
No flags present.
0x1 SUPPLY_MUTABLE
Mosaic supports supply changes through a Mosaic Supply Change Transaction
even when mosaic creator only owns a partial supply.
If the mosaic creator owns the totality of the supply, it can be changed even if this flag is not set.
0x2 TRANSFERABLE
Mosaic supports Transfer Transaction between arbitrary accounts. When not set, this mosaic can only be transferred to or from the mosaic creator.
0x4 RESTRICTABLE
Mosaic supports custom restrictions configured by the mosaic creator.
0x8 REVOKABLE
Mosaic supports revocation of tokens by the mosaic creator.
In the menu app I will describe shortly the mosaics are set to be non-transferrable and have a mutable (changeable) supply. I will explain why later.
Metadata
Metadata is stored as a key-value pair (like a Python dictionary). The key is associated with the metadata which can be any arbitrary data up to a limit of 1Kb. Metadata can be written to accounts, mosaics and namespaces and provides a mechanism for describing e.g. what a mosaic is and what it does. Metadata can easily be retrieved from API nodes and we will use mosaic metadata to describe menu items. Each mosaic represents a different menu item and the metadata associated with it will tell the customer what it is and how much it costs.
Transactions
Another simple concept but in Symbol there are 25 different transaction types and three different methods for making a transaction. In the app described later, we use the following transaction types:
- Mosaic definition
- Mosaic supply change
- Mosaic metadata
- Transfer
- Account metadata
We also use both aggregate complete and aggregate bonded transactions. What are these? Well, hopefully the diagrams below will help demonstrate their functionality.
As you can see a simple single transaction requires a single signature. In the example above Alice sends funds with a message to Bob, she signs and the transaction is completed. In the second example, Alice sends funds to both Bob and Carol and these are wrapped within an aggregate complete transaction. Again this only requires one signature, from Alice and then the transaction will be completed.
The third example of aggregate bonded transactions is more complex and requires multiple signatures from different parties (up to a maximum of 25). In the example above Alice sends the aggregate bonded transaction sending 5 XYM to Bob and requesting that Bob sends one mosaic to her in return. Alice signs the transaction and it is submitted as a partial transaction which awaits Bob’s signature. If Bob signs then the transaction will be completed and Alice will receive the mosaic and Bob will receive the funds. If Bob does not agree and does not sign then the transaction will time out and will not be executed. Aggregate bonded transactions are all or nothing meaning that no state changes are made until all of the parties agree. This makes it safe to perform trades as the parties involved can be assured that they will not lose assets if one party does not sign. However, when signing aggregate bonded transactions, the user has to be careful that they are happy with the contract that they are signing, just as with real-life physical contracts.
Inventory management
First of all a venue has to create their menu. This is done by creating new mosaics and assigning metadata. It can be done manually using e.g. the Python SDK as below:
from symbolchain.facade.SymbolFacade import SymbolFacade
from symbolchain.CryptoTypes import PrivateKey
from symbolchain.symbol.IdGenerator import generate_mosaic_id
from binascii import unhexlify, hexlify
import http.client
import datetime
import random
import time
import requests
facade = SymbolFacade('testnet')
# Replace with your private key
venue_private_key = PrivateKey(unhexlify('*****'))
# Replace with node URL and connection port
node_url = 'mikun-testnet.tk'
node_port = 3000
# Set testnet network generation time and then add deadline
netStart = 1667250467
deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=2)).timestamp()) - netStart) * 1000
venue_keypair = SymbolFacade.KeyPair(venue_private_key)
venue_pubkey = venue_keypair.public_key
venue_address = facade.network.public_key_to_address(venue_pubkey)
embedded_txs=[]
menu_items = [
"Starsburg Classic Burger;Juicy beef patty with lettuce, tomato, onions, and our signature sauce, served on a toasted bun.;$9.99",
"Riverbed Fries;Golden crispy fries seasoned with our special blend of spices, served with a side of ketchup and aioli.;$4.99",
"American Dream Dog;Grilled all-beef hot dog topped with caramelized onions, mustard, and relish, nestled in a soft bun.;$6.99",
"Comfort Crunch Chicken;Tender chicken strips breaded and fried to perfection, accompanied by honey mustard dip.;$8.99",
"Heartland Veggie Burger;Plant-based patty loaded with fresh veggies and spices, topped with avocado and sprouts on a whole-grain bun.;$10.99",
"Starsburg Grilled Cheese;Melted American and cheddar cheese sandwiched between buttery toasted bread, served with a side of tomato soup.;$7.99",
"Fry Me Fish Tacos;Crispy battered fish fillets tucked into soft tortillas, garnished with coleslaw and tangy tartar sauce.;$9.99",
"Loaded Riverboat Nachos;Crispy tortilla chips layered with melted cheese, jalapeños, olives, and ground beef, drizzled with sour cream and salsa.;$8.49",
"Mississippi Milkshake;Creamy vanilla ice cream blended with chocolate syrup and topped with whipped cream and a cherry.;$5.99",
"Apple Pie à la Mode;Classic American apple pie served warm with a scoop of vanilla ice cream.;$6.49"
]
# Create 10 new mosaics and set the supply to a random number between 20 and 100
x = 0
for x in range(10):
nonce = random.randint(0,4294967295)
supply = random.randint(20,100)
mos_id = int(generate_mosaic_id(venue_address, nonce))
# Create a mosaic definition transaction
mosaic_def = facade.transaction_factory.create_embedded({
'type': 'mosaic_definition_transaction_v1',
'signer_public_key': venue_pubkey,
'duration': 0,
'flags': 'supply_mutable',
'nonce': nonce,
'divisibility': 0
})
embedded_txs.append(mosaic_def)
# Change mosaic supply
mosaic_supply = facade.transaction_factory.create_embedded({
'type': 'mosaic_supply_change_transaction_v1',
'signer_public_key': venue_pubkey,
'delta': supply,
'action': 0x1,
'mosaic_id': mos_id
})
embedded_txs.append(mosaic_supply)
metadata_value = bytes(1) + menu_items[x].encode('utf8')
# Create a metadata transaction
mosaic_metadata = facade.transaction_factory.create_embedded({
'type': 'mosaic_metadata_transaction_v1',
'signer_public_key': venue_pubkey,
'target_address': venue_address,
'scoped_metadata_key': x,
'target_mosaic_id': mos_id,
'value_size_delta': len(metadata_value),
'value': metadata_value
})
embedded_txs.append(mosaic_metadata)
x += 1
# Calculate the Merkle hash of the embedded_txs list
merkle_hash = facade.hash_embedded_transactions(embedded_txs)
# Construct the aggregate transaction
aggregate_transaction = facade.transaction_factory.create({
'type': 'aggregate_complete_transaction_v2',
'signer_public_key': venue_pubkey,
'deadline': deadline,
'transactions_hash': merkle_hash,
'transactions': embedded_txs
})
# Set the fee
aggregate_transaction.fee.value = aggregate_transaction.size * 150
# Sign the transaction
signature = facade.sign_transaction(venue_keypair, aggregate_transaction)
# Attach signature
tx = facade.transaction_factory.attach_signature(aggregate_transaction, signature)
print(tx)
# Calculate the hash of the aggregate transaction
tx_hash = facade.hash_transaction(aggregate_transaction)
# Announce the transaction lock to the network
headers = {'Content-type': 'application/json'}
conn = http.client.HTTPConnection(node_url, node_port)
conn.request("PUT", "/transactions", tx, headers)
response = conn.getresponse()
print(response.status, response.reason)
However, we need to make this as easy as possible for the user since not every restaurant owner can do this! For this reason, I have created an inventory management page where the menu can be created or edited.
In the example below we have already populated the inventory using the Python code above. The inventory management page will query all mosaics owned by the restaurant account and display them in the form below. The restaurant owner can then update any of the fields and the stock. For example, if they want to update the description or the price of the item they can do this and when the form is submitted and the transaction signed it will almost instantaneously be reflected in the digital menu and it is a lot cheaper, easier and less hassle than having to print an entire new menu!
As you can see there is the option to add a new item, which will create a new mosaic, set the supply and update the metadata and whilst you cannot delete a mosaic there is an option to remove an item from the menu. Removing an item just triggers a transfer transaction which sends the entire stock (mosaic supply) to a burner address. As we have a mutable supply flag set the restaurant can later reuse this mosaic ID for a different menu item even if they do not own the entire supply of tokens.
Once the save changes button has been pressed the new state of the inventory is compared with the original state and any changes are encoded into embedded transactions that are wrapped into an aggregate complete transaction which is signed by the restaurant. The application creates a custom URI containing the serialised transaction which is then recognised by Oleg’s Symbol mobile wallet and imported for the restaurant owner to sign. I would like to implement a QR code system but as these transactions are quite complex we are not sure if the amount of data would be an issue for QR displays.
An example transaction is shown below. Each of the updates is embedded into a single aggregate complete transaction.
A demo video is included below:
Ordering
So, now we have populated the menu we need a way for the customers to view it and to place orders. The concept here is simple. We want to include a QR code on each table in the restaurant which, once scanned takes them to the correct menu URL. Menus are generated by querying a Symbol address to find all mosaics that they have created and are contained within their account. Mosaic metadata is then obtained for each of the mosaics, and these data are parsed and displayed in a table. The user then selects how many units of each item they wish to order, and they calculate the total price in local currency and in XYM (the latest XYM price is fetched from an API).
Once the order is submitted the details are displayed and a payment URI is generated. Clicking on the URI opens the transaction in the mobile wallet.
The transaction is set up as an aggregate bonded transaction where the customer sends payment along with a message containing their table number and order details to the restaurant. The transaction also requests that the stock of each of the items ordered is modified thus updating the restaurant’s inventory. As the customer is requesting to perform a mosaic supply change transaction on the restaurant’s account the transaction requires both the customer’s and restaurant’s signature and therefore is sent as aggregate bonded.
A demo video showing the process is below:
Aggregating venues
So far we have concentrated on displaying a menu for a single restaurant but there are cases where we might want to aggregate menus and allow customers to browse a set of menus to order food from. One such use case would be online delivery, something similar to Uber Eats. Another might be, as I see more and more in the UK, going to a venue containing lots of pop-up restaurants and food trucks. Once you sit down at a table, scanning a barcode will display the list of restaurants and bars that you can order from and clicking on one of these will display the individual menu.
This type of system can easily be implemented by introducing the concept of an administrator or aggregator account.
Here individual restaurants would register with the administrator account and request that they write metadata about their venue to the administrator account through a simple registration page shown below:
Once the venue submits the form it is sent as an aggregate bonded transaction requesting that metadata be written to the admin address.
If signed by the admin account then the restaurant and its menu are added to the aggregated list automatically.
Analytics
As everything is blockchain-based it lends itself well to data analytics. For example tracking volume and value of sales over time
or looking at the breakdown of sales of different items to identify well and poorly-performing products.
It would also be possible to track sales of individual menu items over time.
Perhaps this could be used to show seasonal trends, e.g. ice cream sells well in the summer but pies sell better in the winter. Being able to track product performance and seasonal trends as well as the consequences of e.g. price increases provides a valuable resource for small businesses. The analytics portal is still a work in progress so I don’t have an example video for this at this point in time.
Loyalty
Another benefit of a blockchain-based system is that it would be easy to implement a loyalty points system, e.g. similar to Symbol Shop Points already used by venues in Japan. Customers could be rewarded based on the number of visits or the amount spent at a venue and these could be redeemed for discounts, free items or in payment for their order. It would be very easy to implement this type of functionality by introducing a second transfer transaction from the venue to the customer sending reward tokens e.g. based on their total spend from within the aggregate bonded transaction.
Benefits
This is a very simple prototype system but it serves as a demonstration of how Symbol can be used to facilitate payment and inventory management for shops and restaurants. With the addition of an analytics page it would allow businesses to track sales and identify potential opportunities to increase revenue and understand customer preferences and seasonal trends. Symbol has low transaction fees and low fees for mosaic creation. This allows businesses to implement the system in an inexpensive way, especially compared to printing and updating physical menus. The ability to send and pay for the order instantly is more efficient than having someone take customer orders and deal with processing payments manually.
The same system could be used for simple online retailers where stock is represented as mosaics and the webpage queries mosaic supply on chain showing the customer how many units are available and adjusting supply when new items are ordered. Again it would remove the need for a bespoke centralised system for storing data and would be available as an off the shelf solution for users.
Limitations
Whilst fully functional, the prototype system does require improvements before it could be used in a commercial setting. Perhaps most importantly, allowing the scanning of QR codes to import payments into any mobile wallet would need to be included so that there was no dependency on URI links. It would also make it easier for inventory management to be able to update the menu on a desktop machine and scan the transaction QR code and then sign on a desktop or mobile wallet.
The system is currently slow at one particular step, displaying the menu. This is because after obtaining the list of mosaic IDs to display, metadata must be fetched for each of the IDs individually. This can be time consuming and will also scale linearly with the number of items in the menu. Paralellising the fetching of, or perhaps caching these data would speed up menu display dramatically.
There is no option currently to add pictures to menu items, this would be relatively straightforward to implement, perhaps by adding an IPFS link to the mosaic metadata which is parsed and displayed to the user. Of course on-chain images could also be used but again may create additional overheads in terms of fetching data from nodes.
One of the strengths of the system is also a potential weakness. All transactions are public on the blockchain and so anyone can see what a restaurant has sold and identify their most popular items and income over time. Perhaps some business owners would not like this information to be public. Customer behaviour could also easily be tracked through their Symbol address allowing businesses to see who else they are transacting with and what they have bought. This may not be something that customers would be comfortable with but it is a fact that every time you transact on a public blockchain this information is recorded permanently for all to see.
Presentation
GitHub
GitHub: Symbol Menu App (please forgive me if you are actually a Javascript programmer – as you will see, I am not 😂).
I’m a Symbol and NEM enthusiast and run this blog to try to grow awareness of the platform in the English-speaking world. If you have any Symbol news you would like me to report on or you have an article that you would like to publish then please let me know!
No Comments