Solidity: lenguaje de programación para contratos inteligentes
Solidity se utiliza para programar contratos inteligentes sofisticados para operar en el blockchain de Ethereum. Es un lenguaje de programación que ofrece un enfoque interesante que lo diferencia de los demás lenguajes.
¿Qué es Solidity?
Solidity es un lenguaje de programación de alto nivel para crear contratos inteligentes en el blockchain de Ethereum. Los “contratos inteligentes” son contratos autoejecutables que automatizan el intercambio de activos o fondos entre dos o más partes. Este tipo de contrato no necesita un intermediario para garantizar su cumplimiento, razón que los hace especiales.
El código fuente de Solidity se compila en bytecode y se introduce en el blockchain de Ethereum como un contrato inteligente. El contrato inteligente puede ser ejecutado por cualquier nodo de la red y su estado se almacena en el blockchain. 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 desarrollado específicamente para crear aplicaciones descentralizadas, DApps (Distributed Applications), que se ejecutan en la Ethereum Virtual Machine (EVM). Los contratos inteligentes son idóneos para gestionar activos digitales, crear bolsas de valores descentralizadas e implementar sistemas de votación, entre otros aspectos.
- Rápido, seguro, flexible y escalable
- Seguro con SSL y protección DDoS
- Dominio y asesor personal incluidos
Finanzas Descentralizadas (DeFi)
Solidity se utiliza para desarrollar aplicaciones de DeFi como mercados de valores descentralizados, plataformas de préstamos y empréstitos, mercados de predicción y criptomonedas. DeFi es uno de los casos de uso más populares de la tecnología blockchain. Solidity se ha convertido en una herramienta indispensable para crear aplicaciones DeFi en la red Ethereum.
Non-Fungible Tokens
Los tokens no fungibles (NFT, non-fungible tokens) gozan de gran popularidad desde 2020. Los NFT son determinados activos digitales que se almacenan en el blockchain. Pueden ser obras de arte digitales, recuerdos deportivos o artefactos del sector de los videojuegos. Solidity se utiliza para crear los contratos inteligentes que respaldan los NFT.
Gestión de la cadena de suministro
Se puede utilizar Solidity para crear contratos inteligentes con el fin de supervisar y gestionar las cadenas de suministro. Los contratos se utilizan para automatizar distintos procesos de la cadena de suministro. Entre ellos, se encuentra el seguimiento de los movimientos de mercancías, la verificación de la autenticidad de los productos y el procesamiento de los pagos entre las distintas partes.
Sistemas de votación
Solidity se puede utilizar para crear contratos inteligentes que implementen en el blockchain sistemas de votación seguros y transparentes. Los contratos pueden utilizarse para garantizar que los votos se cuenten correctamente y que el proceso de votación sea justo y transparente.
¿Cuáles son las ventajas y desventajas de Solidity?
En general, Solidity es un lenguaje de programación potente, desarrollado para crear contratos inteligentes en el blockchain de Ethereum. Sin embargo, como toda tecnología, Solidity ofrece ventajas y desventajas particulares que los desarrolladores deben tener en cuenta a la hora de crear contratos inteligentes. En cualquier caso, crear contratos inteligentes fiables requiere un cierto nivel de habilidad y atención.
A modo de ejemplo, te vamos a mostrar un contrato inteligente que actúa como un agujero negro: todo Ether que se envía al contrato es tragado de forma irreversible, ya que no se puede volver a desembolsar 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);
}
}
solidityVentajas de Solidity
- Flexibilidad: Solidity es un lenguaje de programación muy versátil. Se puede utilizar para crear distintos contratos inteligentes con una gran variedad de casos de uso.
- Seguridad: Solidity se desarrolló precisamente pensando en la seguridad. El lenguaje incluye características como controles de acceso, gestión de excepciones y mecanismos de fallo que ayudan a los desarrolladores a escribir contratos seguros.
- Compatibilidad con Ethereum: Solidity es actualmente el lenguaje de programación por excelencia para crear contratos inteligentes en el blockchain de Ethereum.
- Comunidad sólida: Solidity cuenta con una gran comunidad de desarrolladores de blockchain. Es decir, dispone de numerosos recursos para aprender y resolver problemas.
Desventajas de Solidity
- Curva de aprendizaje: Solidity tiene una curva de aprendizaje relativamente alta para los desarrolladores que tratan por primera vez con el blockchain y los contratos inteligentes.
- Inmutabilidad: una vez que un contrato inteligente se introduce en el blockchain, no puede ser modificado, por lo que los desarrolladores deben ser extremadamente cuidadosos a la hora de redactarlo y probarlo.
- Ausencia de revisión formal: Solidity no tiene herramientas integradas que revisan el código de forma formal, por lo que los desarrolladores tienen que recurrir a herramientas externas para garantizar que sus contratos estén bien escritos.
- Herramientas limitadas: el ecosistema de herramientas de Solidity aún está relativamente poco desarrollado. Por lo tanto, puede causar problemas con IDE, marcos de pruebas y otras herramientas de desarrollo.
¿Cómo es la sintaxis básica de Solidity?
Solidity es un lenguaje orientado a objetos, especializado en contratos inteligentes e influenciado por JavaScript, Python y C++. La sintaxis de Solidity es similar a la de JavaScript, aunque presenta algunas peculiaridades interesantes.
Variables en Solidity
A primera vista, las variables de Solidity parecen funcionar como en otros lenguajes similares. Sin embargo, la mayor diferencia se encuentra en el hecho de que el entorno de ejecución es la Ethereum Virtual Machine (EVM). Todas las operaciones en la EVM, así como el almacenamiento de datos, cuestan una cierta cantidad de “gas”. Por lo tanto, a la hora de programar, puede ser necesario sopesar cómo implementar una operación de la forma más eficiente posible.
Además de las variables “normales”, Solidity reconoce constantes que deben definirse en la compilación. Las constantes requieren menos gas para ser almacenadas:
// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;
solidityLa situación de las variables inmutables
es similar. Estas consumen menos gas y no permiten ser modificadas tras haberles sido asignado un valor. A diferencia de las variables constantes
, se les puede asignar un valor durante la ejecución.
Estructuras de control en Solidity
Solidity es un lenguaje de programación imperativo, por lo que admite las sentencias de control habituales, por ejemplo, los condicionales y bucles. A continuació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;
}
solidityEl bucle for
en Solidity es similar a la sintaxis en JavaScript o C++:
// Loop 10 times
for (int i = 0; i < 10; i++) {
// …
}
solidityEl bucle while
también sigue la misma estructura. Combinamos una condición de interrupció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++;
}
solidityTipos simples en Solidity
Solidity es un lenguaje de tipado estático y admite los tipos de variables comunes a los lenguajes de programación. Los tipos de datos más sencillos que representen valores individuales son los booleanos, numéricos y strings (cadenas de caracteres), entre otros.
Los booleanos en Solidity pueden adoptar el valor true
o false
. Pueden combinarse entre sí con los operadores booleanos conocidos y ser utilizados en sentencias if
:
bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
// ...
}
soliditySolidity 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 representar en incrementos de 8 bits, desde int8
y int16
, hasta int265
:
uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)
solidityLas cadenas de caracteres en Solidity se utilizan principalmente para crear mensajes de estado. El lenguaje de programación admite comillas simples y dobles, así como el estándar de caracteres de Unicode:
string message = 'Hello World';
string success = unicode"Transfer sent";
SolidityFunciones en Solidity
Al igual que en la mayoría de los lenguajes de programación, las funciones son una parte esencial de Solidity. Las funciones se definen de forma similar a JavaScript, por lo que se debe especificar 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;
}
solidityLas llamadas a una función se realizan como de costumbre:
// Call the function
int result = addNumbers(2, 3);
solidityCuriosamente, de forma análoga a los argumentos con nombre, los valores de retorno también pueden tener nombre. En este caso, basta con asignar las variables correspondientes en el cuerpo de la función, no es necesario introducir un retorno mediante return
:
function divideNumbers(int dividend, int divisor) returns (int quotient) {
quotient = dividend / divisor;
// No `return` necessary
}
soliditySimilar 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 inteligentes en Solidity
Además de los tipos habituales, Solidity cuenta con varios tipos especializados en contratos inteligentes. El tipo más común es address
, que asigna direcciones de Ethereum, donde las direcciones payable
pueden recibir transferencias en Ether. Para ello, las direcciones 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);
}
solidityA partir del tipo address
, surge el tipo contract
como estructura esencial del lenguaje. Los contratos equivalen a las clases en los lenguajes de programación orientados 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 normalmente comienzan con una línea pragma
que indica la versión de Solidity permitida, seguida de la definición del contrato propiamente 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;
}
}
solidityLos contratos inteligentes 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 automáticamente una funciónview
como getter (capturador) 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 ainternal
, pero no se puede acceder a la variable desde los contratos heredados.
Las funciones también se pueden clasificar 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
}
solidityModificadores en Solidity
Los modificadores en Solidity te permiten emplear una sintaxis más precisa. Los modificadores funcionan de forma parecida a los decoradores de Python: al igual que los decoradores, los modificadores 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;
}
}
solidityGestión de transacciones con Solidity
Solidity cuenta con una gestión de transacciones integrada, permite verificar que una transferencia de Ether se ha procesado íntegramente o no se ha procesado en absoluto. El lenguaje utiliza la palabra clave revert
, que provoca el “roll-back” de una transacció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
}
}
solidityOtro 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