0

I'm developing a smart contract in Remix, and it is compiling correctly, but every time I try to execute functions related to the transfer function from my token ERC-20, it returns me an error saying that the execution was reverted.

Contract address: 0x32D28885b02Cf0020e5d06EEe6A27D3Ef2A52Ebe

Token address: 0x138F1A0e20af9A0E56fC7d7b23Cd5c394631B19a

One of the functions I'm trying to execute:

       function transferirTokens(address receiver, uint256 numTokens) public {
       require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
            
            token.transfer(receiver, numTokens);
        }

I'm creating the contract in the testnet Sepolia, with my Metamask account with 5 Sepolia ETH as the msg.sender.

I've been trying a lot of things, but I'm a bit desperate. Could someone help me please?

Thanks!

The contract:

        // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import "./TOKEN.sol"; 
    
    contract SistemaRecompenses {
        JaumeCallisToken public token; 
        address public owner; 
        
    
        constructor(address tokenAddress) {
            token = JaumeCallisToken(tokenAddress); 
            owner = msg.sender; 
        }
    
        // Estructura per emmagatzemar les dades de l'alumne
        struct Alumne {
            int256 tokens; 
            uint256 Tokens_Restats_Per_Suspensos_o_Incidencies; 
            uint256 incidencies; 
            uint256 faltes; 
            uint256 retards; 
            uint256 RecompensaBar; 
            uint256 RecompensaSortidaTrimestral; 
            uint256 RecompensaSortidaFinalCurs; 
            uint256 RecompensaBiblioteca;
            uint256 RecompensaTemaClasse;
            
            mapping(string => uint256) assignatures; // Balanç de tokens per assignatura
        }
    
        mapping(address => Alumne) public alumnes; // Mapping amb les dades de cada alumne
        address[] public adrecesAlumnes; // Llista d'adreces d'alumnes
    
        // Afegir un alumne a la llista d'adreces
        function afegirAlumne(address alumne) public {
        require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
        adrecesAlumnes.push(alumne);
        
    }
         // Funció per aconseguir el totalSupply 
        function obtenirTotalSupply() public view returns (uint256) {
            require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
            return token.totalSupply();
        }
    
        // Funció per transferir tokens JC des de l'adreça del creador del contracte
        function transferirTokens(address receiver, uint256 numTokens) public {
       require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
            
            token.transfer(receiver, numTokens);
        }
    
        // Funció per transferir els tokens perduts a l'adreça del creador del contracte
        function transferirTokensPerduts(uint256 numTokens) public {
        require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
            
            token.transfer(owner, numTokens);
        }
    
    
        // Funció per iniciar el trimestre i donar 50 tokens a tots els alumnes
        function iniciarTrimestre() public {
        require(msg.sender == owner,"Nomes el creador pot executar aquesta funcio");
       
        for (uint256 i = 0; i < adrecesAlumnes.length; i++) {
            address alumne = adrecesAlumnes[i];
            transferirTokens(alumne, 50000000000000000000);
            Alumne storage alumneData = alumnes[alumne]; 
            alumneData.tokens += 50; 
            
            
        }
    }
        // Funció per avaluació de l'alumne
        function avaluarAlumne(address alumne, string memory assignatura, uint256 qualificacio) public {
            require(qualificacio >= 0 && qualificacio <= 10, unicode"Qualificació no vàlida");
    
            Alumne storage alumneData = alumnes[alumne];
    
            if (qualificacio >= 9) {
                alumneData.tokens += 10;
                transferirTokens(alumne, 10000000000000000000);
            } else if (qualificacio >= 7) {
                alumneData.tokens += 5;
                transferirTokens(alumne, 50000000000000000000);
            } else if (qualificacio < 5) {
                alumneData.tokens -= 5;
                alumneData.Tokens_Restats_Per_Suspensos_o_Incidencies += 5;
                transferirTokensPerduts(5000000000000000000);
            }
    
            
            alumneData.assignatures[assignatura] += uint256 (qualificacio);
        }
    
        // Funció per registrar una incidència
        function registrarIncidencia(address alumne) public {
    
            Alumne storage alumneData = alumnes[alumne];
    
            if (alumneData.tokens >= 35) {
                alumneData.tokens -= 5;
                alumneData.Tokens_Restats_Per_Suspensos_o_Incidencies += 5;
                transferirTokensPerduts(5000000000000000000);
            } 
    
            // Incrementa el comptador d'incidències
            alumneData.incidencies++;
        }
    
        // Funció per registrar una falta
        function registrarFalta(address alumne) public {
    
            Alumne storage alumneData = alumnes[alumne];
    
            // Incrementa el comptador de faltes
            alumneData.faltes++;
    
            
            if (alumneData.faltes % 3 == 0) {
                registrarIncidencia(alumne);
            }
        }
    
        // Funció per registrar un retard
        function registrarRetard(address alumne) public {
    
            Alumne storage alumneData = alumnes[alumne];
    
            // Incrementa el comptador de retards
            alumneData.retards++;
    
            
            if (alumneData.retards % 3 == 0) {
                registrarIncidencia(alumne);
            }
        }
    
        // Funció per aconseguir les dades d'un alumne
        function obtenirDadesAlumne(address alumne) public view returns (int256 tokens,uint256 Tokens_Restats_Per_Suspensos_o_Incidencies, uint256 incidencies, uint256 faltes, uint256 retards,uint256 RecompensaBar, uint256 RecompensaSortidaTrimestral, uint256 RecompensaSortidaFinalCurs, uint256 RecompensaBiblioteca, uint256 RecompensaTemaClasse) {
            Alumne storage alumneData = alumnes[alumne];
            return (alumneData.tokens, alumneData.Tokens_Restats_Per_Suspensos_o_Incidencies, alumneData.incidencies, alumneData.faltes, alumneData.retards, alumneData.RecompensaBar, alumneData.RecompensaSortidaTrimestral,alumneData.RecompensaSortidaFinalCurs, alumneData.RecompensaBiblioteca, alumneData.RecompensaTemaClasse);
        }
    
        // Funció per canviar recompensa
        function canviarRecompensa(address alumne, uint256 opcio) public {
            Alumne storage alumneData = alumnes[alumne];
            require(opcio >= 1 && opcio <= 5, unicode"Opció no vàlida");
    
            
            uint8[5] memory preus = [20, 0, 0, 60, 60];
    
            require(int256 (alumneData.tokens) >= int8 (preus[opcio - 1]), "Tokens insuficients");
    
            alumneData.tokens -= int8 (preus[opcio - 1]);
    
            // Aplica la recompensa segons l'opció triada
            if (opcio == 1) {
                // Descompte al bar
                // Conversió de 20 JCTokens = 1 euro
                alumneData.RecompensaBar += 1;
                transferirTokensPerduts(20000000000000000000);
            } else if (opcio == 2) {
                // Assistència a sortida escolar (màxim 30 JCTokens restats)
                require(alumneData.Tokens_Restats_Per_Suspensos_o_Incidencies <= 30, "Has restat masses tokens, no pots participar a la sortida");
                alumneData.RecompensaSortidaTrimestral += 1;
            } else if (opcio == 3) {
                // Assistència a sortida de final de curs (màxim 120 JCTokens restats)
                require(alumneData.Tokens_Restats_Per_Suspensos_o_Incidencies <= 120, "Has restat masses tokens, no pots participar a la sortida");
                alumneData.RecompensaSortidaFinalCurs += 1;
            } else if (opcio == 4) {
                // Permis per anar a la biblioteca durant els patis (60 JCTokens)
                alumneData.RecompensaBiblioteca += 1;
                transferirTokensPerduts(60000000000000000000);
            } else if (opcio == 5) {
                // Triar el tema de classe d'una tutoria (60 JCTokens)
                alumneData.RecompensaTemaClasse += 1;
                transferirTokensPerduts(60000000000000000000);
            } 
        }
        // Funció per canviar recompensa (aquesta seria la funció que cridaria algun professor o encarregat del bar per gastar la recompensa de l'alumne)
        function UtilitzarRecompensa(address alumne, uint256 opcio) public {
            Alumne storage alumneData = alumnes[alumne];
            require(opcio >= 1 && opcio <= 5, unicode"Opció no vàlida");
    
            if (opcio == 1) {
                // Descompte al bar
                // Conversió de 20 JCTokens = 1 euro
                require(alumneData.RecompensaBar >=1,"Obten primer la recompensa");
                alumneData.RecompensaBar -= 1;
            } else if (opcio == 2) {
                require(alumneData.RecompensaSortidaTrimestral >=1,"Obten primer la recompensa");
                alumneData.RecompensaSortidaTrimestral -= 1;
            } else if (opcio == 3) {
                // Assistència a sortida de final de curs (mínim 120 JCTokens)
                require(alumneData.RecompensaSortidaFinalCurs >=1,"Obten primer la recompensa");
                alumneData.RecompensaSortidaFinalCurs -= 1;
            } else if (opcio == 4) {
                // Permis per anar a la biblioteca durant els patis (60 JCTokens)
                require(alumneData.RecompensaBiblioteca >=1,"Obten primer la recompensa");
                alumneData.RecompensaBiblioteca -= 1;
            } else if (opcio == 5) {
                // Triar el tema de classe d'una tutoria (60 JCTokens)
                require(alumneData.RecompensaTemaClasse >=1,"Obten primer la recompensa");
                alumneData.RecompensaTemaClasse -= 1;
            } 
        }
    }


The token contract: 

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    interface IERC20 {
    
        function totalSupply() external view returns (uint256);
        function balanceOf(address account) external view returns (uint256);
        function allowance(address owner, address spender) external view returns (uint256);
    
        function transfer(address recipient, uint256 amount) external returns (bool);
        function approve(address spender, uint256 amount) external returns (bool);
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    
    
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    
    contract JaumeCallisToken is IERC20 {
        using SafeMath for uint256;
    
        string public constant name = "JaumeCallisToken";
        string public constant symbol = "JC";
        uint8 public constant decimals = 18;
    
        mapping(address => uint256) balances;
        mapping(address => mapping (address => uint256)) allowed;
    
        uint256 private totalSupply_ = 21000000*10**uint256(decimals);
    
        constructor(){
            balances[msg.sender] = totalSupply_;
        }
    
        function totalSupply() public override view returns (uint256) {
            return totalSupply_;
        }
    
        function balanceOf(address tokenOwner) public override view returns (uint256) {
            return balances[tokenOwner];
        }
    
    
        function transfer(address receiver, uint256 numTokens) public override returns (bool) {
    
            require(numTokens <= balances[msg.sender]);
            balances[msg.sender] = balances[msg.sender].sub(numTokens);
            balances[receiver] = balances[receiver].add(numTokens);
            emit Transfer(msg.sender, receiver, numTokens);
            return true;
    
        }
    
        function approve(address delegate, uint256 numTokens) public override returns (bool) {
            allowed[msg.sender][delegate] = numTokens;
            emit Approval(msg.sender, delegate, numTokens);
            return true;
        }
    
        function allowance(address owner, address delegate) public override view returns (uint) {
            return allowed[owner][delegate];
        }
    
        function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
    
            require(numTokens <= balances[owner]);
            require(numTokens <= allowed[owner][msg.sender]);
    
            balances[owner] = balances[owner].sub(numTokens);
            allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);
            balances[buyer] = balances[buyer].add(numTokens);
            emit Transfer(owner, buyer, numTokens);
            return true;
    
        }
    }
    
    library SafeMath {
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          assert(b <= a);
          return a - b;
        }
    
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
          uint256 c = a + b;
          assert(c >= a);
          return c;
        }
    }
5
  • 2
    Please provide the contract, there can be multiple reasons. Can't find out just looking at the function. Oct 1, 2023 at 12:26
  • 1
    Yes please include more information. A failing tx hash if you have it. Make sure any contracts moving tokens have the allowance to do it by sending any required approvals.
    – Maka
    Oct 1, 2023 at 12:36
  • 1
    okay, here's the two contracts Oct 1, 2023 at 17:00
  • Can you provide the tx hash of the failed Transfer()? I only see a successful Transfer() as the most recent tx
    – Rohan Nero
    Oct 1, 2023 at 18:05
  • 0xc662b3633e2ec14c4b5c6566d3bb45b701286c388d8acd3b8b652e73a18f20bf Oct 1, 2023 at 18:44

1 Answer 1

2

transfer function sends tokens from the caller to the receiver and not from msg.sender.

The function you wrote is called by the contract itself, and since the contract has no tokens, the function reverts.

You should make transferFrom instead of transfer, here is an example.

  function transferirTokens(address receiver, uint256 numTokens) public  {
    require(
      msg.sender == owner,
      "Nomes el creador pot executar aquesta funcio"
    );

    token.transferFrom(msg.sender,receiver, numTokens); // edited line
  }

Keep in mind that your contract should have approval to spend your tokens (msg.sender) tokens. You should fire the approve function before firing transferirTokens function.

enter image description here

2
  • Okay, I understand. Now it works all correctly! Thank you very much! Oct 2, 2023 at 17:51
  • If the problem is solved, you can mark the answer as correct for future visitors. And I will modify the question to be related to that problem.
    – Al-Qa'qa'
    Oct 3, 2023 at 7:44

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.