Ir al contenido principal

Interactúe con otros contratos desde Solidity

contratos inteligentesSolidityremiximplementacióncapacidad de composición
Avanzado
jdourlens
EthereumDev(opens in a new tab)
5 de abril de 2020
4 minuto leído minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

En los tutoriales anteriores aprendimos mucho sobre cómo implementar su primer contrato inteligente y agregar algunas funciones como control de acceso con modificadores(opens in a new tab) o manejo de errores en Solidity(opens in a new tab). En este tutorial aprenderemos cómo implementar un contrato inteligente desde un contrato existente e interactuar con él.

Vamos a hacer un contrato que permita a cualquiera tener su propio contrato inteligente de Counter mediante la creación de una fábrica para tal propósito: se llamará CounterFactory. En primer lugar, aquí tenemos el código del contrato inteligente Counter inicial:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
Mostrar todo
Copiar

Tenga en cuenta que modificamos ligeramente el código del contrato para controlar la dirección de la fábrica y la dirección del contrato del propietario. Al hacer una llamada al código de un contrato desde otro contrato, msg.sender hará referencia a la dirección de la fábrica del contrato. Este es un punto realmente importante que hay que entender, ya que utilizar un contrato para interactuar con otros contratos es una práctica común. Por tanto, se debería prestar a atención a quién es el remitente en casos complejos.

Por esta razón, también añadimos un modificador onlyFactory que se asegure de que la función de cambio de estado solo pueda ser invocada por la fábrica que pasará el invocador inicial como parámetro.

Dentro de nuestro CounterFactory que administrará todos los demás Counters, agregaremos un mapeo que asociará a un propieatario con la dirección de este contrato de contador:

1mapping(address => Counter) _counters;
Copiar

En Ethereum, los mapeos, o mappings, son equivalentes a los objetos en JavaScript: permiten asociar una clave de tipo A a un valor de tipo B. En este caso, asociamos la dirección de un propietario con la instancia de su contador.

La instanciación de un Contador nuevo para alguien se verá de la siguiente manera:

1 function createCounter() public {
2 require (_counters[msg.sender] == Counter(0));
3 _counters[msg.sender] = new Counter(msg.sender);
4 }
Copiar

Primero, revisaremos si la persona en cuestión ya es propietaria de un contador. Si ese no es el caso, instanciaremos un nuevo contador pasando su dirección al constructor de Counter y asignar la instancia recientemente creada al mapeo.

Para obtener el conteo de un Contador específico, se verá así:

1function getCount(address account) public view returns (uint256) {
2 require (_counters[account] != Counter(0));
3 return (_counters[account].getCount());
4}
5
6function getMyCount() public view returns (uint256) {
7 return (getCount(msg.sender));
8}
Copiar

La primera función revisa si el contrato del Contador existe para una dirección proporcionada y luego llama al método getCount desde la instancia. La segunda función, getMyCount, es solo un extremo corto para pasar el msg.sender directamente a la función getMyCount.

La función increment es bastante similar, pero pasa el emisor de la transacción original al contrato Counter:

1function increment() public {
2 require (_counters[msg.sender] != Counter(0));
3 Counter(_counters[msg.sender]).increment(msg.sender);
4 }
Copiar

Note que si es llamado muchas veces, nuestro contador podría ser víctima del desbordamiento, u overflow. Debe usar la biblioteca SafeMath(opens in a new tab) tanto como sea posible para evitar esta situación.

Para implementar nuestro contrato, necesitará proporcionar el código de CounterFactory y el Counter. Al realizar la implementación, por ejemplo, en Remix, deberá seleccionar CounterFactory.

Este es el código completo:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
Mostrar todo
Copiar

Luego de la compilación, en la sección de implementación de Remix, debe seleccionar la fábrica a implementar:

Seleccionar la fábrica a implementar en Remix

Luego puede experimentar con su fábrica de contratos y revisar el valor cambiante. Si desea invocar el contrato inteligente desde una dirección diferente, necesitará cambiar la dirección en la selección de Cuenta en Remix.

Última edición: @joseachemazzei(opens in a new tab), 21 de febrero de 2024

¿Le ha resultado útil este tutorial?