Return On Investment and smart contracts - Part 1

Creating ROI smart contract from the decentralized finance space

solidity

Aniket Savji

@ankasaw99
496 4 5

ROI Smart Contract


Defi is currently very hot  category of the dapp space . We will build a ROI platform in this series.   

So in this part we will be building following important functions, 

  • Invest Function
  • Profit incremental per second function
  • Withdraw function

Contract Declaration

Let's fitst create a contract and declare the compiler version 

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

contract invest {
 ...
}

Variables Declaration

Now lets create a struct for user containing and map it with user's address  which will be like this ,

contract invest {

    struct User {
        uint256 invested_amount;
        uint256 profit;
        uint256 profit_withdrawn;
        uint256 start_time;
        uint256 exp_time;
        bool time_started;
    }

    mapping(address => User) public invest_map;
}

Here we took 6 variables in User struct , each one of the variable's use will be discussed later as we write down our invest and other function .

1. Invest Function 

For our return on invest let's give user return of  0.1% per day for 30 days 

function invest_fun() public payable {
        require(msg.value >= 0, "Please Enter Amount more than 0");
        if (invest_map[msg.sender].time_started == false) {
            invest_map[msg.sender].start_time = block.timestamp;
            invest_map[msg.sender].time_started = true;
            invest_map[msg.sender].exp_time = block.timestamp + 30 days;
        }
        invest_map[msg.sender].invested_amount += msg.value;
        invest_map[msg.sender].profit += ( (msg.value * 1 * 30 ) / (1000));
}

In this function we are first checking for non-zero value with require.

If time_started  , which takes note of ongoing roi plans for user , is false then user can invest in new ROI plan else it will just incement the ongoing ROI plan .

Then we start toggle the time_started  to true , thus user has enrolled in a plan of ROI and we then note the current time for the investment with variable start_time with block.timestamp. 

Then we initialise expiry time as well for the user with exp_time variable to block.timestamp + 30 days , which are solidity variables. Here 30 days will be in timestamp format .

After that we just save the invesed amount in the invested_amount variable and add all the 30 days profit to profit variable.

So here user's overall profit over the period is stored in the profit variable, here we divided by 1000 because solidity don't support float type values so we divided 1% by 10 thus getting 1000.

Now let's create profit incremental function ,

2. Profit Function

function current_profit() public view returns (uint256) {
    uint256 local_profit;
    if (block.timestamp <= invest_map[msg.sender].exp_time) {
        if ( (((invest_map[msg.sender].profit + invest_map[msg.sender].profit_withdrawn) * (block.timestamp - invest_map[msg.sender].start_time)) / (30 * (1 days))) > invest_map[msg.sender].profit_withdrawn ) {
            local_profit = (((invest_map[msg.sender].profit + invest_map[msg.sender].profit_withdrawn) * (block.timestamp - invest_map[msg.sender].start_time)) / (30 * (1 days))) - invest_map[msg.sender].profit_withdrawn; 
            return local_profit;
        } else {
            return 0;
        }
    }
    if (block.timestamp > invest_map[msg.sender].exp_time) {
        return invest_map[msg.sender].profit;
    }
}

Function will return the profit generated from invested time to the time of calling this function , it varies per second thus we can get the profit per second for the invested amount .
First if() block will check for expiry time for the investment period , if time past the expiry time it will not calculate the profit for that instant.
Second if() block will return profit we first saved at the time of investment if current time is past the expiry time.

Here we create a local variable to return current profit for that instant like, 

  • Multiplying whole profit we first saved in the invest function by seconds passed from the time of the investment.
  • Dividing by all the seconds in the time period i.e. 30 * 86400(seconds in a day)

As this is a view function it can be called without any fees and is very fast. Thus we are calculating per second incremental profit with this method .

3. Withdraw Function 

function withdraw_profit() public payable returns(bool){
        uint256 current_profit = current_profit();
        invest_map[msg.sender].profit_withdrawn = invest_map[msg.sender].profit_withdrawn + current_profit;
        invest_map[msg.sender].profit = invest_map[msg.sender].profit - current_profit;
        payable(msg.sender).transfer(current_profit);
        return true;
}

So here we call  current_profit() function from before , it will only return the profit till  time of calling which varies per second then We add that into profit_withdrawn  variable and remove current_profit from profit we saved in invest function.  Then transfer of current_profit amount to the user is done with transfer() function which is solidity function .

In the next part we will add some more things into this contract to make is more secure and more useful with other functionalities .

here is the full code ,

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

contract invest {

    struct User {
        uint256 invested_amount;
        uint256 profit;
        uint256 profit_withdrawn;
        uint256 start_time;
        uint256 exp_time;
        bool time_started;
    }

    mapping(address => User) public invest_map;

    function invest_fun() public payable {
        require(msg.value >= 0, "Please Enter Amount more than 0");
        if (invest_map[msg.sender].time_started == false) {
            invest_map[msg.sender].start_time = block.timestamp;
            invest_map[msg.sender].time_started = true;
            invest_map[msg.sender].exp_time = block.timestamp + 30 days;
        }
        invest_map[msg.sender].invested_amount += msg.value;
        invest_map[msg.sender].profit += ( (msg.value * 1 * 30 ) / (1000));
    }

    function current_profit() public view returns (uint256) {
        uint256 local_profit;
        if (block.timestamp <= invest_map[msg.sender].exp_time) {
            if ( (((invest_map[msg.sender].profit + invest_map[msg.sender].profit_withdrawn) * (block.timestamp - invest_map[msg.sender].start_time)) / (30 * (1 days))) > invest_map[msg.sender].profit_withdrawn ) {
            local_profit = (((invest_map[msg.sender].profit + invest_map[msg.sender].profit_withdrawn) * (block.timestamp - invest_map[msg.sender].start_time)) / (30 * (1 days))) - invest_map[msg.sender].profit_withdrawn; 
            return local_profit;
            } else {
                return 0;
            }
        }
        if (block.timestamp > invest_map[msg.sender].exp_time) {
            return invest_map[msg.sender].profit;
        }
    }

    function withdraw_profit() public payable returns(bool){
        uint256 current_profit = current_profit();
        invest_map[msg.sender].profit_withdrawn = invest_map[msg.sender].profit_withdrawn + current_profit;
        invest_map[msg.sender].profit = invest_map[msg.sender].profit - current_profit;
        payable(msg.sender).transfer(current_profit);
        return true;
    }
}

More SmartBooks like this