Interactúe con otros contratos desde Solidity
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;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}Mostrar todoCopiar
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}56function 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;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}3435contract CounterFactory {3637 mapping(address => Counter) _counters;3839 function createCounter() public {40 require (_counters[msg.sender] == Counter(0));41 _counters[msg.sender] = new Counter(msg.sender);42 }4344 function increment() public {45 require (_counters[msg.sender] != Counter(0));46 Counter(_counters[msg.sender]).increment(msg.sender);47 }4849 function getCount(address account) public view returns (uint256) {50 require (_counters[account] != Counter(0));51 return (_counters[account].getCount());52 }5354 function getMyCount() public view returns (uint256) {55 return (getCount(msg.sender));56 }5758}Mostrar todoCopiar
Luego de la compilación, en la sección de implementación de Remix, debe seleccionar la fábrica a implementar:
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: @nhsz(opens in a new tab), 15 de agosto de 2023