DEV Community

Cover image for Crafting the perfect token launchpad - technical description
Mikhail Kedzel for MiKi

Posted on

Crafting the perfect token launchpad - technical description

Recently, at MiKi we've created a token launchpad for one of our clients. The project is under NDA, so we can't tell you much. But still, we'll try our best to describe our approach when it comes to building a simple, yet impressive token launchpad on Ethereum blockchain.

Our main requirements for a launchpad were

  • Speed of development - we had to meet tight deadlines
  • Ease of future support - the app had to be maintanable with clean, clear code
  • Stability - the app had to be fault-tolerant, we couldn't have even the slightest downtimes
  • Blazing-fast loading - if the app loads too long, it'll definitely frustrate our buyers

Let's discuss every part of the app, and what choices we made to meet all the requirements

Frontend

We picked NextJS as the framework, since it has huge user adoption, vast documentation and community support. In web3 as well, there are many libraries specifically built for NextJS or React(NextJs is built on top of React). For example, Moralis - a famous library to integrate Web3 into your app, has a lot of tutorials for specifically NextJS. As it's very easy to handle authentication there and intergate next-auth(authentication library for NextJS) with Moralis.

Compared to regular React, NextJS supports server-side rendering which makes your website load up to 5x times faster. For reference, here's the performance metrics for a typical React app

results for the CRA

And here's the data for a NextJS app

results for the NextJS app

Crazy difference, right?

Blockchain integration library

We've picked the frontend framework, now we have to decide how to integrate our frontend with Ethereum blockchain and our smart contracts.

Moralis is a great choice if you want to get going very fast, since it's a rather high level of abstraction. But it comes at a cost of limited customization. Even thought the deadlines were short, we still needed an extensible library which would allow future changes.

We chose ethers - the tried and true library for interacting with Ethereum blockchain and it's ecosystem on the frontend. It's mostly used to intergate with wallets, so has first-class MetaMask support.

Ethers logo

A worthy metion is viem - we're using it on our current project and it's working flawlessly. Viem is still new, so using it might be harder than ethers, as there's less comunity support. But Viem addresses many flaws ethers had like type safety.

Doing it the React way

We've also used wagmi - a collection of React Hooks containing everything you need to start working with Ethereum. Wagmi works perfectly with ehters, as it uses ethers under the hook. While wagmi wraps some trivial functionality of ethers to provide you a simple interface, you'll still need to use ethers directly in some cases.

If you want to read more about working with smart contract on the Frontend - check out our article where we go in depth and provide some code snippets https://dev.to/miki-digital/working-with-smart-contract-on-the-frontend-3mem

Backend

We had to implement KYC so a backend was necessary. We could've just stored the KYC on the blockchain, but we weren't sure how large would it get in the future, so opted for a database MongoDB.

As the BE framework - we still used NextJS. It has a minimalist, yet powerful way of writing serverless backend. You can easily run the backend locally, and deploying it is straight-forward as well thanks to vercel. Opossed to bigger providers like AWS, GCP and Azure, on Vercel you can deploy your whole app(backend and frontend) in a few minutes.

The syntax is exteremely simple as well, you just need to declare a handler function for an api route, and handle the logic in there. For example, an API route for fetching balance of a smart contract from etherscan would look like this:

export default async function handle(req: NextApiRequest, res: NextApiResponse) {
  if(req.method === 'GET') {
    const resp = await fetch(`https://api.etherscan.io/api?module=account&action=balance&address=${process.env.NEXT_PUBLIC_CONTRACT_ADDRESS}&tag=latest&apikey=${process.env.ETHERSCAN_API_KEY}`);
    const json = await resp.json();
    res.json(json);
  } else {
    throw new Error(
      `The HTTP ${req.method} method is not supported at this route.`
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Using Serverless will help us save money, since Serveless only bills you for the number of requests, not the server uptime. Most of our audience were in Korea, so at night there was barely any traffic. Compared to a normal server, Serverless is also fully managed so you don't have to configure the infrastructure - Serverless scales up and down by itself, so you can forget about trying to guess the capacity of your server and allocate enough CPU/Memory/etc. for it.

Connecting to the database

The default solution when it comes to MongoDB and NodeJS is using mongoose, but we believe there's a better choice - Prisma.

Prisma logo

Prisma is a next-generation Node.js and TypeScript ORM. While originally made for SQL, it supports MongoDB. The main difference is that Prisma has a custom syntax for schema and a separate file for them - schema.prisma. It has type-safety by default and a rich query builder as well. Functionality is similar to Mongoose; the only difference is that Prisma has Relation filters, while Mongoose doesn't have a dedicated API for it. This means that dealing with relations in Prisma is pretty simple, while you'll have to think of complicated queries in Mongoose. It's one of the most powerful features of Prisma since searching by relations is a must if you have complex data.

For something like finding the user by address, a Prisma query is as simple is:

const user = await prisma.user.findUnique({
  where: {
    address: req.query.address as string
  }
})
Enter fullscreen mode Exit fullscreen mode

Smart contracts

We developed a cascade of smart contracts:

Token:

Customized ERC20 token with specific minting logic (used for vesting, exchange listings, and liquidity distribution, according to the project’s whitepaper) and Ownable feature.

Public Sale:

Original contract with whitelist providing users the opportunity to buy a preset supply of tokens at preset prices. Users were divided into two groups based on their presence in the whitelist, which was implemented on-chain using Merke Tree.

NFT holders’ claim:

The original smart contract implements the logic for NFT holders to be able to claim their NFTs and receive tokens. It was connected to the two already existing NFT collections and distributed tokens depending on NFT item rarity.

Staking:

Gives token and NFT holders an opportunity to stake (lock assets for a set period of time) their tokens and NFTs in 3 ways:

  • bare tokens stake - people just stake tokens and receive rewards based on their amount
  • single NFT stake - users can stake tokens together with an NFT item from a single collection and receive increased rewards based on the rarity of their tokens
  • pair NFT staking - holders can stake their tokens together with paired NFT items from different collections and receive raised rewards based on the degree of how their NFT items’ match each other and their own rarity

Vesting:

Original contract storing minted tokens and insuring them being distributed at specific timestamps and only to the prespecified contracts. Controls the token distribution and makes it transparent.

Smart Contracts were written in Solidity using OpenZeppelin libraries and were deployed, verified and tested both on testnet and mainnet using Truffle Framework. Both unit and integration tests were written in Typescript and Javascript.


That's it, let us know if you have any questions or suggestion for how we could improve the architecture.
Need web3 help? Feel free to contact us at our website or book a call.

Top comments (0)