Building an Automated Market Maker with Celo I (Backend)

Building an Automated Market Maker with Celo I (Backend) https://celo.academy/uploads/default/optimized/2X/0/04e5e2545f5488193671d0cfde8dd8558b076b53_2_1024x576.jpeg
none 0.0 0

Introduction

This tutorial will guide you as a developer through the process of building an Automated Market Maker (AMMs) on the Celo blockchain. Before we proceed, let us take a look at what Market Makers are. Market Makers are also called Liquidity Providers. They are companies that facilitate liquidity provision and trading in financial markets. Basically, they act as middle men that maintain an inventory of tradable assets. The employ the use of human traders and algorithms to carry out the trading activities and ensure that there is always liquidity in the market. One very popular market making company is Goldman Sachs. With this background knowledge, we can define Automated Market Makers as decentralized platforms or exchanges that facilitate liquidity provision and trading on the blockchain. AMMs do not need order books to operate instead they use liquidity pools. Uniswap is one of the leading DeXs that implements the concept of AMMs.

Background Knowledge

Here is a list of important things about AMMs you should know before building one

  • Liquidity Pool: A liquidity pool is a collection of digital assets or funds that is held in a smart contract. These funds are provided by individuals or entities refered to as liquidity providers.
  • Liquidity Provider: A liquidity provider refers to individuals or entities that deposit tokens into the liquidity pool thus serving as fund contributors.
  • Constant Product Formula: This formula is a mathematical equation that ensures that the total assets value in a liquidity pool remains constant. The formula is defined below
    (x * y = k)
  • Token Swaps: This is defined as the process of exchanging one token for another within a liquidity pool. The price during token swapping is usually dependent on the constant product formula and the current token balance available in the liquidity pool.
  • Trading Fees: Most AMMs charge fees on every trade done. The fees varies across AMMs

Process Flow

Starter Guide

The codebase for this tutorial is available here. You could also check out Uniswap

Setting Up The Development Environment

For this tutorial, you need to have the following installed on your computer

  • A package manager such as npm or yarn
  • Node js
  • Celo Composer

Celo Composer

Celo Composer is built on Celo to make it simple to build dApps using a variety of front-end frameworks, and libraries. For more information on how to start off your project with Celo Composer, visit here. Please keep in mind that these are the frameworks you should select while installing Celo Composer

  • React
  • Rainbow-kit celo
  • Hardhat
  • Subgraph

If you are stuck, kindly refer to the Celo Composer installation section of my previous tutorial here.

Smart Contract

From your celo-composer template, navigate to the packages/hardhat/contracts folder and create a solidity file. Our smart contract will address major concepts such as creating a liquidity pool, swapping tokens, calculating the constant product and calculating trading fees. Use this code to guide you on how your contract should look like

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

contract AmmSwap {

  // The constant product.
  uint256 constantProduct;

  // The mapping of token addresses to balances.
  mapping(address => uint256) balances;

  // The mapping of token addresses to trading fees.
  mapping(address => uint256) fees;

  // The constructor.
  constructor() {
    constantProduct = 1e18;
  }

  // The function to create a liquidity pool.
  function createLiquidityPool(
    address tokenA,
    address tokenB,
    uint256 amountA,
    uint256 amountB
  ) public returns (uint256 liquidity) {
    // Check that the amounts are not zero.
    require(amountA > 0 && amountB > 0, "Amounts must be greater than zero");

    // Calculate the liquidity.
    liquidity = amountA * amountB;

    // Add the liquidity to the pool.
    balances[tokenA] += amountA;
    balances[tokenB] += amountB;

    // Return the liquidity.
    return liquidity;
  }

  // The function to swap tokens.
  function swap(
    address tokenA,
    address tokenB,
    uint256 amountA,
    uint256 minAmountB
  ) public returns (uint256 amountB) {
    // Check that the amounts are not zero.
    require(amountA > 0 && minAmountB > 0, "Amounts must be greater than zero");

    // Calculate the amount of B that will be received.
    amountB = calculateAmountB(amountA, minAmountB);

    // Check that the amount of B is not zero.
    require(amountB > 0, "Insufficient liquidity");

    // Swap the tokens.
    swapTokens(tokenA, amountA, tokenB, amountB);

    // Return the amount of B that was received.
    return amountB;
  }
    
function swapTokens(
    address tokenA,
    uint256 amountA,
    address tokenB,
    uint256 amountB
  ) internal {
    // Check that the amounts are not zero.
    require(amountA > 0 && amountB > 0, "Amounts must be greater than zero");

    // Calculate the new constant product.
    uint256 newConstantProduct = constantProduct - amountA * amountB;

    // Update the balances of the pool.
    balances[tokenA] -= amountA;
    balances[tokenB] += amountB;

    // Update the constant product.
    constantProduct = newConstantProduct;

    // Transfer the tokens to the user.

    (bool success, ) = tokenA.call(abi.encodePacked(amountA));
    require(success, "Failed to transfer tokens");

    (success, ) = tokenB.call(abi.encodePacked(amountB));
    require(success, "Failed to transfer tokens");
  }

  // The function to calculate the amount of B that will be received in a swap.
  function calculateAmountB(uint256 amountA, uint256 minAmountB) public view returns (uint256) {
    // Calculate the constant product.
    uint256 newConstantProduct = constantProduct - amountA * minAmountB;

    // Calculate the amount of B that will be received.
    uint256 amountB = newConstantProduct / amountA;

    // Ensure that the amount of B is not less than the minimum amount.
    amountB = amountB < minAmountB ? minAmountB : amountB;

    // Return the amount of B.
    return amountB;
  }

  // The function to calculate the trading fee.
  function calculateTradingFee(uint256 amount) public pure returns (uint256) {
    // The trading fee is 0.03%.
    uint256 fee = amount * 3e18 / 10000;
    // Return the trading fee.
    return fee;
  }

  // The function to calculate the price of a token.
  function calculatePrice(address tokenA, address tokenB) public view returns (uint256) {
    // Get the amounts of tokens in the pool.
    uint256 amountA = balances[tokenA];
    uint256 amountB = balances[tokenB];

    // Calculate the constant product.
    uint256 constantProduct = amountA * amountB;

    // Calculate the price of token A.
    uint256 priceA = constantProduct / amountB;

    // Return the price of token A.
    return priceA;
  }
}

From this contract, users can create liquidity pools by depositing tokens into the contract. This is what will be used to maintain the liquidity pool. The contract also automatically calculates the price of a swap and executes it with the swap function. You can track the price of tokens by calling the calculatePrice function on the contract, that way it’s able to return the current price of the tokens.

To test this contract, let us go to the Remix Online IDE. Remix Online IDE is a tool for developing, debugging, deploying and testing smart contracts. Open Remix and create a file under the contracts folder. We are literally copying the same smart contract code as above

From here navigate to the Solidity compiler tab to compile the code

On a successful compilation, Remix will give several options such as publishing on ipfs or Swarm.

We don’t need to do those so we can move to the next tab and deploy our contract.

Click on the Deploy button

The first test we will do is create the liquidity pool

We will input the token addresses of tokenA and tokenB as well as the amount of the tokens. You can get test addresses from Remix from Account, so just navigate, copy and paste any two addresses



Click on transact and observe your output

From the image above, decoded output tells us how much liquidity we have of which it is 1000

The second test we can do is the swap function

Fill in all inputs and observe the output.

The third test we can do is calculate the price of a token after getting the amounts of tokens in the pool and calculating the constant product.

Now, that we have ran couple of tests, let’s go back to our IDE and deploy our smart contract.

To deploy your contract, write a deploy script in the deploy folder

const hre = require("hardhat");

const main = async () => {
  const contractFactory = await hre.ethers.getContractFactory('AmmSwap');
  const contract = await contractFactory.deploy();
  await contract.deployed();
  console.log("Contract deployed to this address:", contract.address);
  
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();

You can now head back to your terminal to run the following command

npx hardhat --network alfajores run deploy/deploy.js

Your output should look similar to this

Contract deployed to this address: 0x3E73365764fBBE4f58Eb0B6a62CA80FC706D851B

The next process is to verify your contract address with this command

npx hardhat --network alfajores verify 0x3E73365764fBBE4f58Eb0B6a62CA80FC706D851B

Finally, your output should look like this after the verification

Nothing to compile
Successfully submitted source code for contract
contracts/AmmSwap.sol:AmmSwap at 0x3E73365764fBBE4f58Eb0B6a62CA80FC706D851B
for verification on the block explorer. Waiting for verification result...

Successfully verified contract AmmSwap on Etherscan.
https://alfajores.celoscan.io/address/0x3E73365764fBBE4f58Eb0B6a62CA80FC706D851B#code

Conclusion

I hope you enjoyed reading this article. Here is a recap of what you learnt in this article

:heavy_check_mark: Overview of AMMs, basic terms and process flow
:heavy_check_mark: Understanding the constant product formula (x * y = k)
:heavy_check_mark: Using Remix IDE and its features
:heavy_check_mark: Implementing the AMM functionality using Solidity
:heavy_check_mark: Testing the functionality of the AMM contract using Remix
:heavy_check_mark: Deploying of the AMM contract via Celo composer

If you are interested in seeing this getting implemented from a UI point of view, feel free to read my next article here

1 Like

Still trying to understand my way around… Do I reply this with my article already? I thought this topic was approved on Trello and then got Migrated?

1 Like

@busayo edit change the whole topic with the pencil like icon here and change the tag to in progress then write your whole thing there
if you dont have access,then @joenyzio has to attribute this topic to you

1 Like

Thank you Gideon. @ngideon538

1 Like

my guess is your account here wasnt set up at the time he wanted to dedicate it to you

1 Like

@busayo I made you owner and you should have access now. @ngideon538 thanks for the help that’s what happened here :slight_smile:

3 Likes

I will do a review for

1 Like

Thanks. It’s actually in two parts. The second part is here Building an Automated Market Maker (AMM) on the Celo Blockchain II

Thank you for this

Sorry in advance for the review, it will be done by @ishan.pathak2711 including the part 2 that you made

3 Likes

I have removed the first paragraph for you for this time make sure you don’t forgot that in future projects :smiley:
@busayo

2 Likes

:sweat_smile: Oops it did escape my mind. One question though. That was more of the proposal paragraph. How do we keep track of the proposal from initial stage to approved and merge stage?

1 Like

After you make the proposal, you will get message on your card after it has been approved, after that you can start working on that, and then you will have to move it review section and then same process which we are going through
@busayo

4 Likes

You can go back through revision history to check and on the first revision you’ll see the proposal :slight_smile:

2 Likes