Cryptocurrencies have revolutionized the way we transact and exchange value. One fascinating aspect of this ecosystem is the creation of decentralized exchanges, which operate through smart contracts , the ability to create and manage orders is paramount.
This is your ultimate guide to understand working of a Cryptocurrency Exchange and get a overview of how to design a smart contract for the requirements.
Let's dive into the workings of a cryptocurrency exchange smart contract written in Solidity that I created for my CryptoEx Project which you can find on my github.
Link to the Smart Contract.
Link to CryptoEx Project.
The Basics
The contract begins by setting the licensing information and importing required modules. It introduces variables like feeAccount
and feePercent
, responsible for managing transaction fees. The contract tracks token balances, orders, and trade-related events.
The heart of this contract lies in its ability to manage transaction fees, track token balances, orders, and trade events for which it establishes mappings to track token balances, orders, and their states.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "../node_modules/hardhat/console.sol";
import "./Token.sol";
contract Exchange {
address public feeAccount;
uint256 public feePercent;
mapping (address => mapping(address => uint256)) public tokens;
mapping (uint256 => _Order) public orders;
mapping (uint256 => bool) public orderCancelled;
mapping (uint256 => bool) public ordersFilled;
uint256 orderCount;
// Events for user interactions
event Deposit(address token , address user , uint256 amount , uint256 balance);
event Withdraw(address token , address user , uint256 amount , uint256 balance);
event Order(uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp);
event Trade(uint256 id ,
address user ,
address tokenGet ,
uint256 amountGet ,
address tokenGive ,
uint256 amountGive,
address creator,
uint256 timestamp
);
// Struct to define the order structure
struct _Order {
uint256 id;
address user;
address tokenGet;
uint256 amountGet;
address tokenGive;
uint256 amountGive;
uint256 timestamp;
}
// Constructor initializes the contract with fee details
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
Deposit and Withdrawal
Deposit
The depositToken
function enables users to deposit tokens into the exchange. It utilizes the transferFrom
function to move tokens from the user's address to the contract. The user's balance is then updated, and an event is emitted.
// DEPOSIT TOKENS
function depositToken(address _token , uint256 _amount) public {
// Transfer tokens to exchange
require(Token(_token).transferFrom(msg.sender , address(this) , _amount));
// Update User's Balance
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
// Emit an event
emit Deposit(_token, msg.sender , _amount, tokens[_token][msg.sender]);
}
Withdrawal
The withdrawToken
function allows users to withdraw tokens. It transfers tokens from the contract back to the user and updates their balance accordingly.
// WITHDRAW TOKENS
function withdrawToken(address _token, uint256 _amount) public {
// Transfer tokens to the user
Token(_token).transfer(msg.sender , _amount);
// Update User's Balance
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// Emit a Withdrawal Event
emit Withdraw(_token, msg.sender , _amount, tokens[_token][msg.sender]);
}
Token Balances
function balanceOf(address _token , address _user) public view returns (uint256) {
return tokens[_token][_user];
}
The balanceOf
function serves as a transparency tool, allowing users to check their token balances on the exchange.
Creating Orders
The Token Give and Token Get
When users decide to make an order, they are essentially expressing their desire to trade one token for another. In the smart contract, this is represented by the makeOrder
function. Here, users specify the token they want to receive (Token Get
) and the token they are willing to spend (Token Give
).
solidityCopy code// MAKE AN ORDER
function makeOrder(
address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive
) public {
// Ensure the user has enough tokens to fulfill the order
require(balanceOf(_tokenGive, msg.sender) >= _amountGive);
// Increment the order count
orderCount = orderCount + 1;
// Create and store the order
orders[orderCount] = _Order(
orderCount,
msg.sender,
_tokenGet,
_amountGet,
_tokenGive,
_amountGive,
block.timestamp
);
// Order creation event
emit Order(
orderCount,
msg.sender,
_tokenGet,
_amountGet,
_tokenGive,
_amountGive,
block.timestamp
);
}
In essence, a user is putting forward an order saying, "I want to receive a certain amount of Token Get and I'm willing to give a specific amount of Token Give."
Order Cancellation
The Power to Revoke
No transaction system is complete without a way to cancel or revoke an order. In our decentralized exchange smart contract, the cancelOrder
function allows users to do just that. Users can cancel an order they've placed under specific conditions.
solidityCopy code// CANCEL AN ORDER
function cancelOrder(uint256 _id) public {
// Fetch the order to be canceled
_Order storage _order = orders[_id];
// Ensure the order exists
require(_order.id == _id);
// Mark the order as cancelled
orderCancelled[_id] = true;
}
Users can revoke an order by providing its unique identifier (_id
). The contract then checks if the order exists and sets the orderCancelled
status to true, indicating that this order should no longer be considered for execution.
Filling Orders
The Thrill of the Trade
When it comes to executing a trade, the fillOrder
function plays a crucial role. This function checks several conditions before confirming a trade.
solidityCopy code// FILL AN ORDER
function fillOrder(uint256 _id) public {
// Conditions to make a trade
// 1. Must be a valid order id
// 2. Order must not be filled
// 3. Order must not be cancelled
require(_id > 0 && _id <= orderCount, "Order does not exist");
require(!ordersFilled[_id]);
require(!orderCancelled[_id]);
// Fetch the order to be filled
_Order storage _order = orders[_id];
// Execute the trade
trade(
_order.id,
_order.user,
_order.tokenGet,
_order.amountGet,
_order.tokenGive,
_order.amountGive
);
// Mark the order as filled
ordersFilled[_order.id] = true;
}
This function ensures that the order is valid, not already filled, and not canceled before proceeding with the trade. If all conditions are met, the trade
function is called to execute the trade, and the order is marked as filled.
Internal Trade Logic
The Engine Room
The trade
function handles the intricate details of the trade, from calculating fees to updating token balances between users and the exchange.
solidityCopy code// INTERNAL TRADE FUNCTION
function trade(
uint256 _id,
address user,
address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive
) internal {
// Calculate the fee amount
uint256 feeAmount = (_amountGet * feePercent) / 100;
// Swap tokens between exchange, user, and fee account
tokens[_tokenGet][msg.sender] = tokens[_tokenGet][msg.sender] - (_amountGet + feeAmount);
tokens[_tokenGet][user] = tokens[_tokenGet][user] + _amountGet;
tokens[_tokenGet][feeAccount] = tokens[_tokenGet][feeAccount] + feeAmount;
// Swap tokens for the other side of the trade
tokens[_tokenGive][user] = tokens[_tokenGive][user] - _amountGive;
tokens[_tokenGive][msg.sender] = tokens[_tokenGive][msg.sender] + _amountGive;
// Emit a Trade Event
emit Trade(
_id,
msg.sender,
_tokenGet,
_amountGet,
_tokenGive,
_amountGive,
user,
block.timestamp
);
}
This function calculates the transaction fee, swaps tokens between users and the exchange, and emits a trade event for transparency.
This smart contract automates the trading process on a decentralized exchange. Users can deposit, withdraw, create, and fill orders, all managed by a secure and transparent codebase. The contract provides a foundation for decentralized exchanges, eliminating the need for intermediaries and enhancing the efficiency of peer-to-peer transactions within the cryptocurrency ecosystem.
Understanding such smart contracts is crucial for anyone venturing into the world of decentralized finance, providing insights into the mechanics that power these innovative financial systems.