Exchange peer-to-peer

Description:

A simple exemple to create a smart contract who can exchange cryptos peer-to-peer (user to user) for a decentralized exchange.
On Etherscan click on "read contract" to available exchange, and how much they are.
Still on Etherscan, you can click on "write contract" to create an offer, cancel and order, or fill an order.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title DecentralizedExchange
/// @notice Permet aux utilisateurs de créer et remplir des offres d’échange entre tokens ERC20.
contract DecentralizedExchange is ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 public orderCounter;

    struct Order {
        uint256 id;                // Identifiant unique de l'ordre
        address maker;             // Adresse du créateur de l'ordre
        address tokenOffered;      // Adresse du token proposé par le maker
        uint256 amountOffered;     // Quantité de tokens offerts
        address tokenRequested;    // Adresse du token demandé en échange
        uint256 amountRequested;   // Quantité de tokens demandés
        uint256 expirationTime;    // Timestamp d'expiration de l'ordre
        bool active;               // Indique si l'ordre est toujours actif
    }

    // Mapping des ordres par identifiant
    mapping(uint256 => Order) public orders;

    // Événements pour le suivi des actions
    event OrderCreated(
        uint256 indexed id,
        address indexed maker,
        address tokenOffered,
        uint256 amountOffered,
        address tokenRequested,
        uint256 amountRequested,
        uint256 expirationTime
    );
    event OrderCancelled(uint256 indexed id, address indexed maker);
    event OrderFilled(uint256 indexed id, address indexed maker, address indexed filler);

    /// @notice Crée une nouvelle offre d'échange
    /// @param _tokenOffered L'adresse du token que le maker offre (ex : WETH)
    /// @param _amountOffered La quantité de tokens offerts
    /// @param _tokenRequested L'adresse du token que le maker souhaite obtenir (ex : USDT)
    /// @param _amountRequested La quantité de tokens demandés en échange
    /// @param _expirationTime Timestamp d'expiration de l'ordre (doit être supérieur au timestamp courant)
    /// @return L'identifiant de l'ordre créé
    ///
    /// Avant d'appeler cette fonction, le maker doit avoir approuvé le transfert de _amountOffered de _tokenOffered vers ce contrat.
    function createOrder(
        address _tokenOffered,
        uint256 _amountOffered,
        address _tokenRequested,
        uint256 _amountRequested,
        uint256 _expirationTime
    ) external returns (uint256) {
        require(_tokenOffered != address(0) && _tokenRequested != address(0), "Adresse token invalide");
        require(_amountOffered > 0 && _amountRequested > 0, "Les montants doivent etre superieurs a zero");
        require(_expirationTime > block.timestamp, "Expiration invalide");

        // Transfert des tokens offerts depuis le maker vers le contrat (mise en escrow)
        IERC20(_tokenOffered).safeTransferFrom(msg.sender, address(this), _amountOffered);

        orderCounter++;
        orders[orderCounter] = Order({
            id: orderCounter,
            maker: msg.sender,
            tokenOffered: _tokenOffered,
            amountOffered: _amountOffered,
            tokenRequested: _tokenRequested,
            amountRequested: _amountRequested,
            expirationTime: _expirationTime,
            active: true
        });

        emit OrderCreated(orderCounter, msg.sender, _tokenOffered, _amountOffered, _tokenRequested, _amountRequested, _expirationTime);
        return orderCounter;
    }

    /// @notice Annule un ordre actif. Seul le maker peut annuler son propre ordre.
    /// @param _orderId L'identifiant de l'ordre à annuler
    function cancelOrder(uint256 _orderId) external nonReentrant {
        Order storage order = orders[_orderId];
        require(order.active, "L'ordre n'est pas actif");
        require(order.maker == msg.sender, "Seul le createur peut annuler");

        order.active = false;
        // Retour des tokens offerts au maker
        IERC20(order.tokenOffered).safeTransfer(order.maker, order.amountOffered);
        emit OrderCancelled(_orderId, msg.sender);
    }

    /// @notice Permet à un utilisateur de remplir un ordre actif.
    /// @param _orderId L'identifiant de l'ordre à remplir
    ///
    /// Le filler doit avoir approuvé le transfert de _amountRequested de _tokenRequested vers le maker.
    function fillOrder(uint256 _orderId) external nonReentrant {
        Order storage order = orders[_orderId];
        require(order.active, "L'ordre n'est pas actif");
        require(block.timestamp < order.expirationTime, "L'ordre a expire");

        // Marquer l'ordre comme inactif avant les transferts pour éviter les attaques de réentrance
        order.active = false;

        // Transfert des tokens demandés depuis le filler vers le maker
        IERC20(order.tokenRequested).safeTransferFrom(msg.sender, order.maker, order.amountRequested);

        // Transfert des tokens offerts depuis le contrat vers le filler
        IERC20(order.tokenOffered).safeTransfer(msg.sender, order.amountOffered);

        emit OrderFilled(_orderId, order.maker, msg.sender);
    }

    /// @notice Permet à quiconque d'expirer un ordre et de retourner les tokens au maker si l'ordre est expiré.
    /// @param _orderId L'identifiant de l'ordre à expirer
    function expireOrder(uint256 _orderId) external nonReentrant {
        Order storage order = orders[_orderId];
        require(order.active, "L'ordre n'est pas actif");
        require(block.timestamp >= order.expirationTime, "L'ordre n'est pas encore expire");

        order.active = false;
        // Retour des tokens offerts au maker
        IERC20(order.tokenOffered).safeTransfer(order.maker, order.amountOffered);
        emit OrderCancelled(_orderId, order.maker);
    }
}

Tags:
Exchange, decentralized, peer-to-peer, token, coin

Submitted on: 2025-04-02 16:22:12

Comments

Log in to comment.

No comments yet.