Search code examples
ethereumsoliditysmartcontractschainlinkdecentralized-applications

How to send correctly ETH from the manager's deposit to user if an event occurs?


I have this smart contract that I am trying to test. The manager deposits a certain amount of ether. Users can use the vault to earn extra ethers by locking own ether. Users deposit the amount of ether they want (ex 0.10 ETH) and set the seconds for locking. When users deposit, the price of ethereum is recorded via chainlink. If at the end of the locking period the price of ethereum is less than 2000$ the user receives the amount of locked ETH (0.10 ETH) + 2x (0.20ETH) the amount of locked ethers. The extra ethers are taken from the manager's deposit.

The code seems to work fine and the console returns no errors. I am testing the smart contract on the Kovan network. The problem is encountered when the user tries to withdraw the ethereums. When he withdraws, only the deposited ones are returned without the extra ethers being added.

I am new to Solidity so I appreciate any criticism or advice. If there is something already existing similar to what I am trying to create please let me know.

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


import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";



interface EACAggregatorProxy {
    function latestAnswer() external view returns (int256);
}

contract oracleLink {

    address public manager;
    uint256 public managerDeposit;
    uint256[] public managerDeposits;

    constructor() payable {
        manager = msg.sender;
        managerDeposit = msg.value;
        managerDeposits.push(managerDeposit);
    }

    function depositVault() public payable {
        require(msg.sender == manager);
    }

    address public user;
    uint256 public userContribution;
    uint256 public userCount;                   

    uint256 deadline;

    uint256 lockAmount = lockAmounts[msg.sender];      
    mapping(address => uint) lockAmounts;

    uint256 startTime = startTimes[block.timestamp];  
    mapping(uint => uint) startTimes; 
 
    address public chainLinkETHUSDAddress = 0x9326BFA02ADD2366b30bacB125260Af641031331;

    uint public ethPrice = 0; 
    uint256 public price = ethPrice;

    function deposit(uint256 numberOfSeconds) public payable {

        lockAmounts[msg.sender] = msg.value;
        startTimes[block.timestamp] = block.timestamp;
        
        user = msg.sender;           
        userContribution = msg.value;
        userCount++;

        deadline = block.timestamp + (numberOfSeconds * 1 seconds);

        int256 chainLinkEthPrice = EACAggregatorProxy(chainLinkETHUSDAddress).latestAnswer();
        ethPrice = uint(chainLinkEthPrice / 100000000);
    }

    function withdraw() public payable {    


        if (ethPrice <= 2000) {
            uint256 toWithdraw = lockAmounts[msg.sender];
            uint256 amountOfToken = toWithdraw * 2;
            payable(manager).transfer(amountOfToken);
        }

        require(block.timestamp >= deadline);
        uint256 amountToWithdraw = lockAmounts[msg.sender];
        lockAmounts[msg.sender] = 0; 
        payable(msg.sender).transfer(amountToWithdraw); 
    }
}

Solution

  • So the one thing that I noticed is that when you try to send the additional ETH to the recipient's address you're actually trying to send it back to the manager and not the recipient. Also note that you should avoid using the transfer method and use the call method instead: call{value: amount}("") should now be used for transferring ether (Do not use send or transfer.) as of May 2021.

    So your withdraw method should look something like this instead:

        function withdraw() public payable {    
            address recipient = msg.sender;
            uint256 additionalToken;
    
            if (ethPrice <= 2000) {    
                additionalToken = lockAmounts[msg.sender] * 2;
            }
    
            require(block.timestamp >= deadline);
            uint256 amountToWithdraw = lockAmounts[msg.sender] + additionalToken;
            lockAmounts[msg.sender] = 0; 
            (bool success, ) = payable(recipient).call{value: amountToWithdraw}("");
            require(success, "Transfer failed."); 
        }
    

    Hopefully this helps.