Designing a Cryptocurrency Trading Application
Rene Zander
Posted on August 14, 2023
If you’re a solution architect you likely already understand the complex nature of system design. In this post, I’ll provide a high-level overview of what’s involved in designing a cryptocurrency trading application, as well as a deep dive into the key aspects of the system.
Step 1: Establish design scope
First, let’s establish scope. Our goal is to create a system that can support up to 100,000 users. The app should be both, mobile and web app.
Important features are discovering token swaps on a blockchain and opening positions for specific swaps. For each discovered token swap there is a wallet a user can follow.
Users can create rule sets, called bots, to focus on those swaps they are most interested in. A user can follow a maximum of 100 wallets. For each wallet 100 snapshots are kept, and the total number of wallets stored is 5,000.
To host the application I will leverage existing cloud hosting infrastructure. The application will run inside a container, the database will be hosted on Supabase.
Now that I have outlined the requirements I can start designing the system. To fit scale constraints I use a back-of-the-envelope calculation.
Queries per second (QPS)
- Assume 100,000 users. Each user creates 10 positions per day.
- QPS: 100,000 x 10 / 24 hours / 3600 seconds = ~12 queries per seconds
Number of servers
- Assume one server can handle operations for 20,000 users
- 100,000 users should be our scale
- we need 5 servers
Storage requirements
- we store 5,000 wallets, for each wallet 100 snapshots, total of 500,000 snapshots
- we store ~100 swaps x 5,000 wallets = 500,000 swaps
- one wallet is copied by 10 users, resulting in positions, 5,000,000 positions
- size of a position: 200 bytes
- 5,000,000 x 200 bytes = 1,000,000,000 bytes = 1 GB
Step 2: Derive the high-level design
The design is divided into two flows: swap publishing and position building. Lets explore those next.
- swap publishing: when a swap event is discovered on the blockchain, corresponding data is written into cache and database. A swap is populated to the user interface component.
- position building: positions are derived from swaps data, controlled by rule sets the user can create on the user interface component
Swap publishing
The following figure shows the high-level design of the swap publishing flow:
- Blockchain: The blockchain provides access to transactions that are written on the blockchain
- Swap service: Swap service listens for swap events on the blockchain, retrieves wallet information on the swap initiator, and additional information from the related transaction
- Swap cache: Swap cache holds all recent swaps. This includes all swaps that have not been processed by the position service
- Swap DB: the database holds all swaps data
Position building
- user: user have two ways to interact with the application, mobile or web.
- load balancer: the load balancer spreads incoming requests across all web servers.
- web servers: web servers take incoming requests and provide access to the position service, notification service and a user interface for trade management
- Position Service: Position service takes swap data from swap cache, filters for following users, and sends positions back to the blockchain using the settings a user has provided.
- Position Cache: cache holds positions that are yet to be processed. Positions need to be opened, notifications are to be send
- Position DB: Position DB holds all position data
- Notification service: notification service sends notifications to users, i. e. via telegram
Step 3: Design deep dive
The high-level design briefly covered two flows: swap publishing, and position building. Here, we discuss those topics in more depth.
Swap publishing deep dive
Figure to show detailed design:
We touched on the basic process how swap publishing works. Let’s explore more in-depth.
1. Connect to a Web 3 API provider: The application will connect to a Web 3 API provider to access the blockchain, and start to listen for swap events on the US dollar stablecoin token contract (in blockchain land called Tether): The application willl specifically listen for swap events on the Tether token contract, as this is a common stablecoin used in cryptocurrency trading.
2. Further the swap sender’s token balances and related transactions will be retrieved: Once a swap event is detected, the sender’s token balances and related transactions are retrieved, getting all that is needed to build another position. All this information is written to cache. Beside the swap data there is also a snapshot of the sender’s wallet being recorded, documenting the amount of tokens at the time: Every time a swap of tokens is discovered on a transfer event a snapshot of the senders wallet is taken. Once the initial snapshot exists the app can start calculating profits for all tokens the time the next snapshot has been written to the database.
3. Write the swaps to the database: Finally, swaps are written to the database for further processing.
Web servers
Web servers take requests from the load balancer and provide access to APIs, and the user interface. Also, web servers control rate limiting, and handle authentication.
Authentication is needed to provide access, and allow to allocate data a user has created to his account.
Rate limiting is essential to the health of the application, and block bad actors from misuse. The application provides several APIs, that are in scope for rate limiting. The implementation sits on the web server tier, basicly intercepting requests as a middleware. There are different algorithms to choose from. For this application we use the token bucket algorithm.
Position building deep dive
Here is how position building works:
To allow users open positions they would need to set up a trading account on the user interface. Once they have a wallet, they start funding it, and configure the trading experience to their liking.
1. The position service listens for new swaps on the cache tier. Inserted swaps are broadcasted to allow the creation of positions from the swaps data.
2. Every swap could be a potential position. Users who do follow a wallet related to the swap have a position being initialized and send back to the blockchain.
3. Notifications are sent out through external service, Telegram Messenger. Before using the service a user would contact the application’s Telegram Bot, retrieve an ID they would connect with their user account in the profile section on the user interface.
4. Positions are shown on the user interface. The user interface allows users to discover wallets they might want to follow.
Cache
A cache is a temporary storage area that stores the result of expensive responses or frequently accessed data in memory. The application performance is greatly affected by calling the database repeatedly. The cache can mitigate this problem.
Cache tier
The cache tier is a temporary data store layer, much faster than the database. The following figure shows the setup of a cache server:
After receiving a request, a web server first checks if the cache has the available response. If it has, it sends data back to the client. If not, it queries the database, stores the response in cache, and sends it back to the client.
Cache architecture
Cache does enhance the performance of the application. We divide the cache tier into 4 layers as shown in the following figure:
- Swap data: it stores information about the most recent wallet snapshots and swaps
- Social Graph: stores relationships between users, bots, and wallets
- Trading: stores information about positions
- Counters: stores counts for follows and positions
Step 4: Wrap up
In this post, we designed a cryptocurrency trading application. We followed along the process of scoping the requirements, deriving a high level design, and dived in more deeply into the core components of the application.
This design is not perfect. It is always important to know what the requirements are, what is in scope, and to iterate. You only know so much when you start out. Once you revisit a system after some time requirements might have changed, scaling contraints might be different, or the importance of features has changed.
Here are a few ideas to follow up on this design:
- replace postgres with a graphdb for more performance
- build out a module system and integrate with a larger system like Dune.com
- rebuild the app as a monolith to reduce latency between components
- rebuild to support 100M users
I hope you enjoyed reading this article, and learned something. ;-)
Please consider following me. I plan to post more content on system design and related topics.🥳🥳
René
Reference materials
1. Demo application https://ramaris.app
2. Code Base https://github.com/renezander030/ramaris
3. Let’s talk https://www.linkedin.com/in/reneza
Posted on August 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024
November 30, 2024