1

hello I'm trying to user a method from the smart contract and transfer token between different address but I got this error this is a link for the transaction

Error: Warning! Error encountered during contract execution [execution reverted]

this is the code of the function of the contract :

   function sendTokensFromCustomerToRetailer(address retailer, address customer, uint256 tokens) public onlyOwner {
        increaseAllowance(customer, tokens * 10 ** uint256(decimals()));
        transferFrom(customer, retailer, tokens * 10 ** uint256(decimals()));
        updateRewardableBalancePerAddress(customer);
        updateRewardableBalancePerAddress(retailer);
    }

ERC20Upgradeable code:

contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

 function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

 function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }


function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

 function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][spender] + addedValue);
        return true;
    }

  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

 function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

 function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

smart contract code :

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

//pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract FidelityImplementationContract is OwnableUpgradeable, ERC20Upgradeable {
    using SafeMathUpgradeable for uint256;
    
     // *-*-*-*-*-*-*_Attibutes_*-*-*-*-*-*-*

    // *-*-*-*-*-*-*_Constructor_*-*-*-*-*-*-*
    
    // struct EchelonReward is used to build rewards echelons
    struct EchelonReward {
        uint256 echelonMaxValue;
        uint256 percentage;
    }

    // stakeholders array
    address[] internal stakeholders;

    // minimum balance value since the last rewards distribution
    mapping(address => uint256) internal rewardableBalance;

    // timestamp to store when the next reward distribution will be possible
    uint256 internal nextRewardsAvailableTime;

    // duration between each reward distribution
    uint256 internal rewardsDuration;

    // reward percentage by tokens
    EchelonReward[] internal rewardsPercentageByTokens;


    // *-*-*-*-*-*-*_Constructor_*-*-*-*-*-*-*
    
    function init(uint256 initialTokens) initializer  public {
        // SET THE OWNER HERE
        __Ownable_init();
        __ERC20_init("FidelityToken", "FT");
        
        _mint(msg.sender, initialTokens * 10 ** uint256(decimals()));
        fillRewardsPercentageByTokensOnStart();
        nextRewardsAvailableTime = block.timestamp.add(rewardsDuration);
        rewardsDuration = 2 minutes;
    }

    // *-*-*-*-*-*-*_Stakeholders_*-*-*-*-*-*-*

    /**
     * @notice A method to check if an address is a stakeholder.
     * @param _address The address to verify.
     * @return bool, uint256 Whether the address is a stakeholder, 
     * and if so its position in the stakeholders array.
    */
    function isStakeholder(address _address)
        public
        onlyOwner
        view
        returns(bool, uint256)
    {
        for (uint256 s = 0; s < stakeholders.length; s += 1){
            if (_address == stakeholders[s]) return (true, s);
        }
        return (false, 0);
    }

    /**
     * @notice A method to add a stakeholder.
     * @param _stakeholder The stakeholder to add.
    */
    function addStakeholder(address _stakeholder)
        public
        onlyOwner
    {
        (bool _isStakeholder, ) = isStakeholder(_stakeholder);
        if(!_isStakeholder) {
            stakeholders.push(_stakeholder);
            rewardableBalance[_stakeholder] = 0;
        }
    }

    /**
     * @notice A method to remove a stakeholder.
     * @param _stakeholder The stakeholder to remove.
    */
    function removeStakeholder(address _stakeholder)
        public
        onlyOwner
    {
        (bool _isStakeholder, uint256 s) = isStakeholder(_stakeholder);
        if(_isStakeholder){
            stakeholders[s] = stakeholders[stakeholders.length - 1];
            stakeholders.pop();
            rewardableBalance[_stakeholder] = 0;
        } 
    }


    // *-*-*-*-*-*-*_Rewards_*-*-*-*-*-*-*

    /** 
     * @notice A simple method that calculates the rewards of the given stakeholder.
     * @param _stakeholder The stakeholder to calculate rewards for.
    */
    function calculateReward(address _stakeholder)
        public
        onlyOwner
        view
        returns(uint256)
    {
        return getRewardableBalanceByStakeholder(_stakeholder) * getRewardPercentage(_stakeholder) / 100;
    }

    /**
     * @notice A method to get the rewardable balance of the given stakeholder.
     * @param _stakeholder The stakeholder.
     * @return The amount of the rewardable tokens of the given stakeholder
    */
    function getRewardableBalanceByStakeholder(address _stakeholder)
        public
        onlyOwner
        view
        returns(uint256)
    {
        return rewardableBalance[_stakeholder];
    }

    /**
     * @notice A method to get the reward percentage by balance.
     * @param _stakeholder The stakeholder.
     * @return The reward percentage according to the stakeholder's balance
    */
    function getRewardPercentage(address _stakeholder)
        public
        onlyOwner
        view
        returns(uint256)
    {
        if (0 != rewardsPercentageByTokens.length) {
            for (uint index = rewardsPercentageByTokens.length; index > 0; index--) {
                if (rewardableBalance[_stakeholder] >= rewardsPercentageByTokens[index - 1].echelonMaxValue) {
                    return rewardsPercentageByTokens[index - 1].percentage;
                }
            }
        }
        return 0;
    }

    /**
     * @notice A method to get the reward percentage by balance.
     * @return The reward percentage according to the stakeholder's balance
    */
    function getRewardsPercentageByTokens() public onlyOwner view returns (EchelonReward[] memory) {
        return rewardsPercentageByTokens;
    }

    /**
     * @notice A method to distribute rewards to all stakeholders.
    */
    function distributeRewards() 
        public
        onlyOwner
    {
        if (block.timestamp >= nextRewardsAvailableTime) {
            for (uint256 s = 0; s < stakeholders.length; s += 1){
                address stakeholder = stakeholders[s];
                updateRewardableBalancePerAddress(stakeholder);
                uint256 reward = calculateReward(stakeholder);
                increaseAllowance(owner(), reward);
                transferFrom(owner(), stakeholder, reward);
                rewardableBalance[stakeholder] = balanceOf(stakeholder);
            }
            nextRewardsAvailableTime = block.timestamp.add(rewardsDuration);
        }
    }

    /**
     * @notice A method to update the rewards percentage echlons.
     * @param newEchlonArray The new rewards echlons.
    */
    function updateRewardsPercentageByTokens(EchelonReward[] memory newEchlonArray) public onlyOwner {
        if (0 != newEchlonArray.length) {
            delete rewardsPercentageByTokens;
            for (uint index = 0; index < newEchlonArray.length; index++) {
                EchelonReward memory echelon;
                echelon.echelonMaxValue = newEchlonArray[index].echelonMaxValue * 10 ** uint256(decimals());
                echelon.percentage = newEchlonArray[index].percentage;
                rewardsPercentageByTokens[index] = echelon;
            }
        }
    }

    /**
     * @notice A method to initialize the reward percntages.
    */
    function fillRewardsPercentageByTokensOnStart() private onlyOwner {
        rewardsPercentageByTokens.push(EchelonReward(
            {
                echelonMaxValue : 100 * 10 ** uint256(decimals()),
                percentage : 1
            }));

        rewardsPercentageByTokens.push(EchelonReward(
            {
                echelonMaxValue : 500 * 10 ** uint256(decimals()),
                percentage : 2
            }));

        rewardsPercentageByTokens.push(EchelonReward(
            {
                echelonMaxValue : 1000 * 10 ** uint256(decimals()),
                percentage : 3
            }));

        rewardsPercentageByTokens.push(EchelonReward(
            {
                echelonMaxValue : 10000 * 10 ** uint256(decimals()),
                percentage : 4
            }));
        
        rewardsPercentageByTokens.push(EchelonReward(
            {
                echelonMaxValue : 50000 * 10 ** uint256(decimals()),
                percentage : 5
            }));
    }

    /**
     * @notice A method to update the rewardable balance for the given stakeholder.
     * @param _stakeholder The stakeholder.
    */
    function updateRewardableBalancePerAddress(address _stakeholder) public onlyOwner {
        if (balanceOf(_stakeholder) < rewardableBalance[_stakeholder]) {
            rewardableBalance[_stakeholder] = balanceOf(_stakeholder);
        }
    }

    /**
     * @notice A method to update the rewardable balance for all the stakeholders.
    */
    function updateRewardableBalances() public onlyOwner {
        for (uint index = 0; index < stakeholders.length; index++) {
            updateRewardableBalancePerAddress(stakeholders[index]);
        } 
    }


    // *-*-*-*-*-*-*_Transactions_*-*-*-*-*-*-*

    /**
     * @notice A method to send tokens from the retailer to the customer.
     * @param retailer The retailer address.
     * @param customer The customer address.
     * @param tokens The amount of tokens to send.
    */
    function sendTokensFromRetailerToCustomer(address retailer, address customer, uint256 tokens) public onlyOwner {
        increaseAllowance(retailer, tokens * 10 ** uint256(decimals()));
        transferFrom(retailer, customer, tokens * 10 ** uint256(decimals()));
        updateRewardableBalancePerAddress(customer);
        updateRewardableBalancePerAddress(retailer);
    }

    /**
     * @notice A method to send tokens from the customer to the retailer.
     * @param retailer The retailer address.
     * @param customer The customer address.
     * @param tokens The amount of tokens to send.
    */
    function sendTokensFromCustomerToRetailer(address retailer, address customer, uint256 tokens) public onlyOwner {
        increaseAllowance(customer, tokens * 10 ** uint256(decimals()));
        transferFrom(customer, retailer, tokens * 10 ** uint256(decimals()));
        updateRewardableBalancePerAddress(customer);
        updateRewardableBalancePerAddress(retailer);
    }

    /**
     * @notice A method to enable customers to send tokens from one retailer to another.
     * @param retailerSource The retailer source address.
     * @param walletSource The customer address in the source retailer.
     * @param walletDestination The customer address in the target retailer.
     * @param tokensToSend The amount of tokens to send.
     * @param feesForFidelityPercent The percentage of transaction fees for Fidelity.
     * @param feesForRetailerSourcePercent The percentage of transaction fees for the source retailer.
    */
    function tansfertFromRetailerToAnother(address retailerSource, address walletSource, address walletDestination, uint256 tokensToSend,
            uint256 feesForFidelityPercent, uint256 feesForRetailerSourcePercent) public onlyOwner {
                
        uint256 feesForFidelity = ((tokensToSend * feesForFidelityPercent) / 100) * 10 ** uint256(decimals());
        uint256 feesForRetailerSource = ((tokensToSend * feesForRetailerSourcePercent) / 100) * 10 ** uint256(decimals());
        
        increaseAllowance(walletSource, (tokensToSend * 10 ** uint256(decimals())) - feesForFidelity - feesForRetailerSource);
        transferFrom(walletSource, walletDestination, (tokensToSend * 10 ** uint256(decimals())) - feesForFidelity - feesForRetailerSource);
        
        increaseAllowance(walletSource, feesForRetailerSource);
        transferFrom(walletSource, retailerSource, feesForRetailerSource);
        
        increaseAllowance(walletSource, feesForFidelity);
        transferFrom(walletSource, owner(), feesForFidelity);
        
        updateRewardableBalancePerAddress(retailerSource);
        updateRewardableBalancePerAddress(walletSource);
        updateRewardableBalancePerAddress(walletDestination);
        updateRewardableBalancePerAddress(owner());
    }

    /**
     * @notice A method to mint extra tokens.
     * @param tokens The amount of tokens to mint.
    */
    function mintExtraTokens(uint256 tokens) public onlyOwner {
        _mint(owner(), tokens * 10 ** uint256(decimals()));
    }
    
    /**
     * @notice A method to burn tokens.
     * @param tokens The amount of tokens to burn.
    */
    function burnTokens(uint256 tokens) public onlyOwner {
        _burn(owner(), tokens * 10 ** uint256(decimals()));
    }
    
    /**
     * @notice A method to get the formatted balance of the given stakeholder.
     * @param _stakeholder The stakeholder.
     * @return The formatted balance of the given stakeholder
    */
    function getFormattedBalance(address _stakeholder)
        public
        view
        returns(uint256)
    {
        return balanceOf(_stakeholder) / 10 ** uint256(decimals());
    }
    
}

the problem happen when I try to send token with the function sendTokensFromCustomerToRetailer() , when I use my address which is the address of the owner of the smart contract as a customer the transaction success , but when I use an other address as the address of the customer the transaction fail I mean only the transaction happen from my address to another I can't for example make transaction from another address to my address they always fail

here the transaction fail when I use an other address as customer address (not the address of the owner ): enter image description here

this is a link for the transaction that fail : link

link for successful transaction : link

4
  • Please, can you share all smart contract code? Commented Mar 15, 2022 at 17:54
  • ok i will share it Commented Mar 16, 2022 at 16:13
  • Can you add more information about error and about the single operations that you did for encounter the error? Commented Mar 16, 2022 at 23:33
  • ok i will add more information Commented Mar 17, 2022 at 8:31

1 Answer 1

1

Your transaction revert because sendTokensFromCustomerToRetailer function call internal the updateRewardableBalancePerAddress function but this function has onlyOwner modifier.

When you call updateRewardableBalancePerAddress function sendTokensFromCustomerToRetailer then msg.sender address is the contract address.

Try to remove onlyOnwer modifier and mark as a internal then only contract call this function.

If you don't want to mark as a internal function then try to call function with delegate call. Here is the Example of the delegate call.

1
  • I think the problem is in transferFrom function because the transaction throw an error : Fail with error 'ERC20: insufficient allowance' and this error is coming from ERC20Upgradeable exactly from the function _spendAllowance() , I removed onlyOwner from updateRewardableBalancePerAddress () and always the transaction fail : ropsten.etherscan.io/tx/… Commented Mar 17, 2022 at 9:50

Your Answer

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

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