Solidity: Fundamentos
Solidity es el lenguaje de programación principal para escribir smart contracts en Ethereum y blockchains compatibles con la EVM. Es un lenguaje de alto nivel, tipado estáticamente, influenciado por JavaScript, Python y C++.
Primer Smart Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract HolaMundo {
string public saludo = "Hola, Blockchain!";
function setSaludo(string memory _nuevoSaludo) public {
saludo = _nuevoSaludo;
}
function getSaludo() public view returns (string memory) {
return saludo;
}
}
Desglose
SPDX-License-Identifier → Licencia del código (obligatorio)
pragma solidity ^0.8.20 → Versión del compilador
contract → Similar a class en otros lenguajes
public → Visibilidad (accesible externamente)
string memory → Tipo + ubicación de datos
view → No modifica el estado
returns → Tipo de retorno
Tipos de Datos
Tipos por valor
contract TiposDeDatos {
// Enteros
uint256 public enteroPositivo = 42; // 0 a 2²⁵⁶-1
int256 public enteroConSigno = -42; // -2²⁵⁵ a 2²⁵⁵-1
uint8 public enteroPequeno = 255; // 0 a 255
// Booleanos
bool public activo = true;
// Direcciones
address public owner = 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD09;
address payable public receptor; // puede recibir ETH
// Bytes
bytes32 public hash; // Tamaño fijo
bytes public dataDinamica; // Tamaño variable
// Enums
enum Estado { Activo, Pausado, Cancelado }
Estado public estado = Estado.Activo;
}
Tipos por referencia
contract TiposReferencia {
// Arrays
uint[] public numeros; // Dinámico
uint[5] public fijos; // Fijo (5 elementos)
// Mapping (diccionario)
mapping(address => uint) public balances;
mapping(address => mapping(address => uint)) public allowances;
// Strings
string public nombre = "Mi Contrato";
// Structs
struct Usuario {
string nombre;
uint edad;
address wallet;
bool activo;
}
Usuario[] public usuarios;
mapping(address => Usuario) public usuarioPorAddress;
}
Variables Especiales
Solidity proporciona variables globales con información del contexto:
contract VariablesGlobales {
function info() public view returns (
address sender,
uint valor,
uint bloque,
uint timestamp,
uint gasRestante
) {
sender = msg.sender; // Quien llama la función
valor = msg.value; // ETH enviado (en Wei)
bloque = block.number; // Número de bloque actual
timestamp = block.timestamp; // Timestamp del bloque
gasRestante = gasleft(); // Gas disponible
}
}
Funciones
Visibilidad
contract Visibilidad {
// public: accesible desde cualquier lugar
function publica() public pure returns (string memory) {
return "Cualquiera puede llamarme";
}
// external: solo desde fuera del contrato
function externa() external pure returns (string memory) {
return "Solo desde fuera";
}
// internal: solo este contrato y herederos
function interna() internal pure returns (string memory) {
return "Solo interna";
}
// private: solo este contrato
function privada() private pure returns (string memory) {
return "Solo este contrato";
}
}
Modificadores de estado
contract ModificadoresEstado {
uint public contador;
// view: lee el estado pero no lo modifica
function getContador() public view returns (uint) {
return contador;
}
// pure: no lee ni modifica el estado
function sumar(uint a, uint b) public pure returns (uint) {
return a + b;
}
// payable: puede recibir ETH
function depositar() public payable {
// msg.value contiene el ETH enviado
}
// (sin modificador): puede leer y modificar el estado
function incrementar() public {
contador += 1;
}
}
Constructor
contract MiContrato {
address public owner;
string public nombre;
// Se ejecuta una sola vez al desplegar el contrato
constructor(string memory _nombre) {
owner = msg.sender;
nombre = _nombre;
}
}
Modificadores (Modifiers)
Los modifiers permiten reutilizar lógica de validación:
contract Modificadores {
address public owner;
constructor() {
owner = msg.sender;
}
modifier soloOwner() {
require(msg.sender == owner, "No eres el owner");
_; // Placeholder para el cuerpo de la función
}
modifier costoMinimo(uint _cantidad) {
require(msg.value >= _cantidad, "ETH insuficiente");
_;
}
// Usar modificadores
function retirar() public soloOwner {
payable(owner).transfer(address(this).balance);
}
function comprar() public payable costoMinimo(0.01 ether) {
// Solo se ejecuta si msg.value >= 0.01 ETH
}
}
Eventos
Los eventos permiten registrar información en los logs de la blockchain (mucho más barato que el storage):
contract Eventos {
event Transferencia(
address indexed desde,
address indexed hacia,
uint cantidad
);
event NuevoUsuario(address indexed usuario, string nombre);
mapping(address => uint) public balances;
function transferir(address _a, uint _cantidad) public {
balances[msg.sender] -= _cantidad;
balances[_a] += _cantidad;
emit Transferencia(msg.sender, _a, _cantidad);
}
}
Los eventos indexed se pueden filtrar eficientemente desde aplicaciones frontend.
Manejo de Errores
contract Errores {
// require: validación de inputs y condiciones
function retirar(uint cantidad) public {
require(cantidad > 0, "Cantidad debe ser mayor a 0");
require(balances[msg.sender] >= cantidad, "Saldo insuficiente");
// ...
}
// revert: similar a require, útil con lógica compleja
function procesar(uint tipo) public {
if (tipo == 0) {
revert("Tipo invalido");
}
}
// assert: verificar invariantes (nunca debería fallar)
function dividir(uint a, uint b) public pure returns (uint) {
assert(b != 0);
return a / b;
}
// Custom errors (más eficientes en gas)
error SaldoInsuficiente(uint disponible, uint requerido);
mapping(address => uint) public balances;
function retirarV2(uint cantidad) public {
if (balances[msg.sender] < cantidad) {
revert SaldoInsuficiente(balances[msg.sender], cantidad);
}
}
}
Resumen
Solidity es un lenguaje tipado y compilado diseñado para la EVM. Los tipos fundamentales incluyen uint, address, bool, mapping y struct. Las funciones tienen visibilidad (public, external, internal, private) y modificadores de estado (view, pure, payable). Los modifiers permiten reutilizar validaciones, los eventos registran logs eficientes, y el manejo de errores con require, revert y custom errors protege los contratos.