Anatomía de los contratos inteligentes
Última actualización de la página: 2 de diciembre de 2025
Un contrato inteligente es un programa que se ejecuta en una dirección en Ethereum. Están formados por datos y funciones, que se pueden ejecutar al recibir una transacción. A continuación encontrarás una visión general de lo que compone un contrato inteligente.
Requisitos previos
Asegúrese de haber leído primero sobre los contratos inteligentes. Este documento asume que ya estás familiarizado con lenguajes de programación como JavaScript o Python.
Datos
Cualquier dato del contrato debe asignarse a una ubicación: ya sea a storage o memory. Es costoso modificar el almacenamiento en un contrato inteligente, por lo que debes considerar dónde deben ubicarse sus datos.
Almacenamiento
Los datos persistentes se denominan almacenamiento y se representan por variables de estado. Estos valores se almacenan permanentemente en la blockchain. Necesitas declarar el tipo de dato para que el contrato pueda llevar un seguimiento de la cantidad de almacenamiento en la blockchain que se necesitará cuando compile.
1// Ejemplo de Solidity2contract SimpleStorage {3 uint storedData; // Variable de estado4 // ...5}1# ejemplo de Vyper2storedData: int128Si ya has utilizado lenguajes de programación orientados a objetos, probablemente estarás familiarizado con la mayoría de tipos de datos. Sin embargo, address debería ser nuevo para usted si es nuevo en el desarrollo de Ethereum.
Un tipo address puede contener una dirección de Ethereum que equivale a 20 bytes o 160 bits. Devuelve en notación hexadecimal con un 0x al inicio.
Otros tipos de variables incluyen:
- booleano
- entero
- números de punto fijo
- matrices de bytes de punto fijo
- matrices de bytes de tamaño dinámico
- literales racionales y enteros
- literales de cadena
- literales hexadecimales
- enums
Para más explicación, echa un vistazo a la documentación:
Memoria
Los valores que sólo se almacenan durante la vida útil de la ejecución de una función de contrato se llaman variables de memoria. Dado que estos no se almacenan permanentemente en la blockchain, son mucho más baratos de usar.
Obtenga más información sobre cómo la EVM almacena los datos (almacenamiento, memoria y pila) en los documentos de Solidityopens in a new tab.
Variables de entorno
Además de las variables que se definen en su contrato, hay algunas variables globales especiales. Se utilizan principalmente para proporcionar información acerca de la cadena de bloques o la transacción actual.
Ejemplos:
| Propiedad | Variable de estado | Descripción |
|---|---|---|
block.timestamp | uint256 | Marca de tiempo del bloque actual |
msg.sender | dirección | Remitente del mensaje (llamada actual) |
Funciones
De una forma simplista, las funciones pueden obtener información o establecer información en respuesta a las transacciones entrantes.
Existen dos tipos de llamadas de funciones:
internal: estas no crean una llamada a la EVM- Solo se puede acceder a las funciones y variables de estado internas desde dentro (es decir, desde el contrato actual o los contratos que se deriven de él)
external: estas sí crean una llamada a la EVM- Las funciones externas forman parte de la interfaz del contrato, lo que significa que se pueden llamar desde otros contratos y a través de transacciones. Una función externa
fno se puede llamar internamente (es decir,f()no funciona, perothis.f()sí).
- Las funciones externas forman parte de la interfaz del contrato, lo que significa que se pueden llamar desde otros contratos y a través de transacciones. Una función externa
También pueden ser public o private
- Las funciones
publicse pueden llamar internamente desde el contrato o externamente a través de mensajes - Las funciones
privatesolo son visibles para el contrato en el que se definen y no para los contratos derivados
Tanto las funciones como las variables de estado pueden hacerse públicas o privadas.
Aquí se ejemplifica una función para actualizar una variable de estado en un contrato:
1// ejemplo de Solidity2function update_name(string value) public {3 dapp_name = value;4}- El parámetro
valuede tipostringse pasa a la función:update_name - Se declara como
public, lo que significa que cualquiera puede acceder a ella - No se declara como
view, por lo que puede modificar el estado del contrato
Funciones de vista
Estas funciones no modifican el estado de los datos del contrato. Ejemplos comunes son las funciones "getter", que se pueden utilizar para recibir el saldo o balance de un usuario, por ejemplo.
1// Ejemplo de Solidity2function balanceOf(address _owner) public view returns (uint256 _balance) {3 return ownerPizzaCount[_owner];4}1dappName: public(string)23@view4@public5def readName() -> string:6 return dappNameQué se considera modificar un estado:
- Escribir a variables de estado.
- Emisión de eventosopens in a new tab.
- Creación de otros contratosopens in a new tab.
- Uso de
selfdestruct. - Enviae ethers mediante llamadas.
- Llamar a cualquier función no marcada como
viewopure. - Usar llamadas de bajo nivel.
- Utilizando un ensamblaje en línea que contiene ciertos códigos de operador.
Funciones constructoras
Las funciones constructor solo se ejecutan una vez cuando el contrato se implementa por primera vez. Al igual que constructor en muchos lenguajes de programación basados en clases, estas funciones a menudo inicializan las variables de estado con sus valores especificados.
1// Ejemplo de Solidity2// Inicializa los datos del contrato, estableciendo el `owner`3// a la dirección del creador del contrato.4constructor() public {5 // Todos los contratos inteligentes dependen de transacciones externas para activar sus funciones.6 // `msg` es una variable global que incluye datos relevantes sobre la transacción dada,7 // como la dirección del remitente y el valor de ETH incluido en la transacción.8 // Más información: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties9 owner = msg.sender;10}Mostrar todo1# Ejemplo en Vyper23@external4def __init__(_beneficiary: address, _bidding_time: uint256):5 self.beneficiary = _beneficiary6 self.auctionStart = block.timestamp7 self.auctionEnd = self.auctionStart + _bidding_timeFunciones incorporadas
Además de las variables y funciones que define en su contrato, hay algunas funciones especiales integradas. El ejemplo más obvio es:
address.send()– Soliditysend(address)– Vyper
Esto permite que los contratos envíen ETH a otras cuentas.
Escribir funciones
Su función necesita:
- parámetro de la variable y tipo de variable (si acepta parámetros)
- declaraciónde variable interna/externa
- declaración de variable de tipo pure/view/payable
- devuelve el tipo (valor, en caso de devolución)
1pragma solidity >=0.4.0 <=0.6.0;23contract ExampleDapp {4 string dapp_name; // variable de estado56 // Se llama cuando el contrato se implementa e inicializa el valor7 constructor() public {8 dapp_name = "Mi dapp de ejemplo";9 }1011 // Función Get12 function read_name() public view returns(string) {13 return dapp_name;14 }1516 // Función Set17 function update_name(string value) public {18 dapp_name = value;19 }20}Mostrar todoUn contrato completo podría verse así. Aquí la función constructor proporciona un valor inicial para la variable dapp_name.
Eventos y registros
Los eventos permiten que su contrato inteligente se comunique con su interfaz o frontend, u otras aplicaciones de suscripción. Una vez que una transacción es validada y se agrega a un bloque, los contratos inteligentes pueden emitir eventos y registrar información, que la frontend puede procesar y utilizar.
Ejemplos anotados
Estos son algunos ejemplos escritos en Solidity. Si le gustaría jugar con el código, puede interactuar con ellos en Remixopens in a new tab.
Hola, mundo
1// Especifica la versión de Solidity, usando un versionado semántico.2// Más información: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity ^0.5.10;45// Define un contrato llamado `HelloWorld`.6// Un contrato es una colección de funciones y datos (su estado).7// Una vez implementado, un contrato reside en una dirección específica de la cadena de bloques de Ethereum.8// Más información: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 // Declara una variable de estado `message` de tipo `string`.12 // Las variables de estado son variables cuyos valores se almacenan permanentemente en el almacenamiento del contrato.13 // La palabra clave `public` hace que las variables sean accesibles desde fuera de un contrato14 // y crea una función a la que otros contratos o clientes pueden llamar para acceder al valor.15 string public message;1617 // Al igual que en muchos lenguajes de programación orientados a objetos basados en clases, un constructor es18 // una función especial que solo se ejecuta en la creación del contrato.19 // Los constructores se utilizan para inicializar los datos del contrato.20 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) public {22 // Acepta un argumento de cadena `initMessage` y establece el valor23 // en la variable de almacenamiento `message` del contrato.24 message = initMessage;25 }2627 // Una función pública que acepta un argumento de cadena28 // y actualiza la variable de almacenamiento `message`.29 function update(string memory newMessage) public {30 message = newMessage;31 }32}Mostrar todoToken
1pragma solidity ^0.5.10;23contract Token {4 // Una `address` es comparable a una dirección de correo electrónico; se utiliza para identificar una cuenta en Ethereum.5 // Las direcciones pueden representar un contrato inteligente o cuentas externas (de usuario).6 // Más información: https://solidity.readthedocs.io/en/v0.5.10/types.html#address7 address public owner;89 // Un `mapping` es, esencialmente, una estructura de datos de tabla de hash.10 // Este `mapping` asigna un número entero sin signo (el saldo del token) a una dirección (el poseedor del token).11 // Más información: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types12 mapping (address => uint) public balances;1314 // Los eventos permiten registrar la actividad en la cadena de bloques.15 // Los clientes de Ethereum pueden escuchar eventos para reaccionar a los cambios de estado del contrato.16 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events17 event Transfer(address from, address to, uint amount);1819 // Inicializa los datos del contrato, estableciendo el `owner`20 // a la dirección del creador del contrato.21 constructor() public {22 // Todos los contratos inteligentes dependen de transacciones externas para activar sus funciones.23 // `msg` es una variable global que incluye datos relevantes sobre la transacción dada,24 // como la dirección del remitente y el valor de ETH incluido en la transacción.25 // Más información: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties26 owner = msg.sender;27 }2829 // Crea una cantidad de nuevos tokens y los envía a una dirección.30 function mint(address receiver, uint amount) public {31 // `require` es una estructura de control que se utiliza para aplicar ciertas condiciones.32 // Si una declaración `require` se evalúa como `false`, se activa una excepción,33 // que revierte todos los cambios realizados en el estado durante la llamada actual.34 // Más información: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions3536 // Solo el propietario del contrato puede llamar a esta función37 require(msg.sender == owner, "Usted no es el propietario.");3839 // Aplica una cantidad máxima de tokens40 require(amount < 1e60, "Se superó la emisión máxima");4142 // Aumenta el saldo de `receiver` en `amount`43 balances[receiver] += amount;44 }4546 // Envía una cantidad de tokens existentes desde cualquier llamador a una dirección.47 function transfer(address receiver, uint amount) public {48 // El remitente debe tener suficientes tokens para enviar49 require(amount <= balances[msg.sender], "Saldo insuficiente.");5051 // Ajusta los saldos de tokens de las dos direcciones52 balances[msg.sender] -= amount;53 balances[receiver] += amount;5455 // Emite el evento definido anteriormente56 emit Transfer(msg.sender, receiver, amount);57 }58}Mostrar todoActivo digital único
1pragma solidity ^0.5.10;23// Importa símbolos de otros archivos en el contrato actual.4// En este caso, una serie de contratos de ayuda de OpenZeppelin.5// Más información: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files67import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";8import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";9import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";10import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";1112// La palabra clave `is` se utiliza para heredar funciones y palabras clave de contratos externos.13// En este caso, `CryptoPizza` hereda de los contratos `IERC721` y `ERC165`.14// Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance15contract CryptoPizza is IERC721, ERC165 {16 // Utiliza la librería SafeMath de OpenZeppelin para realizar operaciones aritméticas de forma segura.17 // Más información: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath18 using SafeMath for uint256;1920 // Las variables de estado constantes en Solidity son similares a las de otros lenguajes,21 // pero debe asignarlas desde una expresión que sea constante en tiempo de compilación.22 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables23 uint256 constant dnaDigits = 10;24 uint256 constant dnaModulus = 10 ** dnaDigits;25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;2627 // Los tipos de estructura le permiten definir su propio tipo28 // Más información: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs29 struct Pizza {30 string name;31 uint256 dna;32 }3334 // Crea una matriz vacía de estructuras de Pizza35 Pizza[] public pizzas;3637 // Mapeo del ID de la pizza a la dirección de su propietario38 mapping(uint256 => address) public pizzaToOwner;3940 // Mapeo de la dirección del propietario al número de tokens que posee41 mapping(address => uint256) public ownerPizzaCount;4243 // Mapeo del ID del token a la dirección aprobada44 mapping(uint256 => address) pizzaApprovals;4546 // Puede anidar mapeos, este ejemplo mapea al propietario a las aprobaciones del operador47 mapping(address => mapping(address => bool)) private operatorApprovals;4849 // Función interna para crear una Pizza aleatoria a partir de una cadena (nombre) y un ADN50 function _createPizza(string memory _name, uint256 _dna)51 // La palabra clave `internal` significa que esta función solo es visible52 // dentro de este contrato y de los contratos que derivan de este53 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters54 internal55 // `isUnique` es un modificador de función que comprueba si la pizza ya existe56 // Más información: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers57 isUnique(_name, _dna)58 {59 // Añade una pizza a la matriz de pizzas y obtiene el ID60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);6162 // Comprueba que el propietario de la pizza es el mismo que el usuario actual63 // Más información: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions6465 // tenga en cuenta que address(0) es la dirección cero,66 // lo que indica que pizza[id] aún no está asignada a un usuario en particular.6768 assert(pizzaToOwner[id] == address(0));6970 // Mapea la pizza al propietario71 pizzaToOwner[id] = msg.sender;72 ownerPizzaCount[msg.sender] = SafeMath.add(73 ownerPizzaCount[msg.sender],74 175 );76 }7778 // Crea una pizza aleatoria a partir de una cadena (nombre)79 function createRandomPizza(string memory _name) public {80 uint256 randDna = generateRandomDna(_name, msg.sender);81 _createPizza(_name, randDna);82 }8384 // Genera un ADN aleatorio a partir de una cadena (nombre) y la dirección del propietario (creador)85 function generateRandomDna(string memory _str, address _owner)86 public87 // Las funciones marcadas como `pure` prometen no leer ni modificar el estado88 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions89 pure90 returns (uint256)91 {92 // Genera un uint aleatorio a partir de una cadena (nombre) + dirección (propietario)93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +94 uint256(_owner);95 rand = rand % dnaModulus;96 return rand;97 }9899 // Devuelve una matriz de pizzas encontradas por el propietario100 function getPizzasByOwner(address _owner)101 public102 // Las funciones marcadas como `view` prometen no modificar el estado103 // Más información: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions104 view105 returns (uint256[] memory)106 {107 // Utiliza la ubicación de almacenamiento `memory` para almacenar valores solo durante108 // el ciclo de vida de esta llamada de función.109 // Más información: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack110 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);111 uint256 counter = 0;112 for (uint256 i = 0; i < pizzas.length; i++) {113 if (pizzaToOwner[i] == _owner) {114 result[counter] = i;115 counter++;116 }117 }118 return result;119 }120121 // Transfiere la pizza y la propiedad a otra dirección122 function transferFrom(address _from, address _to, uint256 _pizzaId) public {123 require(_from != address(0) && _to != address(0), "Dirección no válida.");124 require(_exists(_pizzaId), "La pizza no existe.");125 require(_from != _to, "No se puede transferir a la misma dirección.");126 require(_isApprovedOrOwner(msg.sender, _pizzaId), "La dirección no está aprobada.");127128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);130 pizzaToOwner[_pizzaId] = _to;131132 // Emite el evento definido en el contrato IERC721 importado133 emit Transfer(_from, _to, _pizzaId);134 _clearApproval(_to, _pizzaId);135 }136137 /**138 * Transfiere de forma segura la propiedad de un ID de token determinado a otra dirección139 * Si la dirección de destino es un contrato, debe implementar `onERC721Received`,140 * que se llama en una transferencia segura, y devuelve el valor mágico141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;142 * de lo contrario, la transferencia se revierte.143 */144 function safeTransferFrom(address from, address to, uint256 pizzaId)145 public146 {147 // solium-disable-next-line arg-overflow148 this.safeTransferFrom(from, to, pizzaId, "");149 }150151 /**152 * Transfiere de forma segura la propiedad de un ID de token determinado a otra dirección153 * Si la dirección de destino es un contrato, debe implementar `onERC721Received`,154 * que se llama en una transferencia segura, y devuelve el valor mágico155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;156 * de lo contrario, la transferencia se revierte.157 */158 function safeTransferFrom(159 address from,160 address to,161 uint256 pizzaId,162 bytes memory _data163 ) public {164 this.transferFrom(from, to, pizzaId);165 require(_checkOnERC721Received(from, to, pizzaId, _data), "Debe implementar onERC721Received.");166 }167168 /**169 * Función interna para invocar `onERC721Received` en una dirección de destino170 * La llamada no se ejecuta si la dirección de destino no es un contrato171 */172 function _checkOnERC721Received(173 address from,174 address to,175 uint256 pizzaId,176 bytes memory _data177 ) internal returns (bool) {178 if (!isContract(to)) {179 return true;180 }181182 bytes4 retval = IERC721Receiver(to).onERC721Received(183 msg.sender,184 from,185 pizzaId,186 _data187 );188 return (retval == _ERC721_RECEIVED);189 }190191 // Quema una pizza: destruye el token por completo192 // El modificador de función `external` significa que esta función forma193 // parte de la interfaz del contrato y otros contratos pueden llamarla194 function burn(uint256 _pizzaId) external {195 require(msg.sender != address(0), "Dirección no válida.");196 require(_exists(_pizzaId), "La pizza no existe.");197 require(_isApprovedOrOwner(msg.sender, _pizzaId), "La dirección no está aprobada.");198199 ownerPizzaCount[msg.sender] = SafeMath.sub(200 ownerPizzaCount[msg.sender],201 1202 );203 pizzaToOwner[_pizzaId] = address(0);204 }205206 // Devuelve el recuento de pizzas por dirección207 function balanceOf(address _owner) public view returns (uint256 _balance) {208 return ownerPizzaCount[_owner];209 }210211 // Devuelve el propietario de la pizza encontrada por ID212 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {213 address owner = pizzaToOwner[_pizzaId];214 require(owner != address(0), "ID de pizza no válido.");215 return owner;216 }217218 // Aprueba a otra dirección para transferir la propiedad de la pizza219 function approve(address _to, uint256 _pizzaId) public {220 require(msg.sender == pizzaToOwner[_pizzaId], "Debe ser el propietario de la pizza.");221 pizzaApprovals[_pizzaId] = _to;222 emit Approval(msg.sender, _to, _pizzaId);223 }224225 // Devuelve la dirección aprobada para una pizza específica226 function getApproved(uint256 _pizzaId)227 public228 view229 returns (address operator)230 {231 require(_exists(_pizzaId), "La pizza no existe.");232 return pizzaApprovals[_pizzaId];233 }234235 /**236 * Función privada para borrar la aprobación actual de un ID de token determinado237 * Se revierte si la dirección dada no es realmente la propietaria del token238 */239 function _clearApproval(address owner, uint256 _pizzaId) private {240 require(pizzaToOwner[_pizzaId] == owner, "Debe ser el propietario de la pizza.");241 require(_exists(_pizzaId), "La pizza no existe.");242 if (pizzaApprovals[_pizzaId] != address(0)) {243 pizzaApprovals[_pizzaId] = address(0);244 }245 }246247 /*248 * Establece o anula la aprobación de un operador determinado249 * Un operador puede transferir todos los tokens del remitente en su nombre250 */251 function setApprovalForAll(address to, bool approved) public {252 require(to != msg.sender, "No se puede aprobar la propia dirección");253 operatorApprovals[msg.sender][to] = approved;254 emit ApprovalForAll(msg.sender, to, approved);255 }256257 // Indica si un operador está aprobado por un propietario determinado258 function isApprovedForAll(address owner, address operator)259 public260 view261 returns (bool)262 {263 return operatorApprovals[owner][operator];264 }265266 // Toma la propiedad de la pizza: solo para usuarios aprobados267 function takeOwnership(uint256 _pizzaId) public {268 require(_isApprovedOrOwner(msg.sender, _pizzaId), "La dirección no está aprobada.");269 address owner = this.ownerOf(_pizzaId);270 this.transferFrom(owner, msg.sender, _pizzaId);271 }272273 // Comprueba si la pizza existe274 function _exists(uint256 pizzaId) internal view returns (bool) {275 address owner = pizzaToOwner[pizzaId];276 return owner != address(0);277 }278279 // Comprueba si la dirección es la propietaria o está aprobada para transferir la pizza280 function _isApprovedOrOwner(address spender, uint256 pizzaId)281 internal282 view283 returns (bool)284 {285 address owner = pizzaToOwner[pizzaId];286 // Desactivar la comprobación de solium debido a287 // https://github.com/duaraghav8/Solium/issues/175288 // solium-disable-next-line operator-whitespace289 return (spender == owner ||290 this.getApproved(pizzaId) == spender ||291 this.isApprovedForAll(owner, spender));292 }293294 // Comprueba si la pizza es única y no existe todavía295 modifier isUnique(string memory _name, uint256 _dna) {296 bool result = true;297 for (uint256 i = 0; i < pizzas.length; i++) {298 if (299 keccak256(abi.encodePacked(pizzas[i].name)) ==300 keccak256(abi.encodePacked(_name)) &&301 pizzas[i].dna == _dna302 ) {303 result = false;304 }305 }306 require(result, "Ya existe una pizza con ese nombre.");307 _;308 }309310 // Devuelve si la dirección de destino es un contrato311 function isContract(address account) internal view returns (bool) {312 uint256 size;313 // Actualmente no hay una forma mejor de comprobar si hay un contrato en una dirección314 // que comprobar el tamaño del código en esa dirección.315 // Consulte https://ethereum.stackexchange.com/a/14016/36603316 // para obtener más detalles sobre cómo funciona.317 // TODO: Vuelva a comprobar esto antes del lanzamiento de Serenity, porque entonces todas las direcciones serán318 // contratos.319 // solium-disable-next-line security/no-inline-assembly320 assembly {321 size := extcodesize(account)322 }323 return size > 0;324 }325}Mostrar todoLecturas adicionales
Revise la documentación de Solidity y Vyper para ver una descripción más completa de los contratos inteligentes:
Temas relacionados
Tutoriales relacionados
- Reducción del tamaño de los contratos para luchar contra el límite de tamaño de los contratos – Algunos consejos prácticos para reducir el tamaño de su contrato inteligente.
- Registro de datos de contratos inteligentes con eventos – Una introducción a los eventos de contratos inteligentes y cómo puede utilizarlos para registrar datos.
- Interactuar con otros contratos desde Solidity – Cómo implementar un contrato inteligente a partir de un contrato existente e interactuar con él.