DF-20200203

DeFi Intermediate February 3, 2020

Back to All Tasks

Problem Statement

You are tasked with developing a simple decentralized liquidity pool for an ERC20 token called "CryptoCoin". The liquidity pool will allow users to deposit and withdraw their CryptoCoins in exchange for another ERC20 token, named "StableToken", at a rate determined by the ratio of deposited tokens. Your smart contract should include functions to add liquidity (deposit both tokens), remove liquidity (withdraw both tokens based on share ownership), and swap tokens (convert one token into another using the current pool ratio). Ensure that your implementation is gas-efficient and fair to all users, preventing manipulation through techniques like front-running.

Concepts

  • smart contracts
  • ERC20 tokens
  • liquidity pools

Constraints

  • The smart contract must be written in Solidity version 0.8.x.
  • Use SafeMath for arithmetic operations.
  • Implement proper access control to restrict certain functions to only the contract owner.

Security Notes

  • Avoid reentrancy vulnerabilities, especially in the swap and liquidity withdrawal functions.
  • Consider flash loan attacks and implement safeguards if necessary.
  • Ensure that the pool ratio is always updated correctly after each transaction to prevent price manipulation.

Solutions

Cpp Solution

// FAILED TO PARSE JSON RESPONSE
// Raw output preserved below (may be incomplete):
{
  "code": "pragma solidity ^0.8.0;\n\nimport \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\";\nimport \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\";\nimport \\\"@openzeppelin/contracts/access/Ownable.sol\\";\n\ncontract CryptoCoinStableTokenPool is Ownable {\n    using SafeMath for uint256;\n\n    IERC20 public cryptoCoin;\n    IERC20 public stableToken;\n\n    uint256 public totalLiquidity;\n    mapping(address => uint256) public liquidityShares;\n\n    event LiquidityAdded(address indexed user, uint256 cryptoAmount, uint256 stableAmount);\n    event LiquidityRemoved(address indexed user, uint256 cryptoAmount, uint256 stableAmount);\n    event TokensSwapped(address indexed user, address indexed fromToken, address indexed toToken, uint256 amountIn, uint256 amountOut);\n\n    constructor(IERC20 _cryptoCoin, IERC20 _stableToken) {\n        cryptoCoin = _cryptoCoin;\n        stableToken = _stableToken;\n    }\n\n    // Adds liquidity to the pool by depositing both CryptoCoin and StableToken.\n    function addLiquidity(uint256 _cryptoAmount, uint256 _stableAmount) external {\n        require(_cryptoAmount > 0 && _stableAmount > 0, \\\"Amounts must be greater than zero\\);\n\n        cryptoCoin.transferFrom(msg.sender, address(this), _cryptoAmount);\n        stableToken.transferFrom(msg.sender, address(this), _stableAmount);\n\n        uint256 newLiquidity = (_cryptoAmount.add(_stableAmount)).div(2);\n        liquidityShares[msg.sender] = liquidityShares[msg.sender].add(newLiquidity);\n        totalLiquidity = totalLiquidity.add(newLiquidity);\n\n        emit LiquidityAdded(msg.sender, _cryptoAmount, _stableAmount);\n    }\n\n    // Removes liquidity from the pool based on share ownership.\n    function removeLiquidity(uint256 _share) external {\n        require(_share > 0 && _share <= liquidityShares[msg.sender], \\\"Invalid share amount\\);\n\n        uint256 cryptoAmount = (cryptoCoin.balanceOf(address(this)).mul(_share)).div(totalLiquidity);\n        uint256 stableAmount = (stableToken.balanceOf(address(this)).mul(_share)).div(totalLiquidity);\n\n        liquidityShares[msg.sender] = liquidityShares[msg.sender].sub(_share);\n        totalLiquidity = totalLiquidity.sub(_share);\n\n        cryptoCoin.transfer(msg.sender, cryptoAmount);\n        stableToken.transfer(msg.sender, stableAmount);\n\n        emit LiquidityRemoved(msg.sender, cryptoAmount, stableAmount);\n    }\n\n    // Swaps one token for another using the current pool ratio.\n    function swapTokens(IERC20 _fromToken, IERC20 _toToken, uint256 _amountIn) external {\n        require(_amountIn > 0, \\\"Swap amount must be greater than zero\\);\n        require(address(_fromToken) == address(cryptoCoin) || address(_fromToken) == address(stableToken), \\\"Invalid from token\\);\n        require(address(_toToken) == address(cryptoCoin) || address(_toToken) == address(stableToken), \\\"Invalid to token\\);\n        require(address(_fromToken) != address(_toToken), \\\"Cannot swap the same token\\);\n\n        uint256 balanceIn = _fromToken.balanceOf(address(this));\n        uint256 balanceOut = _toToken.balanceOf(address(this));\n        uint256 amountOut = (_amountIn.mul(balanceOut)).div(balanceIn.add(_amountIn));\n\n        _fromToken.transferFrom(msg.sender, address(this), _amountIn);\n        _toToken.transfer(msg.sender, amountOut);\n\n        emit TokensSwapped(msg.sender, address(_fromToken), address(_toToken), _amountIn, amountOut);\n    }\n}",
  "explanation": "This Solidity smart contract implements a simple decentralized liquidity pool for two ERC20 tokens: CryptoCoin and StableToken. The contract inherits from the Ownable contract to enable access control, ensuring that certain functions can only be executed by the contract owner.\nThe contract uses SafeMath for all arithmetic operations to prevent overflow and underflow issues, enhancing security.\nThree main functionalities are provided:\n1. addLiquidity: Allows users to deposit both CryptoCoin and StableToken into the pool in equal amounts (for simplicity). The deposited tokens are added to the contract's balance, and liquidity shares are issued to the user based on their contribution.\n2. removeLiquidity: Users can withdraw their share of both CryptoCoin and StableToken from the pool using their liquidity shares. This ensures that users receive a fair portion of the tokens based on their investment.\n3. swapTokens: Enables swapping one token for another at the current ratio determined by the pool's balances. The function calculates the output amount based on the input amount and the current balance ratio, transferring the calculated amount to the user.\nThe contract emits events for liquidity addition, removal, and token swaps to provide transparency and facilitate off-chain monitoring.\nSecurity measures include preventing reentrancy attacks by structuring functions to avoid calling external contracts before updating internal states. Additionally, the contract ensures that the pool ratio is always updated correctly after each transaction, preventing price manipulation."
}

The model produced malformed JSON. Raw response preserved in code field for manual review.