This is the first in a series of FORDEFI Engineering blogs. The goal of this series is to share some of the technical lessons we have learned while building our product.
FORDEFI’s mission is to enable institutions to securely hold digital assets and safely transact across decentralized finance. A key part of executing on that vision has been building our own browser extension. The extension provides ubiquitous connectivity to dApps across a wide range of chains. Furthermore, powered by FORDEFI’s transaction enrichment system, the extension presents the user with insightful details on each transaction, enabling the user to verify its integrity, review and mitigate any risks, and understand the transaction’s impact on the user’s wallet.
In this article, we will discuss some of the technical challenges we have encountered while building FORDEFI’s browser extension, and how we have chosen to tackle them. Specifically, we will describe how we have successfully adapted our extension to Manifest v3 – Google’s latest browser-extension standard.
The Chrome Extension Manifest is a JSON configuration file that instructs the Chrome browser how to behave when a user installs your extension. It includes information about your extension's permissions, content scripts, and background scripts, among other things. In June 2021, Google released Manifest v3, which made significant changes to how extensions behave and how they are managed by the browser, compared to Manifest v2.
Changes to extension permissions
Manifest v3 introduces new restrictions on the permissions that extensions can request. It also allows users to grant permissions to specific sites, rather than giving blanket access to all sites.
Background service worker
In Chrome Extension Manifest v3, the script, which was previously used to run extension code in the background, has been replaced by a service worker.
Background scripts are a crucial element in most extensions, as they enable the developer to carry out actions or run code without requiring user input or action. They are often used for tasks such as sending notifications, coordinating with content scripts, and much more. These scripts typically run continuously in the background.
Background service workers are only activated when needed, such as through the receipt of a message. To begin execution, service workers must first register listeners for specific events. Once these events are triggered, the background service worker will activate and execute the desired tasks. After a period of time which is determined by the browser, the background service worker will automatically shut down, until it is activated again.
Background service workers also have more limited permissions than background scripts in Manifest v2. Service workers can only access certain APIs and perform certain actions when they are active, and they must request permission to use any additional APIs they need. This means that service workers cannot perform certain actions that background scripts could in Manifest v2, such as modifying network requests or accessing the user's data without explicit permission.
Profiling the background service worker
In Manifest v2, it is easy to evaluate the performance of the background script as it is similar to evaluating any other web page. However, this common method of profiling is facing challenges when applied to the background service worker, resulting in errors.
A request for addressing this issue has been submitted.
These changes have had a significant impact on many existing extensions. The changes broke a lot of existing code and required developers to completely redesign their extensions in order to make them compatible with the new manifest.
There were numerous controversies following the changes in Manifest v3, including Brave's announcement to continue supporting Manifest version 2, Tor worrying about user privacy, and some bloggers considering migrating to Firefox.
During the process of adapting their extensions for Manifest v3, developers encountered a significant problem with the lack of documentation and guidance from Google. Due to the significant changes in Manifest v3 and the insufficient documentation, many developers have struggled to comprehend how to migrate their extensions to the new manifest.
Achieving Success in the Face of Challenge
A brief anatomy of our web3 wallet
A web3 transaction involves three key parties: the dApp websites, the blockchain, and the wallet. A user initiates a blockchain transaction through a dApp website. The dApp website creates the transaction based on the user’s action, and passes this transaction to the wallet. The wallet then presents the transaction to the user, asks for the user’s consent, signs the transaction, and passes it to the blockchain.
To facilitate this flow, the wallet needs several functionalities. First, in order to receive the transaction from the dApp website, the wallet needs to interact with the website and inject a piece of code into it. Second, to interact with the user and get the user’s consent, the wallet needs to open a popup. Finally, to send the transaction to the blockchain, the wallet needs to issue network requests. These three pieces need a reliable communication channel with each other in order to pass the transaction data from one step to the other.
On the face of it, the constraints imposed by Manifest v3 might seem to hinder some of the basic tasks the above flows requires:
- The web3 protocol strongly relies on bi-directional communication between the dApp website and the extension. Both the transition from a background script to a service worker and the new permission model of Manifest v3 significantly complicate any such communication.
- The background service worker needs to maintain a consistent record of both its interactions with various dApp websites and the consent prompts awaiting the user. The periodic restart of the background service worker makes it hard.
- Reliably handling the user’s transaction requires the dApp website to continuously communicate with the service worker. Again, the periodic restart of the background service worker makes it difficult.
In the rest of the article we will describe how we addressed those challenges.
All data that is transferred to or from the background service worker, as well as any information that is processed by the worker, must go through the extension's storage. To facilitate this, we developed a generic layer that can store the information efficiently and easily, and can also manage concurrent requests that need to modify the storage.
The extension must be able to handle multiple message requests concurrently from the dApp website.
To handle these messages, we may simultaneously access the same storage elements, such as adding a value to an array or reading a complex object from storage.
An example of how to read and write complex data to chrome storage:Link to code
Imagine the consequences if two separate requests were to update `FORDEFI_data` at the same time. Both read the data and update it – they could overwrite each other and cause data loss, as seen in the following example:
Link to code
To prevent this, we flattened any complex data structures and reconstructed them when they needed to be returned in full. Take note of how the `itemKey` is appended to `globalKey`, allowing for each item within the complex object to be stored individually and preventing any alteration made to one item from overwriting the others.
Link to code
Now, even if multiple requests access the data simultaneously, they will not be able to overwrite each other.
Our extension maintains a long-term connection with the dApp website and any popups it generates. However, in Manifest v3, the extension background service worker is short-term and will shut down after 5 minutes.
It was recently announced that this behavior will change in Q1 of 2023! The service worker staleness will depend on its activity and will not be time based anymore.
Due to this behavior, we needed to develop a communication layer that can re-establish the connection when it is lost.
Our solution has two components:
- A handshake mechanism that enables both sides to bootstrap before resuming it.
- The ability to store pending messages when the connection is lost, and then recreate the connection and transmit all pending messages once it is restored.
In order to establish a two-way handshake, the service worker and the service that wants to communicate with it follow the following protocol:
- The other service sends a `syn` message to indicate that it is ready to connect to the background service worker.
- The background service worker intercepts the `syn` message and, once it is ready, sends an `ack` message in response.
- Upon receiving the `ack` message, the other service knows that it can resume communication and send any pending messages that it has stored in its queue.
- When the port becomes disconnected from the background service worker, the other server begins to queue pending messages and sends a `syn` message again, as described in the first step.
An example of the process in action:
Link to code
Communication with Website
As the extension cannot directly modify or access the website, we had to develop a complementary mechanism that allows us to retrieve and set information that the background service worker needs.
To accomplish this, we created an `inpage` script that is injected into the website by the `content script` service, which has access to the website. The `inpage` script communicates with the `content script` using post messaging, and the `content script` communicates with the background service worker using `chrome.runtime.connect`. This establishes a bi-directional communication channel between the website and the browser service worker, allowing the service worker to interact with and access the website while adhering to the restrictions of Manifest v3.
An alternative method exists to address the present issue of profiling the background service worker, and is mentioned in the link I previously shared in this post.
Here’s a small demonstration of how it would work:
Manifest v3 has both advantages and challenges. While it offers improved security, privacy, and efficiency compared to v2, it requires developers to construct multiple frameworks to replicate the functionality of their previous solutions and completely redesign their infrastructure. As a company that values staying current with technology, FORDEFI made the decision to adopt Manifest v3 from the beginning, which allowed us to carefully plan our product with these limitations in mind and build a reliable and robust infrastructure.
At some point in the future, Google will no longer permit the publication of Manifest v2 extensions (although it is currently unknown when these extensions will be removed from the store). As a result, the demand for best practices, a knowledge base, and a supportive community is growing rapidly. We hope that the documentation for Manifest v3 will expand and developers will no longer have to navigate these challenges on their own. In the meantime, we hope that this post will be helpful to those who, like us, have struggled with the transition to Manifest v3.