1

This is my first post on this stack exchange site and it comes after a couple of hours of banging my head against this error's wall.

In short, I am currently going through a solidity course and one of the projects I am working on is a "shared wallet" concept with different roles (in my example below admin, family, approved spender), an allowance/role and a concept of "remaining balance" for each address that has at least 1 role assigned (i.e member).

The problem I am facing and which I cannot understand is why do I get this error "Note: The called function should be payable if you send value and the value you send should be less than your current balance." when running the "SetAllowance" function? What I cannot understand is why is the notion of "payable" invoked in the error returned when running a function that does not include any "payable" like method (i.e transfer, call, send)?!

My code below:

//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol";


contract SharedWallet is Ownable, AccessControl {
using SafeMath for uint256;

event BalanceReceived (address indexed _from, uint256 _amount);
event AmountSpent (address indexed _by, address indexed _to, uint256 _amount);

bytes32 public constant FAMILY_ROLE = keccak256("FAMILY");
bytes32 public constant ApprovedSpender_ROLE = keccak256("ApprovedSpender");

mapping(bytes32 => uint256) public allowance;
mapping(address => uint256) public remainingBalance;

address payable[] internal AllFamilyAcc;
address payable[] internal AllAppSpendersAcc;

constructor () public {
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    allowance[FAMILY_ROLE] = 1 ether;
    allowance[ApprovedSpender_ROLE] = 2 ether;
}

modifier onlyAdmin() {
    require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
    _;
}
modifier OnlyMembers() {
    require(hasRole(ApprovedSpender_ROLE, msg.sender) || hasRole(FAMILY_ROLE, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
    _;
}

function SetAllowance(uint256 _FamilyAmount, uint256 _AppSpenderAmount)
    public payable
    {
    for (uint i=0; i<=AllFamilyAcc.length; i++) {        
          remainingBalance[AllFamilyAcc[i]] += _FamilyAmount;
      }

    
    for (uint i=0; i<=AllAppSpendersAcc.length; i++) { 
          remainingBalance[AllAppSpendersAcc[i]] += _AppSpenderAmount;
      }
    }

function addFamilyMember(address payable _account)
    public virtual
    {
        require(!hasRole(FAMILY_ROLE, _account),"Already a family member!");
        grantRole(FAMILY_ROLE, _account);
        remainingBalance[_account] = allowance[FAMILY_ROLE];
        AllFamilyAcc.push(payable(_account));
    }

function addApprovedSpender(address payable _account)
    public virtual
    {
        require(!hasRole(ApprovedSpender_ROLE, _account),"Already an approved spender!");
        grantRole(ApprovedSpender_ROLE, _account);
        remainingBalance[_account]=allowance[ApprovedSpender_ROLE];
        AllAppSpendersAcc.push(payable(_account));
    }
}

Thanks in advance!

3
  • What is the function from which you have this error? Apr 6, 2022 at 12:01
  • Can you add the code calling the function?
    – Julissa DC
    Apr 6, 2022 at 13:42
  • the function throwing this error is the "SetAllowance" function (already in the description but you have to slightly scroll down). Sorry for the amount of lines added but thought I would include everything that is relevant to this function for context. Thanks!
    – radub23
    Apr 7, 2022 at 3:53

2 Answers 2

1

You must to change these for conditions in SetAllowance() function:

for (uint i=0; i <= AllFamilyAcc.length; i++){
  ...
}

for (uint i=0; i <= AllAppSpendersAcc.length; i++) {
  ...
}

in this way:

function SetAllowance(uint256 _FamilyAmount, uint256 _AppSpenderAmount) public payable {
        for (uint i=0; i < AllFamilyAcc.length; i++) {
            remainingBalance[AllFamilyAcc[i]] += _FamilyAmount;
        }

        for (uint i=0; i < AllAppSpendersAcc.length; i++) {
            remainingBalance[AllAppSpendersAcc[i]] += _AppSpenderAmount;
        }
    }

This because, if AllFamilyAcc length is 3 items (address0, address1, address2) with this condition i <= AllFamilyAcc.length you iterate until i=3 but in your array doesn't exists an element at index 3 and then compiler give you this error. When you change this condition in i < AllFamilyAcc.length, last iteration'll be 2 and (in this example) last value at index=2 is address2. The same reasoning is for AllAppSpendersAcc cycle.

1
  • This was it, thanks a lot for taking the time!
    – radub23
    Apr 27, 2022 at 14:53
1

I think it have to be payable because the AllAppSpendersAcc[i] is of type address payable and since you are using payble address the compiler asumes that this function will transfer Ether around. You can store the payable addresses as address and whe you need them to be payable you can cast them like that:

    address adr = 0x.........;
    payable(adr).transfer(...);
3
  • Sorry, but I don't really see what the suggested solution is. The "SetAllowance" function is already defined as "payable" and before posting the question here I guess I tried all the possible combinations. What strikes me is why this function throws a "payable" related error when it does not invoke any payable related method; it just features a "balance" addition. P.S: If you copy paste the code in Remix, add 2 random addresses to each one of the groups and then run the function, the error should be visible.
    – radub23
    Apr 8, 2022 at 3:15
  • It is because AllFamilyAcc is of type address payable[] and it is used in your function which make the whole function payable Apr 8, 2022 at 7:25
  • Excuse me, I appreciate you taking the time but the answer does not help in solving the problem. As you can see in the description, everything (i.e the 2 address arrays as well as the "SetAllowance" function) is set as "payable", however I still get the error. I have also tried running the function by removing the "payable" definition from both the address arrays + the function but surprisingly the same error gets thrown out. Would appreciate a precise answer as to what needs to be modified to avoid getting the error mentioned in the description.
    – radub23
    Apr 8, 2022 at 22:56

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.