19 September 2023
Blockchain engineering. The Obligate platform cut down bond issuance costs by 80%
As far as any financial initiatives are concerned, online security is the number one worry. It wasn’t any different for the Obligate platform – used by companies to issue on-chain bonds and commercial paper to receive funding from investors in a regulated DeFi environment. The goal was to design simple, fast, and intuitive blockchain transactions without compromising the platform’s safety. In this article, you’ll learn how blockchain engineering reduced Obligate’s bond issuance costs by 80% and cut down the issuance time from weeks to just a few hours!
Obligate came up with an idea for a fully regulated application that will support companies in issuing on-chain bonds and commercial paper to obtain funding from investors.
They had a defined tech stack, some blockchain know-how, and a 1.0 version of an app that was live for a year but neither the company nor the end-users were too happy about it. The company needed a partner to put it all together into a dream high-end tech solution.
Blockchain technology. Problems to solve
The main project goal was to build a new and extremely secure blockchain-based financial system.
1. Breaking down the microservices
The biggest goal for Obligate was to design an architecture that supports developing two main features of the app, predicting and prepping space for future modifications as the app converges to the final product:
- Investing and borrowing,
- Trading eNotes™.
The company had its fair share of technological hurdles and insisted on a microservices-based solution. The advantages are numerous – encapsulating functionalities into separate services makes it easier to scale, easier to work on features independently, and incentivizes writing well-organized code. Our concern was that in the early stages of development, it may slow us down due to a lot of boilerplate and a stiff backbone.
After careful consideration and conversations with potential customers, Obligate’s idea for the service evolved. The boundaries of the initial microservices often changed, and unfortunately, the development did not move as fast as expected.
After thorough deliberation, we settled on a modular monolith for the main part of the application and several microservices with very narrow and independent functionalities: queuing operations on the blockchain, sending notifications, and generating PDF documents. It allowed us to keep the main app free of a few bigger dependencies and well-organized internally but maintainable and flexible enough to allow us to move fast.
We kept the code modular, like an open door to separating parts that may run into scalability issues or need a dedicated team to maintain in the future.
In terms of the tech stack, we used NestJS for the main app and the modules feature to isolate the code. A few obvious and enduring microservices didn’t change much over subsequent iterations of the app – we decided they were worthy of keeping as separate containers, so we used Kubernetes and Garden to manage them:
- Kubernetes is an obvious choice for container orchestration. However, it does have disadvantages when it comes to development environments, CI/CD pipelines, and its general complexity.
- Garden provides a layer of abstraction and out-of-the-box solutions like code synchronization between the local environment and the cluster, managing namespaces and secrets, Docker images, commands for CI/CD environments, and more.
This move eliminated a lot of the overhead of working with microservices on a day-to-day basis. It made it easier for us to manage the microservices we’ve established so far.
I have already written an extensive tech study based on my experiences with the Obligate project:
2. Improving blockchain transactions
The most unusual problem we had to deal with was making blockchain transactions intuitive without compromising security.
What makes blockchain transactions something we trust even though there’s no governing body or any other authority that oversees them?
It’s called the consensus mechanism – most notably proof of work and proof of stake. Every transaction on the blockchain is validated by miners. First, they check that the transaction is legitimate and then either use their computational power to solve a mathematical puzzle or use an amount of cryptocurrency as collateral to make their validation count.
In short, for the miner to be able to validate a transaction it takes time and resources and that’s by design.
Any time the blockchain diverges into two chains, it is the one growing faster that is treated as the correct one since it took more resources to grow. The assumption is that the rest of the network will always outwork the fraudulent group or individual. If I wanted to manipulate a transaction, I could try to validate it myself and I might even succeed at first but for the rest of the miners, only the original transaction will be valid (the one that the rest of the network will be appending further transactions to). In order to keep up with the length of the genuine chain, I need so much more resources than the rest of the network that it wouldn’t be worth it anyway.
The problem with the decentralized nature of the blockchain is that sometimes a transaction can be validated by two miners at almost the same time. Since both chains that follow are valid, the race is much closer than an individual fighting the rest of the network (so it takes longer). After a while the consensus mechanism still works and one of the chains wins. But in the meantime, many valid transactions were attached to the chain that lost the race and are now orphaned. They need to be redone to be attached to the main chain. It takes time to ensure that the created transaction actually went through – not something that the users are used to in traditional apps.
Another issue is that before a transaction is validated (mined), it lands in the mempool (a kind of purgatory) waiting to be picked up by the miner for approval. In order to reward miners for doing all that work, a transaction comes with a cost called “a gas fee” that’s transferred to the miner that validates it. In order for the transaction to get picked up first, you should offer a gas fee that’s higher than other transactions in the mempool. But since it’s a variable depending on the blockchain traffic, occasionally your fee will be too small and the transaction will get stuck and that’s also a delay we’re not used to in traditional apps.
This is where the tradeoff comes from. Depending on the network, the time it takes to ensure the transaction went through all those stages and confirmations might take minutes. It’s not a guaranteed value either, it’s all based on observations, so different apps assume different rules of thumb, e.g. Circle informing how long they wait before moving on.
In our case, the web3 part was based on a smart contract – basically a simple app on the blockchain with a few defined behaviors, all executed as transactions. For example, when you invest in an eNote™ on the Obligate platform, an investment transaction will be made and the behavior for this transaction defined in the smart contract is to charge you the amount you invested and assign a token to your wallet.
After analyzing the competitive solutions, for the benefit of the users, we moved to the Polygon network, where the waiting time oscillates around 15 minutes.
In the end, it’s not about us, so we chose Polygon
We needed to address this issue to protect the solution from rejected transactions – every transaction opened further irreversible steps for end-users. At the same time, we didn’t want users to get frustrated by long waiting times.
3. eNotes™ and more blockchain issues
Another thing we needed to keep in mind and we sometimes caught ourselves forgetting about. Even though most things regarding the status of eNotes™™ and issuances were recorded in a database for quick information access and options to extend it with internally relevant details and relationships, the source of truth was still the blockchain.
Certain operations (e.g. sending eNotes™™ between one wallet and another) can be performed outside Obligate’s application and you wouldn’t have all the information related to those transfers unless it’s something that you could fetch from the wallet connected to your app.
Unfortunately, there’s no working around some blockchain limitations so in this case the functionality is partially limited and we made sure that we didn’t waste time and resources refining and designing features that would have worked only under the assumption that the database knows about everything.
Choosing the tech solution
As far as the transaction confirmation goes, initially, we only waited for a single confirmation that everything went through. Reorganization of the chains happens pretty often on the real chain (mainnet) but for development, a test chain (testnet) is used – costs of testing on the real chain add up very quickly. Unfortunately, testnet is not a perfect reflection of what happens on the mainnet because there are much fewer miners and so fewer opportunities for a transaction to be mined twice at the same time (also less incentive to commit fraud).
In addition, the expertise at Obligate was centered around Solana, so the problem came when we started testing the real chain before going into production…
At one point during tests, we just got stuck with this transaction on hold and couldn’t take the next step – it was in one of the states frontend couldn’t handle. At first, it looked like a tiny bug. It turned out that it required rewriting a large part of the platform and changing the way Obligate considers each transaction.
The initial idea was to reverse all user behavior that happened after the transaction was abandoned. However, it would be extremely difficult to implement and would likely cause chaos in the code and debugging process. What’s more, we anticipate that this solution would be confusing for end-users, so we quickly abandoned this variant.
Instead, we decided to save the state of the transaction (pending, confirmed, orphaned, etc.) in the database and periodically update this state in the background on the server. So we would not rely on the blockchain to protect all our transactions but also not at the cost of waiting for minutes or getting stuck on a non-responsive page for the users. It was great for developers too – we were able to block users from taking further steps based on the transaction information in the database.
Thankfully, for the purposes of scheduling the server-side transactions (like deploying smart contracts) we already had a few BullMQ queues implemented – we just used the existing setup and first added a few steps to track the transaction status and account for the outcomes other than a successful finalization.
Secondly, we created queues for the client-side transactions too and we made them go through a similar verification process. The idea was inspired by the event-based architecture approach that we were familiar with and it also reused a part of the existing setup, so we were comfortable doing this on a tight schedule and it worked well.
We also had to make some UI adjustments to make the process more obvious for users (one case had to do with disabled buttons).
How did we work on and then implement the solution?
Each transaction in the database got an additional “status” field. We implemented a queue that verifies the status of the application and takes the next steps depending on the result. Each transaction created by the user or the server is added to the database and the validation queue. If the next user step depends on the success of the previous transaction, the user receives an alert informing about the situation (we implemented it in the platform’s UI). When the transaction is approved and the next step is unlocked, the user receives an appropriate notification and the UI elements of the next steps get enabled.
Transactions are treated a little differently depending on whether the action is taken by the user or the server.
- User transaction – require confirmation/signature via the wallet application managing the user’s funds. (undertaken on the frontend), e.g. paying for an eNote™ or approving an issuance.
The diagram shows an example of repaying the eNote™. When a user interacts with the UI button to repay, the transaction is created and registered in the database as pending. From then on we put it into a sort of loop that checks the status using a polygon client. If we can’t find it, it means it got stuck in the mempool. If it failed for chain reasons, we noted the error and saved it in the database. After obtaining a satisfactory number of confirmations, we make changes in the eNotes™ status, which then unlock additional steps for the user.
- Server transaction – a bit easier since there’s full control over them but the flow is very similar, e.g. deploying a smart contract that supports a given issuance.
In this case, the transaction is made on our own behalf, since it’s Obligate that deploys the smart contract after all the investors are committed and the issuance date has been reached. The general confirmation loop is very similar to the previous case – except when the transaction failed or got stuck in the mempool, we can just automatically redo it.
A completely innovative custom solution
To be perfectly honest, everybody is extremely proud of this solution. TSH and Obligate teams partnered to work as one and spent a lot of time together to develop it as wisely as we could. You won’t find it anywhere else – lots of experience from relentless professionals combined with a trial and error method provided this custom solution for Obligate.
The most important problem we solved was making sure that none of the transactions get lost in the blockchain network and that we are able to handle all of the outcomes of this whole process. Another thing we cared about was making it all intuitive to the user who is not well-versed in blockchain and who is used to the advantages of the traditional applications, which was to minimize the waiting and the confusion to the minimum. Still, we would never compromise the main advantages of the blockchain to get there, so it was a balancing act and I think we stuck the landing. And finally, even though we were pushed for time, we kept the code nicely encapsulated and easily extensible, so it is ready for any new features that will come up as the application grows.
Project benefits for Obligate’s business
Cheap, cheaper, the cheapest
The project aimed to be a measurably cheaper and more convenient solution to traditional bonds. Normally, you have to go through a lot of paperwork with an investment bank and get your bonds approved by the appropriate regulatory body, and of course, there are lawyers, accountants, and rating agents involved in all of this – which is to say – it costs a lot. With Obligate, some of these intermediaries are reduced to zero. The legal framework of the system simplifies a lot of the complexity.
As explained before, blockchain guarantees the integrity of all transactions making them impervious. So much so that eNotes™ are recognized as ledger-based securities under Swiss law so the transactions are legally binding. If the agreement is breached in any way, a party can take it up with an arbitration panel in Switzerland.
There are separate components for accepting orders, probing the transaction status, reacting to transaction confirmation, and taking further steps. This is a simple situation for developers who work and will work on the solution in the future.
Improved user experience
Introducing a linear and intuitive process for the end-users! All complexities of blockchain are hidden behind transaction management. This way, users don’t deal with a complicated system or confusing pop-ups they don’t understand. Now, users follow clear paths without worrying about what’s going on with their transactions.
Even though the disputes are subject to arbitration in Switzerland, the decision is enforceable under the New York Convention on the Recognition and Enforceability of Arbitral Awards (recognized by more than 168 countries). Investors around the globe feel 100% safe using the Obligate platform.
The company combined legal and technological knowledge with financial expertise to build a new blockchain-based financial system.
Result? Obligate reduced the bond issuance costs by 80% and cut down the issuance time from weeks to a few hours!
Now, with the Obligate platform, companies can issue on-chain bonds and commercial paper to obtain funding from a diverse range of investors who, on the other hand, get access to regulated digital debt assets, secured with on-chain collateral.
We work in the collision of the very traditional three-piece-suite-and-a-tie world of Institutional Fixed Income Trading and the most agile and technologically advanced DeFi Space. The Software House helps us to keep pace and meet the requirements of both.
Each day we challenge the status quo and demonstrate that even the most established processes could be faster, cheaper, and more transparent; that smart contracts can substitute bank departments, and what used to take weeks can now be accomplished in a matter of minutes.
The expertise and operational approach of TSH gives us the most valuable property of a startup – scalability. We are certain that not only is our product capable of handling x10, x100, x1000 deals, but our tech team can also expand accordingly.
Remember Machiavelli’s skepticism about mercenaries? Well, TSH proves him wrong. They have become our most trusted partner, with every member of The Software House fully integrated into the Obligate team. Together, we share both the ups and downs, as they are an integral part of our journey. There are a lot of challenges ahead and we are happy to have a partner as reliable as The Stable House by our side.
You just take care of business. We deal with blockchain, architecture, infrastructure...
Technology in finances and banks is no easy task, our team understands that. Book a 1-h no-strings-attached meeting and consult your project with specialists with dozens of successful fintech projects in the portfolio.