Solidity se utiliza para programar contratos in­te­li­ge­n­tes so­fi­s­ti­ca­dos para operar en el blo­c­k­chain de Ethereum. Es un lenguaje de pro­gra­ma­ción que ofrece un enfoque in­te­re­sa­n­te que lo di­fe­re­n­cia de los demás lenguajes.

¿Qué es Solidity?

Solidity es un lenguaje de pro­gra­ma­ción de alto nivel para crear contratos in­te­li­ge­n­tes en el blo­c­k­chain de Ethereum. Los “contratos in­te­li­ge­n­tes” son contratos au­toe­je­cu­ta­bles que au­to­ma­ti­zan el in­te­r­ca­m­bio de activos o fondos entre dos o más partes. Este tipo de contrato no necesita un in­te­r­me­dia­rio para ga­ra­n­ti­zar su cu­m­pli­mie­n­to, razón que los hace es­pe­cia­les.

El código fuente de Solidity se compila en bytecode y se introduce en el blo­c­k­chain de Ethereum como un contrato in­te­li­ge­n­te. El contrato in­te­li­ge­n­te puede ser ejecutado por cualquier nodo de la red y su estado se almacena en el blo­c­k­chain. Aquí tienes un ejemplo de un contrato simple que simula una máquina de venta de NFT:

pragma Solidity 0.8.7;
contract NFTVendingMachine {
    // Declare state variables
    address public owner;
    mapping (address => uint) public nftBalance;
    // Run on deployment
    constructor() {
        owner = msg.sender;
        nftBalance[address(this)] = 100;
    }
    // Allow the owner to restock the NFT balance
    function restock(uint amount) public {
        require(msg.sender == owner, "Only the owner can restock.");
        nftBalance[address(this)] += amount;
    }
    // Allow anyone to purchase NFTs
    function purchase(uint amount) public payable {
        require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per NFT");
        require(nftBalance[address(this)] >= amount, "Not enough NFTs in stock to complete this purchase");
        nftBalance[address(this)] -= amount;
        nftBalance[msg.sender] += amount;
    }
}
solidity

¿En qué casos se utiliza Solidity?

Solidity se ha de­sa­rro­lla­do es­pe­cí­fi­ca­me­n­te para crear apli­ca­cio­nes de­s­ce­n­tra­li­za­das, DApps (Di­s­tri­bu­ted Ap­pli­ca­tio­ns), que se ejecutan en la Ethereum Virtual Machine (EVM). Los contratos in­te­li­ge­n­tes son idóneos para gestionar activos digitales, crear bolsas de valores de­s­ce­n­tra­li­za­das e im­ple­me­n­tar sistemas de votación, entre otros aspectos.

Web Hosting
El hosting que crece con tu proyecto
  • Tiempo de actividad de 99.99 % y seguridad ga­ra­n­ti­za­da
  • Aumenta el re­n­di­mie­n­to según el tráfico de tu página web
  • Incluye dominio, SSL, e-mail y soporte 24/7

Finanzas De­s­ce­n­tra­li­za­das (DeFi)

Solidity se utiliza para de­sa­rro­llar apli­ca­cio­nes de DeFi como mercados de valores de­s­ce­n­tra­li­za­dos, pla­ta­fo­r­mas de préstamos y em­pré­s­ti­tos, mercados de pre­di­c­ción y cri­p­to­mo­ne­das. DeFi es uno de los casos de uso más populares de la te­c­no­lo­gía blo­c­k­chain. Solidity se ha co­n­ve­r­ti­do en una he­rra­mie­n­ta in­di­s­pe­n­sa­ble para crear apli­ca­cio­nes DeFi en la red Ethereum.

Non-Fungible Tokens

Los tokens no fungibles (NFT, non-fungible tokens) gozan de gran po­pu­la­ri­dad desde 2020. Los NFT son de­te­r­mi­na­dos activos digitales que se almacenan en el blo­c­k­chain. Pueden ser obras de arte digitales, recuerdos de­po­r­ti­vos o ar­te­fa­c­tos del sector de los vi­deo­jue­gos. Solidity se utiliza para crear los contratos in­te­li­ge­n­tes que respaldan los NFT.

Gestión de la cadena de su­mi­ni­s­tro

Se puede utilizar Solidity para crear contratos in­te­li­ge­n­tes con el fin de su­pe­r­vi­sar y gestionar las cadenas de su­mi­ni­s­tro. Los contratos se utilizan para au­to­ma­ti­zar distintos procesos de la cadena de su­mi­ni­s­tro. Entre ellos, se encuentra el se­gui­mie­n­to de los mo­vi­mie­n­tos de me­r­ca­n­cías, la ve­ri­fi­ca­ción de la au­te­n­ti­ci­dad de los productos y el pro­ce­sa­mie­n­to de los pagos entre las distintas partes.

Sistemas de votación

Solidity se puede utilizar para crear contratos in­te­li­ge­n­tes que im­ple­me­n­ten en el blo­c­k­chain sistemas de votación seguros y tra­n­s­pa­re­n­tes. Los contratos pueden uti­li­zar­se para ga­ra­n­ti­zar que los votos se cuenten co­rre­c­ta­me­n­te y que el proceso de votación sea justo y tra­n­s­pa­re­n­te.

¿Cuáles son las ventajas y de­s­ve­n­ta­jas de Solidity?

En general, Solidity es un lenguaje de pro­gra­ma­ción potente, de­sa­rro­lla­do para crear contratos in­te­li­ge­n­tes en el blo­c­k­chain de Ethereum. Sin embargo, como toda te­c­no­lo­gía, Solidity ofrece ventajas y de­s­ve­n­ta­jas pa­r­ti­cu­la­res que los de­sa­rro­lla­do­res deben tener en cuenta a la hora de crear contratos in­te­li­ge­n­tes. En cualquier caso, crear contratos in­te­li­ge­n­tes fiables requiere un cierto nivel de habilidad y atención.

A modo de ejemplo, te vamos a mostrar un contrato in­te­li­ge­n­te que actúa como un agujero negro: todo Ether que se envía al contrato es tragado de forma irre­ve­r­si­ble, ya que no se puede volver a des­em­bo­l­sar el Ether.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.9.0;
// This contract swallows all Ether sent to it
contract Blackhole {
    event Received(address, uint);
    receive() external payable {
            emit Received(msg.sender, msg.value);
    }
}
solidity

Ventajas de Solidity

  • Fle­xi­bi­li­dad: Solidity es un lenguaje de pro­gra­ma­ción muy versátil. Se puede utilizar para crear distintos contratos in­te­li­ge­n­tes con una gran variedad de casos de uso.
  • Seguridad: Solidity se de­sa­rro­lló pre­ci­sa­me­n­te pensando en la seguridad. El lenguaje incluye ca­ra­c­te­rí­s­ti­cas como controles de acceso, gestión de ex­ce­p­cio­nes y me­ca­ni­s­mos de fallo que ayudan a los de­sa­rro­lla­do­res a escribir contratos seguros.
  • Co­m­pa­ti­bi­li­dad con Ethereum: Solidity es ac­tua­l­me­n­te el lenguaje de pro­gra­ma­ción por ex­ce­le­n­cia para crear contratos in­te­li­ge­n­tes en el blo­c­k­chain de Ethereum.
  • Comunidad sólida: Solidity cuenta con una gran comunidad de de­sa­rro­lla­do­res de blo­c­k­chain. Es decir, dispone de numerosos recursos para aprender y resolver problemas.

De­s­ve­n­ta­jas de Solidity

  • Curva de apre­n­di­za­je: Solidity tiene una curva de apre­n­di­za­je re­la­ti­va­me­n­te alta para los de­sa­rro­lla­do­res que tratan por primera vez con el blo­c­k­chain y los contratos in­te­li­ge­n­tes.
  • In­mu­ta­bi­li­dad: una vez que un contrato in­te­li­ge­n­te se introduce en el blo­c­k­chain, no puede ser mo­di­fi­ca­do, por lo que los de­sa­rro­lla­do­res deben ser ex­tre­ma­da­me­n­te cui­da­do­sos a la hora de re­da­c­tar­lo y probarlo.
  • Ausencia de revisión formal: Solidity no tiene he­rra­mie­n­tas in­te­gra­das que revisan el código de forma formal, por lo que los de­sa­rro­lla­do­res tienen que recurrir a he­rra­mie­n­tas externas para ga­ra­n­ti­zar que sus contratos estén bien escritos.
  • He­rra­mie­n­tas limitadas: el eco­si­s­te­ma de he­rra­mie­n­tas de Solidity aún está re­la­ti­va­me­n­te poco de­sa­rro­lla­do. Por lo tanto, puede causar problemas con IDE, marcos de pruebas y otras he­rra­mie­n­tas de de­sa­rro­llo.

¿Cómo es la sintaxis básica de Solidity?

Solidity es un lenguaje orientado a objetos, es­pe­cia­li­za­do en contratos in­te­li­ge­n­tes e in­flue­n­cia­do por Ja­va­S­cri­pt, Python y C++. La sintaxis de Solidity es similar a la de Ja­va­S­cri­pt, aunque presenta algunas pe­cu­lia­ri­da­des in­te­re­sa­n­tes.

Variables en Solidity

A primera vista, las variables de Solidity parecen funcionar como en otros lenguajes similares. Sin embargo, la mayor di­fe­re­n­cia se encuentra en el hecho de que el entorno de ejecución es la Ethereum Virtual Machine (EVM). Todas las ope­ra­cio­nes en la EVM, así como el al­ma­ce­na­mie­n­to de datos, cuestan una cierta cantidad de “gas”. Por lo tanto, a la hora de programar, puede ser necesario sopesar cómo im­ple­me­n­tar una operación de la forma más eficiente posible.

Además de las variables “normales”, Solidity reconoce co­n­s­ta­n­tes que deben definirse en la co­m­pi­la­ción. Las co­n­s­ta­n­tes requieren menos gas para ser al­ma­ce­na­das:

// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;
solidity

La situación de las variables inmutables es similar. Estas consumen menos gas y no permiten ser mo­di­fi­ca­das tras haberles sido asignado un valor. A di­fe­re­n­cia de las variables constantes, se les puede asignar un valor durante la ejecución.

Es­tru­c­tu­ras de control en Solidity

Solidity es un lenguaje de pro­gra­ma­ción im­pe­ra­ti­vo, por lo que admite las se­n­te­n­cias de control ha­bi­tua­les, por ejemplo, los co­n­di­cio­na­les y bucles. A co­n­ti­nua­ción, te mostramos el código para escoger el mayor de dos números, a o b:

int largerNumber = 0;
// If-else statement
if (a > b) {
    largerNumber = a;
} else {
        largerNumber = b;
}
solidity

El bucle for en Solidity es similar a la sintaxis en Ja­va­S­cri­pt o C++:

// Loop 10 times
for (int i = 0; i < 10; i++) {
    // …
}
solidity

El bucle while también sigue la misma es­tru­c­tu­ra. Co­m­bi­na­mos una condición de in­te­rru­p­ción con una variable numérica para el contador:

bool continueLoop = true;
int counter = 0;
// Loop at most 10 times
while (continueLoop && counter < 10) {
    // …
    counter++;
}
solidity

Tipos simples en Solidity

Solidity es un lenguaje de tipado estático y admite los tipos de variables comunes a los lenguajes de pro­gra­ma­ción. Los tipos de datos más sencillos que re­pre­se­n­ten valores in­di­vi­dua­les son los booleanos, numéricos y strings (cadenas de ca­ra­c­te­res), entre otros.

Los booleanos en Solidity pueden adoptar el valor true o false. Pueden co­m­bi­nar­se entre sí con los ope­ra­do­res booleanos conocidos y ser uti­li­za­dos en se­n­te­n­cias if:

bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
    // ...
}
solidity

Solidity soporta una amplia gama de tipos de números. En el caso de los números enteros, se distingue entre números con signo (int) y sin signo (uint) (estos últimos siempre son positivos). Además, el valor de un número se puede re­pre­se­n­tar en in­cre­me­n­tos de 8 bits, desde int8 y int16, hasta int265:

uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)
solidity

Las cadenas de ca­ra­c­te­res en Solidity se utilizan pri­n­ci­pa­l­me­n­te para crear mensajes de estado. El lenguaje de pro­gra­ma­ción admite comillas simples y dobles, así como el estándar de ca­ra­c­te­res de Unicode:

string message = 'Hello World';
string success = unicode"Transfer sent";
Solidity

Funciones en Solidity

Al igual que en la mayoría de los lenguajes de pro­gra­ma­ción, las funciones son una parte esencial de Solidity. Las funciones se definen de forma similar a Ja­va­S­cri­pt, por lo que se debe es­pe­ci­fi­car cada tipo de argumento. Además, se utiliza la palabra clave returns para indicar el tipo de valor que se obtiene como respuesta:

// Define a function
function addNumbers(int a, int b) returns (int) {
    return a + b;
}
solidity

Las llamadas a una función se realizan como de costumbre:

// Call the function
int result = addNumbers(2, 3);
solidity

Cu­rio­sa­me­n­te, de forma análoga a los ar­gu­me­n­tos con nombre, los valores de retorno también pueden tener nombre. En este caso, basta con asignar las variables co­rre­s­po­n­die­n­tes en el cuerpo de la función, no es necesario in­tro­du­cir un retorno mediante return:

function divideNumbers(int dividend, int divisor) returns (int quotient) {
    quotient = dividend / divisor;
    // No `return` necessary
}
solidity

Similar a las variables constant o immutable, las funciones en Solidity se pueden marcar para que no cambien de estado. Para ello, se utilizan las palabras clave view y pure. Una función view no cambia el estado, mientras que una función pure garantiza además no leer variables de estado.

Contratos in­te­li­ge­n­tes en Solidity

Además de los tipos ha­bi­tua­les, Solidity cuenta con varios tipos es­pe­cia­li­za­dos en contratos in­te­li­ge­n­tes. El tipo más común es address, que asigna di­re­c­cio­nes de Ethereum, donde las di­re­c­cio­nes payable pueden recibir tra­n­s­fe­re­n­cias en Ether. Para ello, las di­re­c­cio­nes payable utilizan los métodos balance() y transfer().

// Get address of this contract
address mine = address(this);
// Get payable external address
address payable other = payable(0x123);
// Transfer if balances fulfill conditions
if (other.balance < 10 && mine.balance >= 100) {
    other.transfer(10);
}
solidity

A partir del tipo address, surge el tipo contract como es­tru­c­tu­ra esencial del lenguaje. Los contratos equivalen a las clases en los lenguajes de pro­gra­ma­ción orie­n­ta­dos a objetos. Se encargan de agrupar una colección de datos de estado y funciones y los protegen del mundo exterior. Los contratos cuentan con herencia múltiple, como en Python o C++. Los contratos no­r­ma­l­me­n­te comienzan con una línea pragma que indica la versión de Solidity permitida, seguida de la de­fi­ni­ción del contrato pro­pia­me­n­te dicho:

// Make sure Solidity version matches
pragma Solidity >=0.7.1 <0.9.0;
// Contract definition
contract Purchase {
    // Public state variables
    address seller;
    address buyer;
    
    // View-function
    function getSeller() external view returns (address) {
        return seller;
    }
}
solidity

Los contratos in­te­li­ge­n­tes pueden definir datos de estado y funciones. Al igual que en C++ y Java, se puede definir uno de los tres niveles de acceso en cada caso:

  • public: se puede acceder a la variable desde dentro del contrato con permisos para leer y escribir. Además, se crea au­to­má­ti­ca­me­n­te una función view como getter (ca­p­tu­ra­dor) para poder leer la variable desde fuera.
  • internal: la variable está protegida de todo acceso externo. Se puede acceder a ella con derechos de lectura y escritura, tanto desde el contrato como desde los contratos herederos.
  • private: es similar a internal, pero no se puede acceder a la variable desde los contratos heredados.

Las funciones también se pueden cla­si­fi­car como external. Una función external funciona como parte de la interfaz del contrato y se utiliza para acceder desde fuera. La función receive es una forma muy conocida para recibir Ether:

// Define without `function` keyword
receive() external payable {
    // Handle Ether
}
solidity

Mo­di­fi­ca­do­res en Solidity

Los mo­di­fi­ca­do­res en Solidity te permiten emplear una sintaxis más precisa. Los mo­di­fi­ca­do­res funcionan de forma parecida a los de­co­ra­do­res de Python: al igual que los de­co­ra­do­res, los mo­di­fi­ca­do­res se utilizan para modificar la llamada a una función. A menudo se utilizan para comprobar que se cumple una condición antes de ejecutar una función:

contract Sale {
    uint price;
    address payable owner;
    modifier onlyOwner {
        // Will throw error if called by anyone other than the owner
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        // The wrapped function's body is inserted here
        _;
    }
    
    // `onlyOwner` wraps `changePrice`
    function changePrice(uint newPrice) public onlyOwner {
        // We'll only get here if the owner called this function
        price = newPrice;
    }
}
solidity

Gestión de tra­n­sac­cio­nes con Solidity

Solidity cuenta con una gestión de tra­n­sac­cio­nes integrada, permite verificar que una tra­n­s­fe­re­n­cia de Ether se ha procesado ín­te­gra­me­n­te o no se ha procesado en absoluto. El lenguaje utiliza la palabra clave revert, que provoca el “roll-back” de una tra­n­sac­ción. La palabra clave error te permite definir tus propios códigos de error:

// Custom error definition
error InsufficientPayment(uint256 paid, uint256 required);
// Contract representing a sale
contract Sale {
    uint price;
    // Purchase if enough ether transferred
    function purchase() public payable {
        if (msg.value < price) {
            revert InsufficientPayment(msg.value, price);
        }
        // Complete purchase
    }
}
solidity

Otro patrón frecuente es el uso de la función require(), que se puede utilizar de forma parecida a revert:

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity
Ir al menú principal