Interazione con altri contratti da Solidity
Nei tutorial precedenti abbiamo imparato molto su come distribuire il primo Smart Contract e aggiungervi alcune funzionalità come il controllo degli accessi con modificatori(opens in a new tab) o la gestione degli errori in Solidity(opens in a new tab). In questo tutorial impareremo come distribuire uno Smart Contract da uno esistente e interagirvi.
Creeremo un contratto che permetta a chiunque ad avere uno Smart Contract Counter
creando una factory associata, il cui nome sarà CounterFactory
. Prima di tutto, ecco il codice del nostro Smart Contract iniziale Counter
:
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 tuttoCopia
Nota: abbiamo leggermente modificato il codice del contratto per tenere traccia dell'indirizzo della factory e dell'indirizzo del proprietario del contratto. Quando si chiama il codice di un contratto da un altro contratto, msg.sender fa riferimento all'indirizzo della factory del contratto. È molto importante comprendere questo concetto, perché usare un contratto per interagire con altri contratti è una prassi comune. Dovresti quindi saper gestire il mittente nei casi complessi.
Per questo motivo abbiamo aggiunto anche un modificatore onlyFactory
che fa in modo che la funzione di cambiamento dello stato sia chiamabile solo dalla factory che passerà il chiamante originale come parametro.
Nella nostra nuova CounterFactory
che gestirà tutti gli altri Counter aggiungeremo un mapping che assocerà un proprietario all'indirizzo del relativo contratto Counter:
1mapping(address => Counter) _counters;Copia
In Ethereum, i mapping equivalgono agli oggetti di Javascript, che permettono di mappare una chiave di tipo A a un valore di tipo B. In questo caso mappiamo l'indirizzo di un proprietario all'istanza del suo Counter.
Istanziare un nuovo Counter per un utente sarà più o meno:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Copia
Prima controlliamo se la persona possiede già un Counter. Se non lo possiede, istanziamo un nuovo Counter passandone l'indirizzo al costruttore del Counter
e assegniamo l'istanza appena creata al mapping.
Ottenere il conteggio di un Counter specifico significa:
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}Copia
La prima funzione controlla se il contratto Counter esiste per un determinato indirizzo e poi chiama il metodo getCount
dall'istanza. La seconda funzione: getMyCount
è passa direttamente msg.sender alla funzione getCount
.
La funzione increment
è abbastanza simile ma passa il mittente della transazione originale al contratto Counter
:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }Copia
Nota: se chiamato troppe volte, il Counter potrebbe rimanere vittima di overflow. È consigliabile usare la libreria di SafeMath(opens in a new tab) il più possibile per evitare questa eventualità.
Per distribuire il contratto, dovrai fornire sia il codice della CounterFactory
che il Counter
. Quando si esegue la distribuzione ad esempio in Remix, è necessario 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 tuttoCopia
Dopo la compilazione, nella sezione di distribuzione di Remix si selezionerà la factory da distribuire:
A questo punto puoi fare esperimenti la factory del contratto e controllare il valore che cambia. Se vorresti chiamare il contratto intelligente da un indirizzo differente, dovrai cambiare l'indirizzo nella selezione Conto di Remix.
Ultima modifica: @nhsz(opens in a new tab), 15 agosto 2023