// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/**
* @title Contrato KipuBank
* @notice Este es un contrato para una bóveda bancaria simple donde los usuarios depositan y retiran ETH con límites de seguridad.
* @author Estudiante EthKipu
* @custom:security No usar en producción. Solo para fines educativos.
*/
contract KipuBank {
/*///////////////////////
Variables
///////////////////////*/
/// @notice Límite global inmutable para el total de depósitos en el banco.
uint256 immutable public i_bankCap;
/// @notice Umbral inmutable para el monto máximo de retiro por transacción.
uint256 immutable public i_umbralRetiro;
/// @notice Mapping para almacenar el saldo de cada usuario en su bóveda personal.
mapping(address => uint256) public s_saldos;
/// @notice Balance total acumulado de todos los depósitos.
uint256 public s_totalDepositado;
/// @notice Contador del número total de depósitos realizados.
uint256 public s_numeroDepositos;
/// @notice Contador del número total de retiros realizados.
uint256 public s_numeroRetiros;
/*///////////////////////
Eventos
////////////////////////*/
/// @notice Evento emitido cuando se realiza un depósito exitoso.
event KipuBank_Deposito(address indexed usuario, uint256 monto);
/// @notice Evento emitido cuando se realiza un retiro exitoso.
event KipuBank_Retiro(address indexed usuario, uint256 monto);
/*///////////////////////
Errores
/////////////////////////*/
/// @notice Error emitido cuando un depósito excede la capacidad global del banco.
error KipuBank_DepositoExcedeCap(uint256 nuevoTotal, uint256 cap);
/// @notice Error emitido cuando el saldo es insuficiente para un retiro.
error KipuBank_SaldoInsuficiente(uint256 solicitado, uint256 disponible);
/// @notice Error emitido cuando un retiro excede el umbral permitido.
error KipuBank_RetiroExcedeUmbral(uint256 solicitado, uint256 umbral);
/// @notice Error emitido cuando falla una transferencia de ETH.
error KipuBank_TransferenciaFallida(bytes data);
/*///////////////////////
Funciones
/////////////////////////*/
/**
* @notice Constructor para inicializar el contrato.
* @param _capacidadBanco El límite global de depósitos.
* @param _umbralRetiro El umbral máximo por retiro.
*/
constructor(uint256 _capacidadBanco, uint256 _umbralRetiro) {
i_bankCap = _capacidadBanco;
i_umbralRetiro = _umbralRetiro;
}
/// @notice Función para recibir ETH directamente (depósito automático).
receive() external payable {
_depositar(msg.sender, msg.value);
}
/// @notice Fallback: evita pérdida si se envían datos no esperados con ETH.
fallback() external payable {
if (msg.value > 0) {
_depositar(msg.sender, msg.value);
}
}
/**
* @notice Función external payable para realizar un depósito en la bóveda personal.
* @dev Sigue el patrón Checks-Effects-Interactions implícitamente a través de la función privada.
* @dev Emite un evento al finalizar.
*/
function depositar() external payable {
_depositar(msg.sender, msg.value);
}
/**
* @notice Función para retirar fondos de la bóveda personal.
* @dev Solo permite retiros hasta el umbral y saldo disponible.
* @dev Sigue Checks-Effects-Interactions para seguridad.
* @param _monto El monto a retirar.
*/
function retirar(uint256 _monto) external {
// Checks
if (_monto == 0) revert KipuBank_SaldoInsuficiente(0, 0); // Evita retiros cero, opcional pero buena práctica.
if (_monto > s_saldos[msg.sender]) {
revert KipuBank_SaldoInsuficiente(_monto, s_saldos[msg.sender]);
}
if (_monto > i_umbralRetiro) {
revert KipuBank_RetiroExcedeUmbral(_monto, i_umbralRetiro);
}
// Effects
s_saldos[msg.sender] -= _monto;
s_totalDepositado -= _monto;
s_numeroRetiros++;
// Interactions
(bool exito, bytes memory data) = payable(msg.sender).call{value: _monto}("");
if (!exito) {
revert KipuBank_TransferenciaFallida(data);
}
emit KipuBank_Retiro(msg.sender, _monto);
}
/**
* @notice Función external view para consultar el saldo de un usuario.
* @param _usuario La dirección del usuario.
* @return El saldo actual en la bóveda.
*/
function saldoDe(address _usuario) external view returns (uint256) {
return s_saldos[_usuario];
}
/**
* @notice Función private para procesar depósitos.
* @dev Realiza validaciones y actualizaciones de estado.
* @param _usuario El usuario que deposita.
* @param _monto El monto del depósito.
*/
function _depositar(address _usuario, uint256 _monto) private {
// Checks
if (_monto == 0) revert KipuBank_DepositoExcedeCap(0, i_bankCap); // Evita depósitos cero.
uint256 nuevoTotal = s_totalDepositado + _monto;
if (nuevoTotal > i_bankCap) {
revert KipuBank_DepositoExcedeCap(nuevoTotal, i_bankCap);
}
// Effects
s_saldos[_usuario] += _monto;
s_totalDepositado = nuevoTotal;
s_numeroDepositos++;
// Sin interactions externas aquí, solo estado interno.
emit KipuBank_Deposito(_usuario, _monto);
}
}KipuBank es un smart contract simple y seguro implementado en Solidity que permite a los usuarios depositar ETH en una bóveda personal y retirarlos con restricciones de seguridad. El contrato sigue mejores prácticas de desarrollo Web3, incluyendo el uso de errores personalizados, el patrón Checks-Effects-Interactions (CEI), eventos para logging, y variables inmutables para límites fijos.
- Depósitos: Los usuarios pueden depositar ETH vía
depositar(),receive()ofallback(). Hay un límite global (i_bankCap) para el total depositado. - Retiros: Los usuarios retiran vía
retirar(uint256 _monto), limitado por saldo personal y un umbral por transacción (i_umbralRetiro). - Registros: Mantiene contadores de depósitos (
s_numeroDepositos) y retiros (s_numeroRetiros), y saldos por usuario (s_saldos). - Seguridad: Errores personalizados, CEI en retiros, manejo seguro de transferencias nativas.
- Consultas: Función view
saldoDe(address)para balances.
El contrato es ownerless (sin propietario), enfocado en usuarios individuales y soporta múltiples usuarios simultáneamente (cada wallet tiene su bóveda independiente).
- Solidity: 0.8.26
- Wallet: MetaMask (conectado a Sepolia testnet)
- ETH de Prueba: Obtén de un faucet como Alchemy Sepolia Faucet o Infura Sepolia Faucet.
- IDE: Remix IDE
-
Configura MetaMask para Sepolia:
- Abre MetaMask y agrega la red Sepolia (RPC:
https://rpc.sepolia.org, Chain ID:11155111, Explorer:https://sepolia.etherscan.io). - Solicita ETH de prueba en un faucet (necesitas ~0.05 ETH para gas).
- Abre MetaMask y agrega la red Sepolia (RPC:
-
Abre Remix y Carga el Código:
- Ve a remix.ethereum.org.
- Crea un nuevo archivo: En el panel izquierdo (File Explorer), clic en "Create new file" > Nómbralo
KipuBank.sol. - Copia y pega el código del contrato KipuBank (ver
/contracts/KipuBank.solen tu repo).
-
Compila el Contrato:
- Ve a la pestaña Solidity Compiler (ícono de Solidity).
- Selecciona compiler 0.8.26.
- Clic en Compile KipuBank.sol. Verifica "Compilation successful".
-
Despliega en Sepolia:
- Ve a Deploy & Run Transactions (ícono de Ethereum).
- En "Environment", selecciona Injected Provider - MetaMask (conecta tu wallet).
- En "Contract", elige KipuBank.
- En campos del constructor:
_capacidadBanco:10000000000000000000(10 ETH en wei)._umbralRetiro:1000000000000000000(1 ETH en wei).
- Clic en Deploy. Confirma en MetaMask (gas ~0.01 ETH).
- Copia la dirección del contrato (e.g.,
0x123...abc) de la sección "Deployed Contracts".
-
Verifica en Etherscan (Opcional pero recomendado):
- Ve a sepolia.etherscan.io.
- Busca tu contract address.
- Para verificar código: En Remix (Solidity Compiler > "Verification"), ingresa address, compiler, y el código fuente. O manualmente en Etherscan > "Verify and Publish".
Usa la sección "Deployed Contracts" en Remix para llamar funciones. Cambia accounts en MetaMask para simular múltiples usuarios.
- Pon valor en Value (e.g.,
0.1para 0.1 ETH). - Selecciona
depositar. - Clic en transact > Confirma en MetaMask.
- Ver evento en logs:
KipuBank_Deposito.
- Selecciona
saldoDe. - Input: Tu address (e.g.,
0x...de MetaMask). - Clic en call (no gasta gas).
- Resultado: Saldo en wei (e.g.,
100000000000000000= 0.1 ETH).
- Deja Value en 0.
- Selecciona
retirar. - Input: Monto en wei (e.g.,
50000000000000000= 0.05 ETH). - Clic en transact > Confirma.
- Ver evento:
KipuBank_Retiro. Consulta saldo actualizado.
- Crea otra account en MetaMask y envía ETH de prueba.
- Cambia a la nueva account (dropdown en MetaMask).
- Repite depósitos/retiros: Cada address tiene saldo independiente.
- Prueba límite global: Deposita hasta exceder
i_bankCap(revierte).
- Deposito 0 ETH: Revierte (
KipuBank_DepositoExcedeCap). - Retiro > umbral: Revierte (
KipuBank_RetiroExcedeUmbral). - Retiro > saldo: Revierte (
KipuBank_SaldoInsuficiente).
- Red: Sepolia Testnet
- Dirección: [Inserta aquí después del despliegue, e.g., 0x123...abc]
- Block Explorer: [Enlace verificado, e.g., https://sepolia.etherscan.io/address/0x123...abc]
- Transacciones de Ejemplo:
- Despliegue: [Tx Hash de MetaMask]
- Depósito: [Tx Hash]
- Retiro: [Tx Hash]
- Conversión Wei/ETH: Usa eth-converter.com para wei a ETH.
- Troubleshooting:
- "Insufficient funds": Pide más ETH del faucet.
- Transacción fallida: Verifica gas en MetaMask (baja si es alto).
- Múltiples usuarios: Importa seeds de test en MetaMask.
- Este contrato es educativo.
Este proyecto demuestra habilidades en Solidity: seguridad (CEI, errores custom), documentación (NatSpec), y despliegue en testnet con Remix/MetaMask. ¡Explora Web3!
Desplegado el: 12 de octubre de 2025