# Guía Completa de Solidity: Desde Básico hasta Transferencias de Ether
## 📚 Introducción
Bienvenido a esta guía completa de Solidity. Imagina que Solidity es como un lenguaje especial que utilizamos para escribir "contratos inteligentes" en la blockchain de Ethereum. Estos contratos son como pequeños programas que pueden manejar dinero digital (Ether) de forma automática y segura.
---
## 🏷️ 1. Tipos de Datos en Solidity
### ¿Qué son los tipos de datos?
Los tipos de datos son como "etiquetas" que le dicen a la computadora qué tipo de información estamos guardando. Es como organizar tu casa: tienes cajones para ropa, otros para documentos, y otros para herramientas.
### Tipos de Datos Básicos
#### 1.1 Números Enteros (uint, int)
```solidity
pragma solidity ^0.8.0;
contract EjemploTipos {
// uint = números positivos solamente (0, 1, 2, 3...)
uint256 public edad = 25; // Puede almacenar números muy grandes
uint8 public diasSemana = 7; // Números pequeños (0-255)
// int = números positivos y negativos (-3, -2, -1, 0, 1, 2, 3...)
int256 public temperatura = -10; // Puede ser negativo
// Explicación: Es como tener diferentes tamaños de cajas
// uint8 = caja pequeña (0-255)
// uint256 = caja gigante (números enormes)
}
```
**Explicación simple:** Imagina que tienes diferentes tipos de contenedores para números. Algunos solo pueden guardar números positivos (uint), otros pueden guardar números negativos también (int).
#### 1.2 Texto (string)
```solidity
contract EjemploTexto {
string public nombre = "Juan Pérez";
string public mensaje = "Hola, mundo!";
// Los strings son como notas escritas que puedes leer
// Siempre van entre comillas
}
```
#### 1.3 Verdadero o Falso (bool)
```solidity
contract EjemploBooleano {
bool public estaActivo = true; // Verdadero
bool public estaCompleto = false; // Falso
// Es como un interruptor de luz: encendido (true) o apagado (false)
}
```
#### 1.4 Direcciones (address)
```solidity
contract EjemploDireccion {
address public propietario = 0x742d35Cc6634C0532925a3b8D6a3ca2F62fb5b7;
// Una dirección es como la dirección de tu casa, pero para wallets
// Cada persona tiene una dirección única en Ethereum
}
```
### Ejemplo Completo de Tipos de Datos
```solidity
pragma solidity ^0.8.0;
contract TiposDatos {
// Números
uint256 public precio = 100; // Precio en wei (unidad más pequeña de Ether)
int256 public balance = -50; // Balance que puede ser negativo
// Texto
string public nombreProducto = "Café Premium";
// Booleano
bool public disponible = true;
// Dirección
address public vendedor = 0x742d35Cc6634C0532925a3b8D6a3ca2F62fb5b7;
// Función para mostrar información
function obtenerInfo() public view returns (string memory, uint256, bool) {
return (nombreProducto, precio, disponible);
}
}
```
---
## 🌍 2. Variables Globales
### ¿Qué son las variables globales?
Las variables globales son como información que está disponible en todo momento dentro de tu contrato. Es como tener un reloj en la pared que todos pueden ver desde cualquier parte de la casa.
### Variables Globales Más Importantes
```solidity
pragma solidity ^0.8.0;
contract VariablesGlobales {
address public propietario;
uint256 public tiempoCreacion;
constructor() {
propietario = msg.sender; // Quien despliega el contrato
tiempoCreacion = block.timestamp; // Momento actual
}
function informacionBloque() public view returns (
address, // Minero actual
uint256, // Número de bloque
uint256, // Timestamp
uint256 // Difficulty
) {
return (
block.coinbase, // Dirección del minero
block.number, // Número del bloque actual
block.timestamp, // Tiempo actual en segundos
block.difficulty // Dificultad de minado
);
}
function informacionTransaccion() public payable returns (
address, // Quien envía la transacción
uint256, // Cuánto Ether envía
uint256 // Gas usado
) {
return (
msg.sender, // Tu dirección
msg.value, // Ether que enviaste
gasleft() // Gas restante
);
}
}
```
### Explicación Detallada de Variables Globales
#### msg.sender
```solidity
// msg.sender es como ver el nombre en el sobre de una carta
// Te dice quién está enviando la transacción
address public ultimoUsuario = msg.sender;
```
#### msg.value
```solidity
// msg.value te dice cuánto Ether envió la persona
// Es como contar el dinero en un sobre
uint256 public ultimoPago = msg.value;
```
#### block.timestamp
```solidity
// block.timestamp es como mirar el reloj
// Te dice la hora exacta en segundos
uint256 public momentoActual = block.timestamp;
```
---
## 🔧 3. Funciones en Solidity
### ¿Qué son las funciones?
Las funciones son como "recetas" o "instrucciones" que le dices a tu contrato que haga. Es como tener un robot que puede hacer diferentes tareas cuando se lo pides.
### Tipos de Funciones
#### 3.1 Funciones Básicas
```solidity
pragma solidity ^0.8.0;
contract EjemploFunciones {
uint256 public numero = 0;
string public mensaje = "Hola";
// Función que NO cambia nada (view)
// Es como "mirar" algo sin tocarlo
function verNumero() public view returns (uint256) {
return numero;
}
// Función que CAMBIA algo
// Es como "modificar" algo
function cambiarNumero(uint256 _nuevoNumero) public {
numero = _nuevoNumero;
}
// Función que no lee ni cambia nada (pure)
// Es como hacer una cuenta matemática
function sumar(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
}
```
#### 3.2 Funciones con Modificadores de Acceso
```solidity
contract FuncionesAcceso {
address public propietario;
uint256 private dineroSecreto = 1000;
constructor() {
propietario = msg.sender;
}
// Solo el propietario puede usar esta función
modifier soloPropiestario() {
require(msg.sender == propietario, "No eres el propietario");
_;
}
// Función pública - todos pueden usarla
function funcionPublica() public view returns (string memory) {
return "Cualquiera puede ver esto";
}
// Función privada - solo este contrato puede usarla
function funcionPrivada() private view returns (uint256) {
return dineroSecreto;
}
// Función con restricción
function cambiarDineroSecreto(uint256 _nuevaCantidad) public soloPropiestario {
dineroSecreto = _nuevaCantidad;
}
}
```
---
## 💰 4. Transferencias de Ether
### ¿Qué es una transferencia de Ether?
Una transferencia de Ether es como enviar dinero digital de una persona a otra. Es como enviar dinero por transferencia bancaria, pero usando la blockchain.
### Métodos de Transferencia
#### 4.1 Transfer (Método Básico)
```solidity
pragma solidity ^0.8.0;
contract TransferBasico {
address public propietario;
constructor() {
propietario = msg.sender;
}
// Función para enviar Ether usando transfer
function enviarEtherTransfer(address payable _destinatario, uint256 _cantidad) public {
require(msg.sender == propietario, "Solo el propietario puede enviar");
require(address(this).balance >= _cantidad, "No hay suficiente Ether");
// transfer automáticamente revierte si falla
_destinatario.transfer(_cantidad);
}
// Función para ver el balance del contrato
function verBalance() public view returns (uint256) {
return address(this).balance;
}
}
```
#### 4.2 Send (Método con Verificación Manual)
```solidity
contract TransferSend {
address public propietario;
constructor() {
propietario = msg.sender;
}
function enviarEtherSend(address payable _destinatario, uint256 _cantidad) public returns (bool) {
require(msg.sender == propietario, "Solo el propietario puede enviar");
require(address(this).balance >= _cantidad, "No hay suficiente Ether");
// send retorna true/false, necesitas verificar manualmente
bool exito = _destinatario.send(_cantidad);
require(exito, "Transferencia fallida");
return exito;
}
}
```
#### 4.3 Call (Método Moderno y Recomendado)
```solidity
contract TransferCall {
address public propietario;
constructor() {
propietario = msg.sender;
}
function enviarEtherCall(address payable _destinatario, uint256 _cantidad) public {
require(msg.sender == propietario, "Solo el propietario puede enviar");
require(address(this).balance >= _cantidad, "No hay suficiente Ether");
// call es el método más seguro y moderno
(bool exito, ) = _destinatario.call{value: _cantidad}("");
require(exito, "Transferencia fallida");
}
}
```
### Ejemplo Práctico: Sistema de Pagos
```solidity
pragma solidity ^0.8.0;
contract SistemaPagos {
address public propietario;
uint256 public totalRecaudado;
// Mapeo para rastrear pagos
mapping(address => uint256) public pagosRealizados;
// Eventos para registrar actividad
event PagoRecibido(address from, uint256 amount);
event PagoEnviado(address to, uint256 amount);
constructor() {
propietario = msg.sender;
totalRecaudado = 0;
}
// Función para recibir pagos
function realizarPago() public payable {
require(msg.value > 0, "Debe enviar algo de Ether");
pagosRealizados[msg.sender] += msg.value;
totalRecaudado += msg.value;
emit PagoRecibido(msg.sender, msg.value);
}
// Función para enviar dinero (solo propietario)
function enviarPago(address payable _destinatario, uint256 _cantidad) public {
require(msg.sender == propietario, "Solo el propietario puede enviar");
require(address(this).balance >= _cantidad, "Balance insuficiente");
require(_cantidad > 0, "La cantidad debe ser mayor a 0");
(bool exito, ) = _destinatario.call{value: _cantidad}("");
require(exito, "Transferencia fallida");
emit PagoEnviado(_destinatario, _cantidad);
}
// Función para retirar todo (solo propietario)
function retirarTodo() public {
require(msg.sender == propietario, "Solo el propietario puede retirar");
uint256 balance = address(this).balance;
require(balance > 0, "No hay nada que retirar");
(bool exito, ) = payable(propietario).call{value: balance}("");
require(exito, "Retiro fallido");
}
}
```
---
## 📨 5. Cómo Reciben Ether los Contratos
### ¿Cómo puede un contrato recibir Ether?
Un contrato puede recibir Ether de varias formas, como una caja que puede recibir dinero de diferentes maneras.
### 5.1 Función Payable
```solidity
pragma solidity ^0.8.0;
contract ReceptorEther {
address public propietario;
uint256 public totalRecibido;
// Mapeo para rastrear quién envió cuánto
mapping(address => uint256) public contribuciones;
constructor() {
propietario = msg.sender;
}
// Función que puede recibir Ether (payable)
function depositar() public payable {
require(msg.value > 0, "Debe enviar Ether");
contribuciones[msg.sender] += msg.value;
totalRecibido += msg.value;
}
// Función para depositar con mensaje
function depositarConMensaje(string memory _mensaje) public payable {
require(msg.value > 0, "Debe enviar Ether");
contribuciones[msg.sender] += msg.value;
totalRecibido += msg.value;
// Aquí podrías guardar el mensaje si quisieras
// Por simplicidad, solo lo recibimos
}
}
```
### 5.2 Función Receive
```solidity
contract ReceptorAutomatico {
address public propietario;
uint256 public etherRecibido;
constructor() {
propietario = msg.sender;
}
// receive() se ejecuta cuando alguien envía Ether directamente al contrato
// Es como tener un buzón automático
receive() external payable {
etherRecibido += msg.value;
}
// fallback() se ejecuta cuando no hay función que coincida
fallback() external payable {
etherRecibido += msg.value;
}
function verBalance() public view returns (uint256) {
return address(this).balance;
}
}
```
### 5.3 Ejemplo Completo: Caja de Ahorros
```solidity
pragma solidity ^0.8.0;
contract CajaAhorros {
address public propietario;
uint256 public metaAhorro;
uint256 public totalAhorrado;
bool public metaAlcanzada;
// Mapeo de ahorradores
mapping(address => uint256) public ahorros;
address[] public ahorradores;
// Eventos
event AhorroRealizado(address ahorrador, uint256 cantidad);
event MetaAlcanzada(uint256 totalFinal);
event RetiroRealizado(address ahorrador, uint256 cantidad);
constructor(uint256 _metaAhorro) {
propietario = msg.sender;
metaAhorro = _metaAhorro;
totalAhorrado = 0;
metaAlcanzada = false;
}
// Función para ahorrar
function ahorrar() public payable {
require(msg.value > 0, "Debe enviar algo de Ether");
require(!metaAlcanzada, "Meta ya alcanzada");
// Si es la primera vez que ahorra, agregarlo a la lista
if (ahorros[msg.sender] == 0) {
ahorradores.push(msg.sender);
}
ahorros[msg.sender] += msg.value;
totalAhorrado += msg.value;
emit AhorroRealizado(msg.sender, msg.value);
// Verificar si se alcanzó la meta
if (totalAhorrado >= metaAhorro) {
metaAlcanzada = true;
emit MetaAlcanzada(totalAhorrado);
}
}
// Función para retirar ahorros (solo si la meta fue alcanzada)
function retirarAhorros() public {
require(metaAlcanzada, "Meta no alcanzada aun");
require(ahorros[msg.sender] > 0, "No tienes ahorros");
uint256 cantidad = ahorros[msg.sender];
ahorros[msg.sender] = 0;
(bool exito, ) = payable(msg.sender).call{value: cantidad}("");
require(exito, "Retiro fallido");
emit RetiroRealizado(msg.sender, cantidad);
}
// Función automática para recibir Ether
receive() external payable {
// Redirigir a la función ahorrar
this.ahorrar{value: msg.value}();
}
// Función para ver información
function verInformacion() public view returns (
uint256 meta,
uint256 totalActual,
bool alcanzada,
uint256 misAhorros
) {
return (
metaAhorro,
totalAhorrado,
metaAlcanzada,
ahorros[msg.sender]
);
}
// Función para ver todos los ahorradores
function verAhorradores() public view returns (address[] memory) {
return ahorradores;
}
}
```
---
## 🎯 6. Ejemplo Práctico Completo: Tienda Online
Aquí tienes un ejemplo que combina todo lo que hemos aprendido:
```solidity
pragma solidity ^0.8.0;
contract TiendaOnline {
// Variables de estado
address public propietario;
uint256 public totalVentas;
uint256 public numeroProductos;
// Estructura para productos
struct Producto {
uint256 id;
string nombre;
uint256 precio;
uint256 stock;
bool activo;
}
// Mapeos
mapping(uint256 => Producto) public productos;
mapping(address => uint256) public comprasCliente;
// Arrays
uint256[] public productosDisponibles;
// Eventos
event ProductoAgregado(uint256 id, string nombre, uint256 precio);
event CompraRealizada(address cliente, uint256 productoId, uint256 cantidad);
event StockActualizado(uint256 productoId, uint256 nuevoStock);
constructor() {
propietario = msg.sender;
totalVentas = 0;
numeroProductos = 0;
}
// Modificador para solo propietario
modifier soloPropiestario() {
require(msg.sender == propietario, "Solo el propietario puede hacer esto");
_;
}
// Función para agregar productos
function agregarProducto(
string memory _nombre,
uint256 _precio,
uint256 _stock
) public soloPropiestario {
numeroProductos++;
productos[numeroProductos] = Producto({
id: numeroProductos,
nombre: _nombre,
precio: _precio,
stock: _stock,
activo: true
});
productosDisponibles.push(numeroProductos);
emit ProductoAgregado(numeroProductos, _nombre, _precio);
}
// Función para comprar productos
function comprarProducto(uint256 _productoId, uint256 _cantidad) public payable {
Producto storage producto = productos[_productoId];
require(producto.activo, "Producto no disponible");
require(producto.stock >= _cantidad, "Stock insuficiente");
require(msg.value >= producto.precio * _cantidad, "Pago insuficiente");
// Actualizar stock
producto.stock -= _cantidad;
// Registrar compra
comprasCliente[msg.sender] += _cantidad;
totalVentas += producto.precio * _cantidad;
// Devolver cambio si es necesario
uint256 cambio = msg.value - (producto.precio * _cantidad);
if (cambio > 0) {
(bool exito, ) = payable(msg.sender).call{value: cambio}("");
require(exito, "Error al devolver cambio");
}
emit CompraRealizada(msg.sender, _productoId, _cantidad);
emit StockActualizado(_productoId, producto.stock);
}
// Función para ver producto
function verProducto(uint256 _productoId) public view returns (
uint256 id,
string memory nombre,
uint256 precio,
uint256 stock,
bool activo
) {
Producto memory producto = productos[_productoId];
return (
producto.id,
producto.nombre,
producto.precio,
producto.stock,
producto.activo
);
}
// Función para retirar ganancias (solo propietario)
function retirarGanancias() public soloPropiestario {
uint256 balance = address(this).balance;
require(balance > 0, "No hay ganancias para retirar");
(bool exito, ) = payable(propietario).call{value: balance}("");
require(exito, "Error al retirar ganancias");
}
// Función para ver balance del contrato
function verBalance() public view returns (uint256) {
return address(this).balance;
}
// Función para ver productos disponibles
function verProductosDisponibles() public view returns (uint256[] memory) {
return productosDisponibles;
}
// Función automática para recibir Ether
receive() external payable {
// Simplemente acepta el Ether
// Podrías agregar lógica adicional aquí
}
}
```
---
## 📝 Resumen y Mejores Prácticas
### Conceptos Clave Aprendidos:
1. **Tipos de Datos**: Como organizar diferentes tipos de información
2. **Variables Globales**: Información siempre disponible en el contrato
3. **Funciones**: Instrucciones que puede ejecutar el contrato
4. **Transferencias**: Cómo enviar Ether de forma segura
5. **Recepción de Ether**: Cómo los contratos pueden recibir dinero
### Mejores Prácticas:
1. **Siempre usar `require()`** para verificar condiciones
2. **Usar `call()` en lugar de `transfer()`** para transferencias
3. **Emitir eventos** para registrar actividades importantes
4. **Usar modificadores** para controlar acceso
5. **Manejar errores** apropiadamente
6. **Documentar tu código** con comentarios claros
### Próximos Pasos:
1. Practica con estos ejemplos
2. Modifica los contratos para agregar nuevas funcionalidades
3. Aprende sobre seguridad en contratos inteligentes
4. Explora bibliotecas como OpenZeppelin
5. Practica con testnets antes de usar dinero real
¡Felicidades! Has completado una introducción completa a Solidity. Con estos conceptos básicos puedes empezar a crear tus propios contratos inteligentes.