Ir al contenido principal
Change page

Anatomía de los contratos inteligentes

Última edición: @506utyutyuty(opens in a new tab), 1 de abril de 2024

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úrate de haber leído primero la documentación 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 almacenamiento o memoria. 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 Solidity
2contract SimpleStorage {
3 uint storedData; // variable de estado
4 // ...
5}
Copiar
1# ejemplo de Vyper
2storedData: int128
Copiar

Si ya has utilizado lenguajes de programación orientados a objetos, probablemente estarás familiarizado con la mayoría de tipos de datos. Sin embargo, la dirección debe ser nueva para ti si eres nuevo en el desarrollo de Ethereum.

Una variable de tipo dirección 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 de tipo real, racional o integradores
  • Literales de cadenas de caracteres
  • Literales en base hexadecimal
  • Enumeraciones

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.

Obtén más información sobre cómo la EVM almacena datos (almacenamiento, memoria y pila) en la documentación de Solidity(opens 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:

PropiedadVariable de estadoDescripción
block.timestampuint256Marca de tiempo del bloque actual
msg.senderdirecciónRemitente 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:

  • Internas: Estas no crean una llamada a la EVM.
    • Solo se puede acceder a las funciones internas y a las variables de estado internamente (es decir, desde el contrato actual o los contratos que derivan de él)
  • Externas: Estas 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 f no puede llamarse internamente (es decir, f() no funciona, pero this.f() funciona).

También pueden ser públicas o privadas.

  • las funciones públicas pueden llamarse internamente desde dentro del contrato o externamente a través de mensajes
  • las funciones privadas solo son visibles para el contrato en el que están definidas y no en 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 Solidity
2function update_name(string value) public {
3 dapp_name = value;
4}
Copiar
  • El parámetro valor del tipo string se transfiere a la función: update_name
  • Se declara pública, lo que significa que cualquiera puede acceder a ella.
  • No está declarada view para solo lectura, por lo que puede modificar el estado del contrato.

Funciones de visualización

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 Solidity
2function balanceOf(address _owner) public view returns (uint256 _balance) {
3 return ownerPizzaCount[_owner];
4}
Copiar
1dappName: public(string)
2
3@view
4@public
5def readName() -> string:
6 return dappName
Copiar

Qué se considera modificar un estado:

  1. Escribir a variables de estado.
  2. Emisión de eventos(opens in a new tab).
  3. Creando otros contratos(opens in a new tab).
  4. Usar la variable selfdestruct.
  5. Enviae ethers mediante llamadas.
  6. Llamar a cualquier función no marcada como sólo lectura view o pure.
  7. Usar llamadas de bajo nivel.
  8. Utilizando un ensamblaje en línea que contiene ciertos códigos de operador.

Funciones de constructor

Las funciones constructor solo se ejecutan una vez cuando el contrato es implementado por primera vez. Al igual que ocurre con constructor en muchos otros lenguajes de programación basados en clases, estas funciones a menudo inicializan variables de estado a sus valores especificados.

1// Ejemplo de Solidity
2// Inicializa los datos del contrato, estableciendo el `propietario`
3// Establece 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 en la transacción dada,
7 // tales como la dirección del remitente y el valor 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-properties
9 owner = msg.sender;
10}
Mostrar todo
Copiar
1# Ejemplo en Vyper
2
3@external
4def __init__(_beneficiary: address, _bidding_time: uint256):
5 self.beneficiary = _beneficiary
6 self.auctionStart = block.timestamp
7 self.auctionEnd = self.auctionStart + _bidding_time
Copiar

Funciones integradas

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(): Solidity
  • send(address) – Vyper

Esto permite que los contratos envíen ETH a otras cuentas.

Funciones de escritura

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;
2
3contract ExampleDapp {
4 string dapp_name; // state variable
5
6 // Called when the contract is deployed and initializes the value
7 constructor() public {
8 dapp_name = "My Example dapp";
9 }
10
11 // Get Function
12 function read_name() public view returns(string) {
13 return dapp_name;
14 }
15
16 // Set Function
17 function update_name(string value) public {
18 dapp_name = value;
19 }
20}
Mostrar todo
Copiar

Un 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 le permiten comunicarse con su contrato inteligente desde su front-end u otras aplicaciones de suscripción. Cuando se mina una transacción, los contratos inteligentes pueden emitir eventos y escribir registros en la cadena de bloques que el front-end pueda procesar.

Ejemplos anotados

Estos son algunos ejemplos escritos en Solidity. Si quiere experimentar con el código, puede interactuar con ellos en Remix(opens in a new tab).

Hello World

1// Especifica la versión de Solidity, utilizando la versión semántica.
2// Más información: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragmma
3pragma solidity ^0.5.10;
4
5// Define un contrato llamado `HelloWorld`.
6// Un contrato es una colección de funciones y datos (su estado).
7// Una vez desplegado, un contrato reside en una dirección específica en la blockchain de Ethereum.
8// Más información: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // Declara una variable de estado `message` del 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 contrato
14 // y crea una función que otros contratos o clientes pueden llamar para acceder al valor.
15 string public message;
16
17 // Similar a muchos idiomas orientados a objetos basados en clases, un constructor es
18 // una función especial que sólo se ejecuta cuando se crea un contrato.
19 // Los constructores se utilizan para inicializar los datos del contrato.
20 // Más información: https://solidity.readthedocs.io/es/v0.5.10/contracts. tml#constructors
21 constructor(string memory initMessage) public {
22 // Acepta un argumento de cadena `initMessage` y establece el valor
23 // en la variable de almacenamiento `message` del contrato).
24 message = initMessage;
25 }
26
27 // Una función pública que acepta un argumento de cadena
28 // y actualiza la variable de almacenamiento `message`.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Mostrar todo
Copiar

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Una `dirección` es comparable a una dirección de correo electrónico - se usa para identificar una cuenta en Ethereum.
5 // Direcciones pueden representar un contrato inteligente o una cuenta externa (de usuario).
6 // Más información: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Un `mapping` es esencialmente una estructura de datos de tabla hash.
10 // Este `mapping` asigna un entero sin signo (el saldo del token) a una dirección (el titular del token).
11 // Más información: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapping (address => uint) public balances;
13
14 // Los eventos permiten registrar la actividad en la blockchain.
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/es/v0.5.10/contracts. tml#events
17 event Transfer(address from, address to, uint amount);
18
19 // Inicializa los datos del contrato establecer el `dueño`
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 en la transacción dada,
24 // tales como la dirección del remitente y el valor 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-properties
26 owner = msg.sender;
27 }
28
29 // Crea una cantidad de nuevos tokens y los envía a una dirección.
30 function mint(address receiver, uint amount) public {
31 // `requiere` es una estructura de control utilizada para hacer cumplir ciertas condiciones.
32 // Si una instrucción `require` evalúa a `falso`, se activa una excepción,
33 // la cual 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-exceptions
35
36 // Sólo el propietario del contrato puede llamar a esta función
37 require(msg.sender == owner, "You are not the owner.");
38
39 // Impone una cantidad máxima de tokens
40 require(amount < 1e60, "Maximum issuance exceeded");
41
42 // Aumenta el saldo del `receiver` en `amount`.
43 balances[receiver] += amount;
44 }
45
46 // Envía una cantidad de tokens existentes de cualquier llamante a una dirección.
47 function transfer(address receiver, uint amount) public {
48 // El remitente debe tener suficientes tokens para enviar
49 require(amount <= balances[msg.sender], "Insufficient balance.");
50
51 // Ajusta el saldo del token de las dos direcciones
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Emite el evento definido anteriormente
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Mostrar todo
Copiar

Activo digital único

1pragma solidity ^0.5.10;
2
3// Importa símbolos de otros archivos al contrato actual.
4// En este caso, una serie de contratos de ayuda de OpenZeppelin.
5// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files
6
7import "../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";
11
12// The `is` keyword is used to inherit functions and keywords from external contracts.
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#inheritance
15contract 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:
18https://docs.openzeppelin.com/contracts/3. /api/math#SafeMath
19 using SafeMath for uint256;
20
21 // Las variables de estado constantes en Solidity son similares a otros idiomas
22 // pero debe asignar desde una expresión que es constante en el momento de compilar.
23 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables
24 uint256 constant dnaDigits = 10;
25 uint256 constant dnaModulus = 10 ** dnaDigits;
26 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
27
28 // Struct types let you define your own type
29 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
30 struct Pizza {
31 string name;
32 uint256 dna;
33 }
34
35 // Creates an empty array of Pizza structs
36 Pizza[] public pizzas;
37
38 // Mapping from pizza ID to its owner's address
39 mapping(uint256 => address) public pizzaToOwner;
40
41 // Mapping from owner's address to number of owned token
42 mapping(address => uint256) public ownerPizzaCount;
43
44 // Mapping from token ID to approved address
45 mapping(uint256 => address) pizzaApprovals;
46
47 // You can nest mappings, this example maps owner to operator approvals
48 mapping(address => mapping(address => bool)) private operatorApprovals;
49
50 // Internal function to create a random Pizza from string (name) and DNA
51 function _createPizza(string memory _name, uint256 _dna)
52 // The `internal` keyword means this function is only visible
53 // within this contract and contracts that derive this contract
54 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
55 internal
56 // `isUnique` is a function modifier that checks if the pizza already exists
57 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
58 isUnique(_name, _dna)
59 {
60 // Adds Pizza to array of Pizzas and get id
61 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
62
63 // Checks that Pizza owner is the same as current user
64 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
65
66 // note that address(0) is the zero address,
67 // indicating that pizza[id] is not yet allocated to a particular user.
68
69 assert(pizzaToOwner[id] == address(0));
70
71 // Maps the Pizza to the owner
72 pizzaToOwner[id] = msg.sender;
73 ownerPizzaCount[msg.sender] = SafeMath.add(
74 ownerPizzaCount[msg.sender],
75 1
76 );
77 }
78
79 // Creates a random Pizza from string (name)
80 function createRandomPizza(string memory _name) public {
81 uint256 randDna = generateRandomDna(_name, msg.sender);
82 _createPizza(_name, randDna);
83 }
84
85 // Generates random DNA from string (name) and address of the owner (creator)
86 function generateRandomDna(string memory _str, address _owner)
87 public
88 // Functions marked as `pure` promise not to read from or modify the state
89 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
90 pure
91 returns (uint256)
92 {
93 // Generates random uint from string (name) + address (owner)
94 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
95 uint256(_owner);
96 rand = rand % dnaModulus;
97 return rand;
98 }
99
100 // Returns array of Pizzas found by owner
101 function getPizzasByOwner(address _owner)
102 public
103 // Functions marked as `view` promise not to modify state
104 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
105 view
106 returns (uint256[] memory)
107 {
108 // Uses the `memory` storage location to store values only for the
109 // lifecycle of this function call.
110 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack
111 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);
112 uint256 counter = 0;
113 for (uint256 i = 0; i < pizzas.length; i++) {
114 if (pizzaToOwner[i] == _owner) {
115 result[counter] = i;
116 counter++;
117 }
118 }
119 return result;
120 }
121
122 // Transfers Pizza and ownership to other address
123 function transferFrom(address _from, address _to, uint256 _pizzaId) public {
124 require(_from != address(0) && _to != address(0), "Invalid address.");
125 require(_exists(_pizzaId), "Pizza does not exist.");
126 require(_from != _to, "Cannot transfer to the same address.");
127 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
128
129 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
130 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
131 pizzaToOwner[_pizzaId] = _to;
132
133 // Emits event defined in the imported IERC721 contract
134 emit Transfer(_from, _to, _pizzaId);
135 _clearApproval(_to, _pizzaId);
136 }
137
138 /**
139 * Safely transfers the ownership of a given token ID to another address
140 * If the target address is a contract, it must implement `onERC721Received`,
141 * which is called upon a safe transfer, and return the magic value
142 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
143 * otherwise, the transfer is reverted.
144 */
145 function safeTransferFrom(address from, address to, uint256 pizzaId)
146 public
147 {
148 // solium-disable-next-line arg-overflow
149 this.safeTransferFrom(from, to, pizzaId, "");
150 }
151
152 /**
153 * Safely transfers the ownership of a given token ID to another address
154 * If the target address is a contract, it must implement `onERC721Received`,
155 * which is called upon a safe transfer, and return the magic value
156 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
157 * otherwise, the transfer is reverted.
158 */
159 function safeTransferFrom(
160 address from,
161 address to,
162 uint256 pizzaId,
163 bytes memory _data
164 ) public {
165 this.transferFrom(from, to, pizzaId);
166 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implement onERC721Received.");
167 }
168
169 /**
170 * Internal function to invoke `onERC721Received` on a target address
171 * The call is not executed if the target address is not a contract
172 */
173 function _checkOnERC721Received(
174 address from,
175 address to,
176 uint256 pizzaId,
177 bytes memory _data
178 ) internal returns (bool) {
179 if (!isContract(to)) {
180 return true;
181 }
182
183 bytes4 retval = IERC721Receiver(to).onERC721Received(
184 msg.sender,
185 from,
186 pizzaId,
187 _data
188 );
189 return (retval == _ERC721_RECEIVED);
190 }
191
192 // Burns a Pizza - destroys Token completely
193 // The `external` function modifier means this function is
194 // part of the contract interface and other contracts can call it
195 function burn(uint256 _pizzaId) external {
196 require(msg.sender != address(0), "Invalid address.");
197 require(_exists(_pizzaId), "Pizza does not exist.");
198 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
199
200 ownerPizzaCount[msg.sender] = SafeMath.sub(
201 ownerPizzaCount[msg.sender],
202 1
203 );
204 pizzaToOwner[_pizzaId] = address(0);
205 }
206
207 // Returns count of Pizzas by address
208 function balanceOf(address _owner) public view returns (uint256 _balance) {
209 return ownerPizzaCount[_owner];
210 }
211
212 // Returns owner of the Pizza found by id
213 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
214 address owner = pizzaToOwner[_pizzaId];
215 require(owner != address(0), "Invalid Pizza ID.");
216 return owner;
217 }
218
219 // Approves other address to transfer ownership of Pizza
220 function approve(address _to, uint256 _pizzaId) public {
221 require(msg.sender == pizzaToOwner[_pizzaId], "Must be the Pizza owner.");
222 pizzaApprovals[_pizzaId] = _to;
223 emit Approval(msg.sender, _to, _pizzaId);
224 }
225
226 // Returns approved address for specific Pizza
227 function getApproved(uint256 _pizzaId)
228 public
229 view
230 returns (address operator)
231 {
232 require(_exists(_pizzaId), "Pizza does not exist.");
233 return pizzaApprovals[_pizzaId];
234 }
235
236 /**
237 * Private function to clear current approval of a given token ID
238 * Reverts if the given address is not indeed the owner of the token
239 */
240 function _clearApproval(address owner, uint256 _pizzaId) private {
241 require(pizzaToOwner[_pizzaId] == owner, "Must be pizza owner.");
242 require(_exists(_pizzaId), "Pizza does not exist.");
243 if (pizzaApprovals[_pizzaId] != address(0)) {
244 pizzaApprovals[_pizzaId] = address(0);
245 }
246 }
247
248 /*
249 * Sets or unsets the approval of a given operator
250 * An operator is allowed to transfer all tokens of the sender on their behalf
251 */
252 function setApprovalForAll(address to, bool approved) public {
253 require(to != msg.sender, "Cannot approve own address");
254 operatorApprovals[msg.sender][to] = approved;
255 emit ApprovalForAll(msg.sender, to, approved);
256 }
257
258 // Tells whether an operator is approved by a given owner
259 function isApprovedForAll(address owner, address operator)
260 public
261 view
262 returns (bool)
263 {
264 return operatorApprovals[owner][operator];
265 }
266
267 // Takes ownership of Pizza - only for approved users
268 function takeOwnership(uint256 _pizzaId) public {
269 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
270 address owner = this.ownerOf(_pizzaId);
271 this.transferFrom(owner, msg.sender, _pizzaId);
272 }
273
274 // Checks if Pizza exists
275 function _exists(uint256 pizzaId) internal view returns (bool) {
276 address owner = pizzaToOwner[pizzaId];
277 return owner != address(0);
278 }
279
280 // Checks if address is owner or is approved to transfer Pizza
281 function _isApprovedOrOwner(address spender, uint256 pizzaId)
282 internal
283 view
284 returns (bool)
285 {
286 address owner = pizzaToOwner[pizzaId];
287 // Disable solium check because of
288 // https://github.com/duaraghav8/Solium/issues/175
289 // solium-disable-next-line operator-whitespace
290 return (spender == owner ||
291 this.getApproved(pizzaId) == spender ||
292 this.isApprovedForAll(owner, spender));
293 }
294
295 // Check if Pizza is unique and doesn't exist yet
296 modifier isUnique(string memory _name, uint256 _dna) {
297 bool result = true;
298 for (uint256 i = 0; i < pizzas.length; i++) {
299 if (
300 keccak256(abi.encodePacked(pizzas[i].name)) ==
301 keccak256(abi.encodePacked(_name)) &&
302 pizzas[i].dna == _dna
303 ) {
304 result = false;
305 }
306 }
307 require(result, "Pizza with such name already exists.");
308 _;
309 }
310
311 // Returns whether the target address is a contract
312 function isContract(address account) internal view returns (bool) {
313 uint256 size;
314 // Currently there is no better way to check if there is a contract in an address
315 // than to check the size of the code at that address.
316 // En https://ethereum.stackexchange.com/a/14016/36603
317 // podrás consultar más detalles sobre cómo funciona esto.
318 // TODO Verifica esto de nuevo antes de el lanzamiento de Serenity, porque todas las direcciones serán
319 // contratos entonces.
320 // solium-disable-next-line security/no-inline-assembly
321 assembly {
322 size := extcodesize(account)
323 }
324 return size > 0;
325 }
326}
Mostrar todo
Copiar

Más información

Revise la documentación de Solidity y Vyper para ver una descripción más completa de los contratos inteligentes:

¿Le ha resultado útil este artículo?