Funciones en Solidity

Solidity

En este tutorial vamos a ver qué son, cómo se declaran y cómo se usan las funciones en un Smart Contract de Solidity.

Qué es una función

Una función es una porción de código encargada de realizar una determinada tarea que se puede reutilizar en cualquier parte de una aplicación, ahorrando así memoria o recursos del sistema, al no ser necesario almacenar el código cada vez que se ejecuta, disminuyendo también el tiempo de ejecución de la aplicación.

Una función acepta una serie de parámetros y produce una salida, pudiendo también usar o modificar las propiedades declaradas en el contrato.

Al crear una función se reduce la necesidad de escribir el mismo código repetidas veces. Gracias a las funciones, es posible organizar el código y crear aplicaciones modulares, siendo más fáciles de mantener y de entender.

Cómo declarar una función

Las funciones de Solidity se definen mediante la sentencia function, a la que sigue el nombre de la función que queremos declarar y la lista de posibles parámetros de entrada entre paréntesis. El nombre de la función debe ser único y no debe coincidir con el nombre de otra función o propiedad del contrato. Seguidamente se declara su visibilidad y se define el tipo de valor que devuelve:

pragma solidity ^0.8.0;

contract MiContrato
{
    function sumarNumeros(uint num1, uint num2) public pure returns(uint)
    {
        return (num1 + num2); 
    }
}

Los parámetros de entrada de la función deben estar separados con una coma y se deben definir junto a su tipo. Las funciones no tienen por qué devolver un valor, aunque es recomendable definir el tipo de valor que devuelve la misma en la propia declaración de la función, mediante la sentencia returns.

En el siguiente ejemplo vamos a declarar una variable en un contrato y seguidamente vamos a declarar una función para obtener su valor y otra para configurarlo:

pragma solidity ^0.8.0;

contract MiContrato
{
    uint num;

    function getNum() external view returns(uint)
    {
        return (num); 
    }

    function setNum(uint _num) external
    {
        num = _num;
    }
}

Tal y como ves, hemos declarado la propiedad num. Luego hemos definido la función getNum, que devuelve el contenido de la propiedad num, y la función setNum, que modifica el valor de la propiedad num.

Hemos declarado la función getNum junto con el modificador de visibilidad external, que indica que será posible llamar a la función desde fuera del contrato. También hemos usado el término view para indicar que es una función de solo lectura que no modificará los datos de la blockchain.

Luego hemos especificado la sentencia return junto con el tipo de valor que devuelve la función.

Seguidamente hemos declarado la función setNum, que acepta el argumento _num. Podríamos haberle dado el nombre de num, sin el guion bajo, aunque en dicho caso la variable num dejaría de referenciar a la propiedad num del método en el ámbito de la función, lo que se conoce como shadowing.

También hemos usado la sentencia external, indicando que también será posible llamar a la función desde fuera del contrato. Sin embargo no hemos usado el término view, ya que queremos que la función modifique datos en la blockchain. En su interior sencillamente modificamos el valor de la propiedad num del contrato, asignándole el valor del argumento _num que pasamos a la función.

Cómo llamar a una función

Habitualmente se llamará a una función del contrato cuando el usuario y otro función lo deseen. Para llamar a una función bastará con que uses su nombre desde el lugar en donde quieras invocarla, pasándole los parámetros necesario separados por una coma.

En el siguiente ejemplo creamos la función doble, que obtendrá el doble del número que le pasemos como parámetro. Para ello, llamamos en su interior a la función multiplicar, a la que le pasamos como parámetros el multiplicando y el multiplicador, que en este caso serán la variable valor y el número 2 respectivamente.

pragma solidity ^0.8.0;
  
contract MiContrato
{
   function doble(uint valor) public pure returns(uint)
   {
      return multiplicar(valor, 2);
   }
  
   function multiplicar(uint multiplicando, uint multiplicador) public pure returns(uint)
   {
       return multiplicando * multiplicador;
   }
}

Cómo retornar valores de una función

Para devolver un valor desde una función en Solidity se usa la sentencia return. Se trata de la última sentencia que se ejecutará en la función. A diferencia de otros lenguajes de programación, en Solidity es posible devolver más de un valor siempre y cuando se hayan definido en la sentencia returns de la declaración de la función:

pragma solidity ^0.8.0;
  
contract MiContrato
{
   function multa() public pure returns(bytes32, bytes32, uint)
   {
       bytes32 conductor = 'Max Damage';
       bytes32 coche = 'Red Eagle';
       uint velocidad = 240;

      return (conductor, coche, velocidad);
   }

}

En el ejemplo anterior hemos declarado la función multa, que devuelve tres valores. Estos valores se corresponden con el conductor, el coche y la velocidad de la multa. Esto es algo muy útil que pocos lenguajes de programación soportan.

Visibilidad de las funciones

La visibilidad de una función define qué o quién tiene acceso a la función y de qué modo. En esta sección vamos a ver cuáles son las sentencias o palabras que se usan para definir la visibilidad de las funciones de Solidity. Si te fijas, ya hemos estado usando palabras como external en los apartados anteriores, usadas para definir la visibilidad de las funciones.

Esta es la lista de palabras que se usan para definir la visibilidad de las funciones, comenzando desde la más restrictiva:

  • private: Solamente podrás tener acceso a las funciones privadas, definidas usando la sentencia private, desde dentro del propio Smart Contract. Estas funciones no podrán ser llamadas desde fuera del Smart Contract ni desde otros contratos que hereden desde el Smart Contract en el que están definidas. Por convención, se definen con un guion bajo al inicio de su nombre.
    pragma solidity ^0.8.13;
    
    contract MiContrato
    {
        uint valor;
    
        // Función declarada como privada
        function _getValor() private view returns(uint)
        {
            return valor;
        }
    }
  • internal: Del mismo modo que las funciones definidas como private, las funciones internas, definidas mediante la sentencia internal, no podrán ser llamadas desde fuera del Smart Contract, salvo una excepción, y es que sí podrán ser llamadas desde los Smart Contracts que hereden del Smart Contract en el que se definen. Solidity, al igual que otros lenguajes de programación orientados a objetos, también incluye un sistema de herencia de contratos. La visibilidad de las funciones declaradas como internal es restrictiva, aunque no tanto como la de las funciones declaradas como private. Por convención, también se definen con un guion bajo al inicio de su nombre.
    pragma solidity ^0.8.13;
    
    contract MiContrato
    {
        uint valor;
    
        // Función declarada como interna
        function _getValor() internal view returns(uint)
        {
            return valor;
        }
    }
  • external: Las funciones externas, declaradas usando la sentencia external, solamente podrán ser llamadas desde fuera del propio Smart Contract. No podrán ser invocadas desde el propio Smart Contract. En este caso no se aplica la convención de que el nombre de la función comience por un guion bajo.
    pragma solidity ^0.8.13;
    
    contract MiContrato
    {
        uint valor;
    
        // Función declarada como externa
        function getValor() external view returns(uint)
        {
            return valor;
        }
    }
  • public: las funciones públicas, declaradas mediante la sentencia public, podrán ser invocadas tanto desde dentro como desde fuera del propio Smart Contract, siendo la configuración de visibilidad menos restrictiva. Es lógico pensar que establecer todas las funciones como public supondría un gran problema de seguridad, por lo que debes tener mucha cautela a la hora de declarar las funciones como public. Tampoco se aplica la convención de que el nombre de la función comience por un guion bajo.
    pragma solidity ^0.8.13;
    
    contract MiContrato
    {
        uint valor;
    
        // Función declarada como pública
        function getValor() public view returns(uint)
        {
            return valor;
        }
    }

En general deberás intentar que tus funciones sean siempre lo más restrictivas posibles para su correcto funcionamiento. Si basta con declarar una función como private, entonces no deberías declararla como internal. Del mismo modo, si es suficiente con declarar una función como external, entonces no deberías declararla como public.

Esto ha sido todo.


Avatar de Edu Lazaro

Edu Lázaro: Ingeniero técnico en informática, actualmente trabajo como desarrollador web y programador de videojuegos.

👋 Hola! Soy Edu, me encanta crear cosas y he redactado esta guía. Si te ha resultado útil, el mayor favor que me podrías hacer es el de compatirla en Twitter 😊

Si quieres conocer mis proyectos, sígueme en Twitter.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

“- Hey, Doc. No tenemos suficiente carretera para ir a 140/h km. - ¿Carretera? A donde vamos, no necesitaremos carreteras.”