Interagire con altri contratti da Solidity
Nei tutorial precedenti abbiamo imparato molto su come distribuire il tuo primo smart contract 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 uno smart contract da un contratto esistente e interagirvi.
Creeremo un contratto che consenta a chiunque di avere il proprio smart contract Counter creandone una factory, il cui nome sarà CounterFactory. Innanzitutto, ecco il codice del nostro smart contract Counter iniziale:
pragma solidity 0.5.17;
contract Counter {
uint256 private _count;
address private _owner;
address private _factory;
modifier onlyOwner(address caller) {
require(caller == _owner, "You're not the owner of the contract");
_;
}
modifier onlyFactory() {
require(msg.sender == _factory, "You need to use the factory");
_;
}
constructor(address owner) public {
_owner = owner;
_factory = msg.sender;
}
function getCount() public view returns (uint256) {
return _count;
}
function increment(address caller) public onlyFactory onlyOwner(caller) {
_count++;
}
}
Nota che abbiamo modificato leggermente 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, msg.sender si riferirà all'indirizzo della nostra factory di contratti. 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 sia 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 del nostro nuovo CounterFactory che gestirà tutti gli altri Counter, aggiungeremo una mappatura che assocerà un proprietario all'indirizzo del suo contratto counter:
mapping(address => Counter) _counters;
In Ethereum, le mappature (mapping) 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:
function createCounter() public {
require (_counters[msg.sender] == Counter(0));
_counters[msg.sender] = new Counter(msg.sender);
}
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:
function getCount(address account) public view returns (uint256) {
require (_counters[account] != Counter(0));
return (_counters[account].getCount());
}
function getMyCount() public view returns (uint256) {
return (getCount(msg.sender));
}
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 msg.sender direttamente alla funzione getCount.
La funzione increment è abbastanza simile ma passa il mittente originale della transazione al contratto Counter:
function increment() public {
require (_counters[msg.sender] != Counter(0));
Counter(_counters[msg.sender]).increment(msg.sender);
}
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 di CounterFactory che di Counter. Quando distribuisci, ad esempio in Remix, dovrai selezionare CounterFactory.
Ecco il codice completo:
pragma solidity 0.5.17;
contract Counter {
uint256 private _count;
address private _owner;
address private _factory;
modifier onlyOwner(address caller) {
require(caller == _owner, "You're not the owner of the contract");
_;
}
modifier onlyFactory() {
require(msg.sender == _factory, "You need to use the factory");
_;
}
constructor(address owner) public {
_owner = owner;
_factory = msg.sender;
}
function getCount() public view returns (uint256) {
return _count;
}
function increment(address caller) public onlyFactory onlyOwner(caller) {
_count++;
}
}
contract CounterFactory {
mapping(address => Counter) _counters;
function createCounter() public {
require (_counters[msg.sender] == Counter(0));
_counters[msg.sender] = new Counter(msg.sender);
}
function increment() public {
require (_counters[msg.sender] != Counter(0));
Counter(_counters[msg.sender]).increment(msg.sender);
}
function getCount(address account) public view returns (uint256) {
require (_counters[account] != Counter(0));
return (_counters[account].getCount());
}
function getMyCount() public view returns (uint256) {
return (getCount(msg.sender));
}
}
Dopo la compilazione, nella sezione di distribuzione di Remix selezionerai la factory da distribuire:
Quindi puoi giocare con la tua factory di contratti e controllare il cambiamento del valore. Se desideri chiamare lo smart contract da un indirizzo diverso, dovrai cambiare l'indirizzo nella selezione Account di Remix.
Ultimo aggiornamento della pagina: 3 marzo 2026
