0

Code of contract followed by an explanation and requirements. Everything works but trade function.

/**
 *Submitted for verification at Etherscan.io on 2023-05-16
*/

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;

// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract P2PDEX {
    struct Listing {
        address payable user;
        address tokenToTransfer;
        uint256 amountToTransfer;
        address tokenDesired;
        uint256 amountDesired;
    }

    uint256 public transactionFee; // Fee percentage (1% represented as 100)
    mapping(address => uint256) public tokenFees; // Fee amount per token
    address public owner; // Owner's address

    Listing[] public listings;
    
    mapping(address => uint256) private allowed;

    event NewListing(uint256 indexed listingId, address indexed user, address indexed tokenToTransfer, uint256 amountToTransfer, address tokenDesired, uint256 amountDesired);
event TradeComplete(uint256 indexed listingId, address indexed buyer, uint256 amountSold, address indexed seller, uint256 amountBought);

constructor() {
    transactionFee = 100; // Set initial transaction fee to 1%
    owner = msg.sender; // Set contract deployer as the owner
}

modifier onlyOwner() {
    require(msg.sender == owner, "Only the owner can call this function");
    _;
}

function createListing(address _tokenToTransfer, uint256 _amountToTransfer, address _tokenDesired, uint256 _amountDesired) public {
    Listing memory newListing = Listing({
        user: payable(msg.sender),
        tokenToTransfer: _tokenToTransfer,
        amountToTransfer: _amountToTransfer,
        tokenDesired: _tokenDesired,
        amountDesired: _amountDesired
    });

    listings.push(newListing);

    emit NewListing(listings.length - 1, msg.sender, _tokenToTransfer, _amountToTransfer, _tokenDesired, _amountDesired);
}

function trade(uint256 _listingId) public {
    Listing storage listing = listings[_listingId];

    uint256 feeAmountToTransfer = (listing.amountToTransfer * transactionFee) / 10000; // Calculate the fee amount for tokenToTransfer
    uint256 feeAmountDesired = (listing.amountDesired * transactionFee) / 10000; // Calculate the fee amount for tokenDesired

    uint256 totalAmountToTransfer = listing.amountToTransfer - feeAmountToTransfer; // Add fee to the amount being transferred
    uint256 totalAmountDesired = listing.amountDesired - feeAmountDesired; // Add fee to the amount being transferred

    require(IERC20(listing.tokenToTransfer).transferFrom(listing.user, address(this), listing.amountToTransfer), "Token transfer failed");
    require(IERC20(listing.tokenDesired).transferFrom(msg.sender, address(this), listing.amountDesired), "Token transfer failed");

    // Transfer fee amounts to owner's wallet
    require(IERC20(listing.tokenToTransfer).transferFrom(listing.user, owner, feeAmountToTransfer), "Token transfer failed");
    require(IERC20(listing.tokenDesired).transferFrom(msg.sender, owner, feeAmountDesired), "Token transfer failed");

    // Transfer tokens to the user
    require(IERC20(listing.tokenToTransfer).transfer(msg.sender, totalAmountToTransfer), "Token transfer failed");
    require(IERC20(listing.tokenDesired).transfer(listing.user, totalAmountDesired), "Token transfer failed");

    delete listings[_listingId];

    emit TradeComplete(_listingId, msg.sender, totalAmountToTransfer, listing.user, totalAmountDesired);
}

function getListingCount() public view returns (uint256) {
    return listings.length;
}

function setTransactionFee(uint256 _transactionFee) external onlyOwner {
    require(_transactionFee <= 10000, "Invalid fee"); // Fee should be between 0% and 100%
    transactionFee = _transactionFee;
}

function setTokenApproval(address _token, uint256 _amount) external {
    require(_amount > 0, "Invalid approval amount");
    IERC20(_token).approve(address(this), _amount);
}
}

The contract creates a listing array of possible trades users can do. a user can either create a listing or call the trade function on an existing listing. The logic is - user A creates a listing specifying tokens they want to transfer with amount and tokens they want in return with amount against the same. this listing is created in createListing function. UserA approval for token to transfer should be taken here as user A doesn't interact again with the contract.

A user B calls the trade function where they provide the token desired by user A in their listing. The trade function swaps the tokens defined in the listing between userA and B in the amount specified and it also transfers a certain amount of these tokens in the owner wallet as transaction fee. Now i want to fix the logic in this contract and make sure the transaction does not break anywhere.

Kindly let me know what I am missing here to have end to end flow. Thanks!

1 Answer 1

0

trying openzepplin libraries like safeMath to perform calculations will limit the error rate in calculations, safeErc20 library will allow you to use functions like safeTransferFrom to transfer tokens between addresses and change the modifier on the trade function from public to exeternal

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.