0

When I call the function giveFifteenthTokens in my contract problem2_bank, it does not transfer tokens from msg.sender to recipient. Why?

My Files:

LoanToken.sol

pragma solidity ^0.5.8;

import "browser/ERC20.sol";

contract LoanToken {
    // Data of  the Token
    uint private _totalSupply = 1000;
    string public name = "LoanToken";
    string public symbol = "LT";
    uint8 public decimals = 0;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    mapping (address => uint) public _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    constructor() public {
        _balances[msg.sender] = totalSupply();
        emit Transfer(address(0), msg.sender, totalSupply());
    }

    // total amount of tokens which can be spent, when this limit will be reached, the smart contract will refuse to create new tokens
    function totalSupply() public view returns (uint) {
        return _totalSupply;
    }

    // returns how many tokens specified address has
    function balanceOf(address _owner) public view returns (uint256) {
        return _balances[_owner];
    }

    // takes amount of tokens from msg.sender and send them to recipient
    function transfer(address recipient, uint256 amount) public returns (bool)
    {
        if (_balances[msg.sender] >= amount && amount > 0) {

            _balances[recipient] += amount;
            _balances[msg.sender] -= amount;

            emit Transfer(msg.sender, recipient, amount);

            return true;
        } else { return false; }
    }

    // returns the amount of tokens with which spender can still withdraw from owner.
    function allowance(address owner, address spender) public view returns (uint256) 
    {
        return _allowances[owner][spender];
    }

    // approved spender is able to withdraw his balance so much as he want, but not more that amount
    function approve(address spender, uint256 amount) public returns (bool)
    {
        _allowances[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    // transfer tokens from sender and send them to recipient
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool)
     {
        if (_balances[sender] >= amount && _allowances[sender][msg.sender] >= amount && amount > 0) {

            _balances[recipient] += amount;
            _balances[sender] -= amount;
            _allowances[sender][msg.sender] -= amount;

            emit Transfer(sender, recipient, amount);

            return true;
        } else { return false; }
    }
}

problem2_bank.sol

pragma solidity ^0.5.8;

import "browser/LoanToken.sol";

contract problem2_bank {
    address private _owner;
    LoanToken public loanToken;
    uint private loansCount = 0;
    uint private guaranteesCount = 0;
    mapping (uint => Loan) private loans;
    mapping (uint => Guarantee) private guarantees;
    mapping (uint => address payable) private lenders;

    constructor(address loanTokenAddress) public 
    {
        _owner = msg.sender;
        loanToken = LoanToken(loanTokenAddress);
    }

    // if(msg.sender == address(0)) 
    // msg.value, payable, transfer() <=== refactor it

    struct Loan {
        address payable loanee;
        uint etherBorrow;
        uint index;
        uint payBackDate;
        uint etherInterest;
        bool _isGuaranteeProvided;
        bool _isLoanProvided;
        bool _isLoanExist;
    }

    struct Guarantee {
        address payable guarantor;
        uint etherInterest;
        uint loanIndex;
        bool _isWaitingForHandling;
        bool _isGuaranteeExist;
    }

    function getAllowance(address spender) public view returns(uint)
    {
        return loanToken.allowance(msg.sender, spender);
    }

    function giveFifteenthTokens(address spender) public returns(bool)
    {
        require(msg.sender == _owner, "You are not owner of that smart contract");
        require(spender != _owner, "The owner of the smart contract can't receive tokens from himself");

        // require(
        //     loanToken.approve(spender, 15),
        //     "You don't receive approval from loanToken for that address on 15 LoanTokens");

        // require(
        //     loanToken.transfer(spender, 15),
        //     "Your transfer was failed");
        // loanToken.approve(spender, 15);
        loanToken.transfer(spender, 15);

        return true;
    }

    function getBalanceOfTokens(address account) public view returns(uint)
    {
        return loanToken.balanceOf(account);
    }

    function requestLoan(uint etherBorrow, uint8 payBackDate, uint8 etherInterest) public
    {
        Loan memory loan = Loan({loanee: msg.sender, index: loansCount, etherBorrow: etherBorrow,
                                        payBackDate: now + (payBackDate * 1 days), etherInterest: etherInterest,
                                        _isGuaranteeProvided: false, _isLoanProvided: false, _isLoanExist: true});
        loans[loansCount] = loan;
        loansCount++;
    }

    function provideGuarantee(uint index, uint8 guaranteeInterest) public // payable means that value should have ether
    {
        require(
            index < loansCount,
            "This index does not exist");

        require(loans[index].loanee != msg.sender,
                "The borrower can't provide a guarantee to himself");

        require(lenders[index] != msg.sender,
                "The lender can't provide guarantee for the loan");

        require(!loans[index]._isGuaranteeProvided, 
            "This loan already has a guarantee");

        require(guaranteesCount < loansCount || !guarantees[index]._isWaitingForHandling, 
            "This guarantee already waiting for handling of borrower");

        require(guaranteeInterest > 0, 
                "Too low interest");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

        require(
            !guarantees[index]._isGuaranteeExist,
            "This guarantee does exist");

        Guarantee memory guarantee = Guarantee({guarantor: msg.sender, etherInterest: guaranteeInterest,
                                                    loanIndex: index, _isWaitingForHandling: true, _isGuaranteeExist: true});
        guarantees[index] = guarantee;
        guaranteesCount++;
    }

    function handleGuarantee(uint index, bool isGuaranteeProvided) public
    {
        require(
            index < loansCount,
            "This index does not exist");

        require(loans[index].loanee == msg.sender, 
            "This is not your loan");

        require(guarantees[index]._isWaitingForHandling,
            "Guarantee already processed");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

        require(
            guarantees[index]._isGuaranteeExist,
            "This guarantee does not exist");

        loans[index]._isGuaranteeProvided = isGuaranteeProvided;
        guarantees[index]._isWaitingForHandling = false;

        if(!loans[index]._isGuaranteeProvided){
            guarantees[index].guarantor.transfer(loans[index].etherBorrow); // transfer eather from smart contract to guarantor back
            require(
                loanToken.approve(msg.sender, loans[index].etherBorrow),
                            "You don't receive approval from loanToken for this amount");

            require(
            loanToken.transferFrom(_owner, msg.sender, loans[index].etherBorrow),
                            "You don't receive tokens from loanToken for this amount");

            delete guarantees[index];
            guaranteesCount--;
        }
    }

    function getLoansInfo(uint index) public view returns(uint, bool, uint, address payable) {
        require(
            lenders[index] == msg.sender,
            "You are not a leander of this loan");

        require(
            index < loansCount,
            "This index does not exist");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

       return(loansCount,   // count of loans
                loans[index]._isGuaranteeProvided, // was the guarantee provided
                loans[index].etherBorrow * loans[index].etherInterest / 100,    // interest of loan, which should receive lender in ether
                guarantees[index].guarantor);   //  address of guarantor
    }

    function provideLoanForLoanee(uint index) public payable
    {
        require(
            lenders[index] == address(0),
            "The lender for this loan is already exist");

        require(
            loans[index].loanee != msg.sender,
            "You are not a lender");

        require(
            guarantees[index].guarantor != msg.sender,
            "You are not a lender");

        require(
            index < loansCount,
            "This index does not exist");

        require(
            loans[index]._isGuaranteeProvided,
            "This loan doesn't have a guarantee");

        require(
            !loans[index]._isLoanProvided,
            "This loan is already provided");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

        require(
            msg.value == loans[index].etherBorrow,
            "You don't have enough ether");

        require(
            loanToken.allowance(msg.sender, loans[index].loanee) == loans[index].etherBorrow,  // msg.value == loans[index].etherBorrow
            "You don't have enough ether");

        loans[index].loanee.transfer(loans[index].etherBorrow); // transfer eather from lender to loanee
        loans[index]._isLoanProvided = true;
        lenders[index] = msg.sender;
    }

    function isBorrowerTransferEtherAtTime(uint index) public 
    {
        require(
            lenders[index] != address(0),
            "Loan doesn't have a lender");

        require(
            lenders[index] == msg.sender,
            "You are not leander of this loan");

        require(
            index < loansCount,
            "This index does not exist");

        require(
            loans[index]._isLoanProvided,
            "This loan has not yet been provided");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

        // if should be executed, when borrower doesn't provide ether and interest at time
        if(loans[index].payBackDate * 1 days <= now){
            // lender receive back his ether(this amount from smart contract, which was locked)
            lenders[index].transfer(loans[index].etherBorrow); 

            // remove the loan
            delete loans[index];
            loansCount--;

            // remove the guarantee
            delete guarantees[index];
            guaranteesCount--;

            // remove the lender
            delete lenders[index];
        }
    }

    function payBackLoan(uint index) public payable {
        require(
            index < loansCount,
            "This index does not exist");

        require(loans[index].loanee == msg.sender, 
            "This is not your loan");

        require(
            loans[index]._isLoanProvided,
            "This loan was not provided");

        require(
            loans[index]._isLoanExist,
            "This loan does not exist");

        require(
            guarantees[index]._isGuaranteeExist,
            "This guarantee does not exist");

        uint amount = (loans[index].etherBorrow + loans[index].etherBorrow * guarantees[index].etherInterest / 100) +
                            (loans[index].etherBorrow * loans[index].etherInterest / 100);

        require(
            msg.value == amount, // amount => guarantor interest + lender interest
            "You should to pay back amount with interest");

        // transfer ether with interest from smart contract to guarantor back
        guarantees[index].guarantor.transfer(loans[index].etherBorrow + loans[index].etherBorrow * guarantees[index].etherInterest / 100);

        // transfer ether with interest from smart contract to lender
        lenders[index].transfer(loans[index].etherBorrow + loans[index].etherBorrow * loans[index].etherInterest / 100);

        // remove the loan
        delete loans[index];
        loansCount--;

        // remove the guarantee
        delete guarantees[index];
        guaranteesCount--;

        // remove the lender
        delete lenders[index];
    }
}
1
  • Could you elaborate? What happens when you try it? Also it would be helpful to remove the parts of code which are not relevant for this problem. at least from the custom contract problem2_bank. Jan 11, 2020 at 19:29

1 Answer 1

0

The if statement below is failing in the transfer function of the LoanToken.sol contract

if (_balances[msg.sender] >= amount && amount > 0) {

This is failing because this function is being called from the problem2_bank.sol contract. For this reason msg.sender = the problem2_bank.sol contract's address rather than the address that was funded by the contracts constructor as I believe you are expecting.


A potential solution is to deploy the LoanToken from within the problem2_bank contract constructor. By doing this, you can save the value of the problem2_bank contract address in LoanToken contract constructor as parentContractAddress. You can also update the LoanToken constructor to expect an address. You can pass msg.sender in to the LoanToken constructor from the problem2_bank constructor and use that to prefund the account from the sender this address should also be saved as the owner address. With this setup you can do something like:

function transfer(recipient, amount){
   if (msg.sender == parentContractAddress) {
      if (_balances[owner] >= amount && amount > 0) {
           // transfer code
           return true
      }
   } else {
      if (_balances[msg.sender] >= amount && amount > 0) {
           // transfer code
           return true
      } else {
           return false
      }
   }
}

Two Notes With This Approach:

  1. You need to make sure anytime you call transfer funds from the parent contract you perform the appropriate require statements otherwise there could be some security concerns.

  2. It will also be helpful for future debugging to switch some of these if statements with require statements

3

Your Answer

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

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