Payable Error – Fixing ‘Function Should Be Payable’ Error for Non-Transfer Functions

arrayspayablesmart-contract-wallets

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!

Best Answer

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.

Related Topic