# Building the AirSwap Debugger App: A Technical Overview

By [starrdev](https://paragraph.com/@starrdev) · 2023-12-15

---

This blog post will give a technical overview of how I built the AirSwap Debugger app. I'll discuss the code, challenges faced, and lessons learned.

Going forward, I’ll sometimes refer to the app as `airswap-debugger`.

Since I started contributing to AirSwap, this was the second major app that I worked on from its inception, and the first web app that I’ve pushed most of the code to GitHub!

`airswap-debugger` is a web app that helps AirSwap market-makers debug their API connections.

GitHub: [https://github.com/airswap/airswap-debugger](https://github.com/airswap/airswap-debugger)

App: [https://check.airswap.eth.limo/](https://check.airswap.eth.limo/)

**What is the AirSwap Debugger?**
---------------------------------

![The AirSwap OTC platform relies on makers to facilitate orders](https://storage.googleapis.com/papyrus_images/774d65a2d5d7b9deac69694af898d20d693f16ffe90fd471fe33c6fc78fa9cd8.png)

The AirSwap OTC platform relies on makers to facilitate orders

To understand this app, first you must understand what a “maker” is. AirSwap makers are liquidity providers for the trading platform.

Makers run web servers that implement APIs like RFQ and LastLook. To build these APIs, makers provide JSON objects with certain values.

To learn more about makers, RFQ and LastLook, read the [**official documentation**](https://about.airswap.io/technology/makers).

`airswap-debugger` is a web app where AirSwap makers can debug their JSON objects and server URLs. This will be covered in more detail later in this blog post.

Tech Stack
----------

This app is built using TypeScript, JavaScript, React, and TailwindCSS, with blockchain connections facilitated by the `viem` and `wagmi` libraries. The app was initialized with Vite.

`viem` and `wagmi` provide hooks and functions that make it easy to connect your app to EVM blockchains like Ethereum. For more details on how these libraries work, check out some of my old blog posts like “[Creating Seamless Web3 Wallet Connections in Your DApp Using Wagmi Hooks](https://mirror.xyz/starrdev.eth/ACRuWBd-jArASW50cTwG1ECftUCC-AqxUnVVVT-jnjE)”.

How The Debugger App Works
--------------------------

![To use the app, paste an OTC order URL into the textarea. Order details will be displayed in the left column, and issues with the order URL will be displayed in the right column](https://storage.googleapis.com/papyrus_images/366aac2dce1f4b856d7c67aa4db76909079b3037bc7fbd786a063e77326daddc.png)

To use the app, paste an OTC order URL into the textarea. Order details will be displayed in the left column, and issues with the order URL will be displayed in the right column

There are 2 main functions in the app: `validateJson` and `check`.

1.  `validateJson`: this is a JavaScript function that uses TypeScript. This checks a user’s JSON input on the front-end of the application. If these validations fail, the `check` function won’t run.
    
2.  `check`: this function exists on the AirSwap `SwapERC20` smart contract, and requires 11 different inputs to run without errors. This is the function that checks whether or not a JSON is valid.
    

Validate JSON function
----------------------

`validateJson` is a JavaScript function that uses TypeScript to validate the shape and contents of the user’s JSON input. If these validations fail, the `check` function won’t run.

For non-developers, errors returned by `check` can be vague if any values are missing. This is why the `validateJson` was implemented on the front-end app.

`validateJson` has 3 major checks:

*   JSON must be in valid syntax
    
*   Specific JSON key-value pairs are present
    
*   JSON values are in the correct format. For example, some values must be a valid ERC20 addresses. `validateJson` checks this by using the `isAddress` function, which is imported into the React component from the `viem` library
    

`validateJson` has several `if..else` code blocks which run validations. If any part of the JSON is invalid, an error gets pushed into an array called `errorsList` and returned by the function.

![Code snippet from the validateJson function](https://storage.googleapis.com/papyrus_images/130becbc88cf51b38f74f45aa2e5b84cc22737396aff06a31e29c604792771ca.png)

Code snippet from the validateJson function

If there are no errors, `validateJson` returns `false`. Otherwise it returns a list of errors. We’ll revisit this `errorsList` variable later in the post.

Check Function
--------------

The `check` function lives on the AirSwap `SwapERC20` [smart contract](https://etherscan.io/address/0x0C9b31Dc37718417608CE22bb1ba940f702BF90B#readContract). `airswap-debugger` is just a UI that makes it easier to use this function.

`check` validates the following inputs:

1.  nonce
    
2.  expiry
    
3.  signer wallet
    
4.  signer token
    
5.  signer amount
    
6.  sender token
    
7.  sender amount
    
8.  sender wallet
    
9.  transaction signatures (3 separate values: “v”, “r” and “s”)
    

The AirSwap protocol collects revenue from trading fees, so it’s critical that makers are able to validate their technical setups.

UI Components
-------------

![File component structure](https://storage.googleapis.com/papyrus_images/24f88368544525d83f5b984bcac5774e82dc14311bbe7a46519060bcd44403e8.png)

File component structure

The main React components include: `App`, `Toggle`, `JsonForm`, `UrlForm`, `Errors`, `Selector` and `Dialogue`. There are some smaller components that aren’t important to go over, for example the header.

App
---

This is the main entry point of the app. Here’s a breakdown of the different sections of the `App` component.

### **Imports:**

At the top of the component you’ll see various imports from `React`, `Wagmi`, `@airswap/libraries`, and various hooks and components from other files.

### React State Hooks

![Code snippet of all the useState hooks used](https://storage.googleapis.com/papyrus_images/cd4da5ddc0d7b525d067663f2eb1ed9e30920e06d8ff95e2eba085ee3ffd1516.png)

Code snippet of all the useState hooks used

`airswap-debugger` utilizes React’s `useState` hook. Here’s an overview of each state variable:

*   `inputType`: this can either be `json` or `url`. Depending on which value it is, different forms will render, and different functions will run.
    
*   `jsonString`: when a user pastes a JSON, it’s a string that gets saved to this variable. Later on, `jsonString` gets parsed into a valid JSON object, and stored in a different variable.
    
*   `urlString`: when a user pastes a server URL into the form, it gets saved into this variable. This variable gets passed as a prop down to the `Toggle` component, and used in a click handler.
    
*   `parsedJson`: This variable gets set after a user presses the submit input button. The JSON gets parsed in the native JavaScript function `JSON.parse`.
    
*   `decompressedJson`: when a user enters a server URL, the app decompresses it into a JSON object and saves it into this variable. This takes place in the `Dialog` component.
    
*   `swapContractAddress`: this variable stores the smart contract address of AirSwap’s `SwapERC20` address. We need the correct address so that `wagmi` is able to call the correct contract functions.
    
*   `selectedChainId`: if a user selects a chain ID from the selector, it will be stored here. This is different than if a user hard-codes a chain ID into a JSON object. This will be covered in more detail later in the `Selector` component section.
    
*   `errors`: this is a list of errors if a JSON or server URL is invalid.
    
*   `isEnabledCheck`: this is a boolean value that serves as an on/off switch for the `check` function. It helps prevents the app from rendering outdated data.
    
*   `isNoErrors`: this is a boolean value that gets set to `true` if no errors were found. If `true`, and if `isEnabledCheck` is also true, a special message will be displayed to the user. If `isEnabledCheck` is false, then the `check` function hasn’t yet run. In this case we show the user nothing until they run the debugger.
    

### Custom Hooks

![Code snippet of useDecompressedOrderFromUrl hook](https://storage.googleapis.com/papyrus_images/c51c696d6eb85e1e919d72ffd463a0c70e776d7eb1e92004220b688ae2a3dbc5.png)

Code snippet of useDecompressedOrderFromUrl hook

`useDecompressedOrderFromUrl`: this hook was build to convert a server URL into JSON. The library `@airswap/util` library has a function called `decompressFullOrderERC20`. This accepts a server URL string and converts it to human-readable JSON.

`useJsonValues`: This function takes in a JSON as an object. It parses the JSON, converts each of its vales into the correct format, and returns its individual values in an object. The correct formats are determined by what the `check` function require.

### **Smart Contract Interaction**:

`useContractRead` hook from `wagmi` is utilized 5 times. The first time is in the `check` function.

The other 4 return values are: `protocolFee`, `domainName`, `domainChainId`, and `domainVersion`. These values are specific to the deployed `SwapERC20` contract. `SwapERC20` is deployed on various chains, including Mainnet, BSC, Avalanche, and others. On different chains, `domainChainId` would change. Upgraded contract versions might have different `domainVersion` values.

If a user’s JSON object has errors, it’s possible that the JSON is not using the correct value for one of these 4 values. This feedback is returned to the user.

### `checkFunctionArgs`:

This is a list of inputs that we pass into the `args` property of `useContractRead` for the `check` function.

![checkFunctionArgs utilizes TypeScript for validations](https://storage.googleapis.com/papyrus_images/0c87fc3574d0b0ceac9879b9c2389380632aeec53839fc9acc32479b0b828454.png)

checkFunctionArgs utilizes TypeScript for validations

### **Event Handlers**:

There are 2 handlers that control the forms on the app: `handleChangeTextAreaJson` and `handleChangeTextAreaUrl`. These function similarly, and get called on each of the `textarea` HTML elements within the `JsonForm`, and `UrlForm`. There’s also the `handleSubmit`, which will be explained next.

### Submit Function (`handleSubmit`)

Several smaller functions and hooks make up the submit function. The function begins by initializing the following `useState` hooks to their default values: `setParsedJson`, `setIsEnabledCheck`, `setErrors`, `setIsNoErrors`.

![Clicking this button on the UI runs the handleSubmit function](https://storage.googleapis.com/papyrus_images/32cafebd2f0208deebd6e0f5ec5ecd7b929f158477fffe5cebdef8d7b23de90a.png)

Clicking this button on the UI runs the handleSubmit function

The following functions make up the `handleSubmit` function:

### `validateInputs`

This checks for blank inputs. If inputs are blank, `handleSubmit` returns nothing and ends.

### `handleJsonSubmission`

If input type is `json`, this function runs and parses a JSON string into valid JSON format. It then sets the `parsedJson` variable equal to this value. Lastly, it runs the function `checkSmartContractError`.

### `checkSmartContractError`

This accepts in an argument `errorCheck`, which gets returned from the `check` function. This function handles unreadable errors returned from the smart contract, and makes them more human-readable.

### `handleUrlSubmission`

This is similar to `handleJsonSubmission`, but for server URLs.

### `validateInputs`

This function checks that a user input isn’t blank. Otherwise the smart contract `check` function won’t run.

![If the user tries submitting a blank form, the app will return an error](https://storage.googleapis.com/papyrus_images/45d6c32b70b995673b2bc44196dacfdf4609db5b22521d73d2bb9d5ce6f4d8ec.png)

If the user tries submitting a blank form, the app will return an error

### `useEffect` Hook #1

There are 2 `useEffect` hooks in the `App` component. Here’s how the first one works:

1.  The hook first gets triggered when the `parsedJson` value changes. Recall that running `handleSubmit` updates `parsedJson`.
    
2.  At the start of this `useEffect` hook, the `validateJson` function runs. `validateJson` returns a boolean value. If the value returned is `true`, there are errors which will get pushed into the `errors` array.
    
3.  Next, the function `getOutputErrorsList` runs. This takes in an argument `checkFunctionData`, which is an array of errors returned from the `check` function. These errors are unreadable, in hex format. `getOutPutErrorsList` maps over this array of hex values, then uses the `hexToString` function, which is imported from `viem`, to decode the hex formats into human-readable strings.
    
4.  Next in the `useEffect` hook is the function `displayErrors`. This parses errors returned from the smart contract, and makes them human-readable. For example, if an error contains `'NonceAlreadyUsed'`, then `displayErrors` will switch that into the error message: `'Nonce: the nonce entered is invalid'`.
    
5.  Next is the function `handleFormattedListErrors`. This cleans up any errors returned from the smart contract that have too much blank space. These errors get pushed as strings into the `errors` array.
    
6.  Lastly, the `useEffect` hook checks if there are no errors in the `errors` array. If no errors, the state variable `isNoErrors` gets set to `true`.
    

![If there are no errors, this special message is displayed ](https://storage.googleapis.com/papyrus_images/d3c0020d4ccffd8a8eb8890e096740c4b99ae5c991d9b68ce8c8a84379473e1b.png)

If there are no errors, this special message is displayed

### `useEffect` Hook #2

This hook checks for a change in `chainId`, the blockchain network ID. When a change is detected, the function `getAddress` from the AirSwap `SwapERC20` contract gets called, and sets the correct address in the `swapContractAddress`variable. `swapContractAddress` gets passed into the `useContractRead` hook from `wagmi`, which is used to call the `check` smart contract function.

Forms & Toggle
--------------

![You can toggle between JSON and URL forms](https://storage.googleapis.com/papyrus_images/136c01fa5a5f9e7cefc2a90dfaf426d2eb528ebdd59060ca8bcfaea73d3d431c.png)

You can toggle between JSON and URL forms

We have 2 forms on the app: `jsonForm` and `urlForm`. Each form lives in separate React components. Both forms correspond to the relevant functions mentioned above. The `Toggle` component contains 2 buttons, which lets you select the form you want to use.

Errors
------

![When a user enters an invalid JSON, errors are displayed on the right](https://storage.googleapis.com/papyrus_images/d84fd01a47ce98ccf24f78accc13e000bcd75409b4b73859ccd7e7070573acaa.png)

When a user enters an invalid JSON, errors are displayed on the right

This component’s purpose is to render errors that are returned from the smart contract, or any of the following functions: `validateJson`, `checkSmartContractErrors`, or `displayErrors`.

The `Errors` component either displays nothing, a list of errors, or a message that reads “🎊 No errors found! 🎊”.

`airswap-debugger` is mobile responsive. On tablets and larger screen, there are 2 columns: forms on the left, and `Errors` on the right. On mobile devices, the UI displays 1 column: forms on top, and `Errors` on the bottom.

![On mobile devices, the app displays as a single column](https://storage.googleapis.com/papyrus_images/55ec680a0ccebfeba44a880185c6e41c5d2fe369738ac793a33ecc219377aa1a.png)

On mobile devices, the app displays as a single column

Selector
--------

The selector has several blockchain networks (chain IDs) a user can choose. Users can choose a `chainId` from the selector, or optionally hard-code it into their JSON objects. If `chainId` is hard-coded into a JSON, it’ll override the selector option, but not vice-versa.

![The selector lets users choose from a variety of networks](https://storage.googleapis.com/papyrus_images/0eca7850f0640fb8964ad38462a40231d72af01e4bf49b6c0e0e5da2ba264541.png)

The selector lets users choose from a variety of networks

Building the selector was fun and challenging. Styling a native HTML selector with CSS is more difficult than it looks. For this reason, I ended up using the `RadixSelect` component from [Radix-UI](https://www.radix-ui.com/primitives/docs/components/select). Radix-UI is easy to use, and is compatible with TailwindCSS!

Dialog Component
----------------

![The dialog is a modal which displays the decompressed URL](https://storage.googleapis.com/papyrus_images/49e3805e8e58f93ed4dba3f778e1adba3b6b88146616be42426963423c84b92d.png)

The dialog is a modal which displays the decompressed URL

`dialog` is a modern HTML element, and its common use case is for building modals. I built the dialog for when users enter a server URL. The JSON from the decompressed URL gets rendered onto the dialog, as seen in the image above.

Challenges I Faced While Building The AirSwap Debugger
------------------------------------------------------

### **Managing state among React components**

![Getting the errors component to display the correct errors took some trial & error](https://storage.googleapis.com/papyrus_images/d84fd01a47ce98ccf24f78accc13e000bcd75409b4b73859ccd7e7070573acaa.png)

Getting the errors component to display the correct errors took some trial & error

As the app grew in complexity, it became hard to manage the `errors` array state variable. Various functions, hooks, and components update `errors`, and sometimes I had issues with duplicate values being pushed into the array. Other times the array wasn’t being reset to a blank array when I wanted it to.

These issues happened over the development cycle of the app. At this point I cannot recall every time it happened. To fix these issues, I used `console.log` a lot.

A common process I use to track down bugs is locate the function that is causing the error. Within that function, I’ll put `console.log` statements after every line, then hunt down the exact issue. Simple, yet effective!

### Error: `Buffer: buffer not found`

This was an error that came up, and similar ones as well. It was related to Vite not having polyfills. In JavaScript, polyfills are scripts that ensure cross-browser compatibility. Some older browsers may lack support for certain features, and polyfills bridge the gap, allowing newer features to function in these legacy browsers.

To fix this error, I found an npm called `vite-plugin-node-polyfills`. I added this to my project, and updated my `vite.config.js` file like so:

    export default defineConfig({
      plugins: [
        react(),
        nodePolyfills({
          include: ['path', 'stream', 'util'],
          exclude: ['http'],
          globals: {
            Buffer: true,
            global: true,
            process: true,
          },
          overrides: {
            fs: 'memfs',
          },
          protocolImports: true,
        }),
      ],
    });
    

### Designing the UX

Other than small personal projects, this was the first time I designed an entire UX! I took inspiration from existing AirSwap apps, and used similar color schemes and design patterns.

![For airswap-debugger, I took inspiration from airswap-voter-rewards](https://storage.googleapis.com/papyrus_images/e843f9ff82cded18dde1dfac33b6628e99347d774d8a10764d38c89a530e8a9d.png)

For airswap-debugger, I took inspiration from airswap-voter-rewards

I took into account different screen sizes, and the best way to display data to the user. One example was deciding between displaying the decompressed JSON to the user in a prompt, or a dialog. Prompts are harder to close than dialogs, so I opted for a dialog. The design was more congruent with the rest of the app, and the dialog opens and closes smoothly.

### Navigating a project solo, start to finish

Typically I prefer to work with a group. When I originally took on this project, I expected it to be a simple app that would take a week or 2. Instead, it out to be a good 6 week challenge!

The journey of building this app came with a lot of self-doubt. At times I thought that I wouldn’t be able to finish it by myself. Other people would have helped me if I needed, but I wanted to challenge myself and see what I was capable of.

My confidence as a software developer has improved because of this. Building the `airswap-debugger` app from the ground up is my proudest coding achievement so far.

Conclusion
----------

This blog post is a technical write-up of a debugger app that I built for AirSwap. It’s a DeFi tool to help AirSwap makers debug their servers.

If you’ve read this far, thank you for your time. I wrote this blog post for several reasons:

*   Enhance my writing skills
    
*   Build my personal brand, showcase my abilities, and build in public
    
*   Improve my ability to organize thoughts
    
*   Create valuable resources for others
    
*   Establish future sentimental value (it'll be enjoyable reading this post in 5 years)
    

During the writing process, I reviewed each line of code, allowing me to audit my work and identify areas for improvement.

Now that my thoughts are written down, it’ll be easier to talk about this project in person (ask me about it next time you see me!)

Check out the GitHub repository for a deeper look into the code: [https://github.com/airswap/airswap-debugger](https://github.com/airswap/airswap-debugger)

Here’s the deployed app. [https://check.airswap.eth.limo/](https://check.airswap.eth.limo/)

![](https://storage.googleapis.com/papyrus_images/0b857cffdf081e64bc5d72651ad6533da44519f269b0216cd69a913b0620ceb9.png)

---

*Originally published on [starrdev](https://paragraph.com/@starrdev/building-the-airswap-debugger-app-a-technical-overview)*
