Interagire con altri contratti da Solidity
Nei tutorial precedenti abbiamo imparato molto su come distribuire il tuo primo contratto intelligente e aggiungervi alcune funzionalità come controllare l'accesso con i modificatori (opens in a new tab) o gestire gli errori in Solidity (opens in a new tab). In questo tutorial impareremo come distribuire un contratto intelligente da un contratto esistente e interagirvi.
Creeremo un contratto che consenta a chiunque di avere il proprio contratto intelligente Counter creandone una fabbrica (factory), il cui nome sarà CounterFactory. Innanzitutto, ecco il codice del nostro contratto intelligente Counter iniziale:
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}Mostra tuttoNota che abbiamo leggermente modificato il codice del contratto per tenere traccia dell'indirizzo della factory e dell'indirizzo del proprietario del contratto. Quando chiami il codice di un contratto da un altro contratto, il msg.sender si riferirà all'indirizzo della nostra factory del contratto. Questo è un punto davvero importante da comprendere poiché usare un contratto per interagire con altri contratti è una pratica comune. Dovresti quindi prestare attenzione a chi è il mittente nei casi complessi.
Per questo abbiamo anche aggiunto un modificatore onlyFactory che si assicura che la funzione che modifica lo stato possa essere chiamata solo dalla factory, la quale passerà il chiamante originale come parametro.
All'interno della nostra nuova CounterFactory che gestirà tutti gli altri Counter, aggiungeremo una mappatura (mapping) che assocerà un proprietario all'indirizzo del suo contratto counter:
1mapping(address => Counter) _counters;In Ethereum, le mappature sono l'equivalente degli oggetti in javascript, consentono di mappare una chiave di tipo A a un valore di tipo B. In questo caso mappiamo l'indirizzo di un proprietario con l'istanza del suo Counter.
L'istanziazione di un nuovo Counter per qualcuno avrà questo aspetto:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Per prima cosa controlliamo se la persona possiede già un counter. Se non possiede un counter, istanziamo un nuovo counter passando il suo indirizzo al costruttore di Counter e assegniamo l'istanza appena creata alla mappatura.
Per ottenere il conteggio di un Counter specifico, l'aspetto sarà questo:
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}La prima funzione controlla se il contratto Counter esiste per un dato indirizzo e poi chiama il metodo getCount dall'istanza. La seconda funzione: getMyCount è solo una scorciatoia per passare il msg.sender direttamente alla funzione getCount.
La funzione increment è abbastanza simile ma passa il mittente originale della transazione al contratto Counter:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }Nota che se chiamato troppe volte, il nostro counter potrebbe essere vittima di un overflow. Dovresti usare la libreria SafeMath (opens in a new tab) il più possibile per proteggerti da questo possibile caso.
Per distribuire il nostro contratto, dovrai fornire sia il codice della CounterFactory che del Counter. Quando distribuisci, ad esempio in Remix, dovrai selezionare CounterFactory.
Ecco il codice 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}Mostra tuttoDopo la compilazione, nella sezione di distribuzione di Remix selezionerai la factory da distribuire:
Quindi puoi giocare con la tua factory del contratto e controllare il cambiamento del valore. Se desideri chiamare il contratto intelligente da un indirizzo diverso, dovrai cambiare l'indirizzo nella selezione Account di Remix.
Ultimo aggiornamento della pagina: 15 agosto 2023
