Interacționează cu alte contracte din Solidity
În tutorialele anterioare ai învățat multe: cum să implementezi primul tău contract inteligent și să adaugi câteva caracteristici la acesta precum controlul accesului cu modificatori sau gestionarea erorilor în Solidity. În acest tutorial vei învăța cum să implementezi un contract inteligent dintr-un contract existent și să interacționezi cu acesta.
Vei crea un contract care permite oricui să aibă propriul contract inteligent Counter
, creând o fabrică pentru acesta, numele ei va fi CounterFactory
. În primul rând aici este codul contractului inteligent inițial 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, "Nu ești proprietarul contractului");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Trebuie să folosești fabrica");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}34Afișează totCopiere
Reține că am modificat ușor codul contractului pentru a urmări adresa fabricii și adresa proprietarului contractului. Când apelezi un cod de contract dintr-un alt contract, msg.sender va face referire la adresa fabricii noastre de contracte. Acesta este un punct foarte important de înțeles, deoarece utilizarea unui contract pentru a interacționa cu alte contracte este o practică comună. Prin urmare, ar trebui să ai grijă de cine este expeditorul în cazuri complexe.
Pentru aceasta am adăugat și un modificator onlyFactory
care se asigură că funcția de modificare a stării poate fi apelată doar de fabrică, care va transmite apelantul inițial ca parametru.
În interiorul noului nostru CounterFactory
, care va gestiona toate celelalte contoare, vom adăuga o mapare care va asocia un proprietar la adresa contractului său:
1mapping(address => Counter) _counters;2Copiere
În Ethereum, maparea este echivalentă cu obiectele din javascript, acestea permit maparea unei chei de tip A la o valoare de tip B. În acest caz, vom mapa adresa unui proprietar cu instanța Counter-ului său.
Crearea unei instanțe de contor nou pentru cineva, va arăta astfel:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }5Copiere
Verificăm mai întâi dacă persoana deține deja un contor. Dacă nu deține un contor, creăm o nouă instanță de contor nou prin trecerea adresei sale la constructorul Counter
și atribuim noua instanță creată la mapare.
Pentru a obține numărul unui contor specific, scriem:
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}9Copiere
Prima funcție verifică dacă contractul de contor există pentru o anumită adresă, apoi apelează metoda getCount
din instanță. A doua funcție: getMyCount
este doar un capăt scurt pentru a trece msg.sender direct la funcția getCount
.
Funcția increment
este destul de asemănătoare, dar transmite expeditorul inițial al tranzacției la contractul Counter
:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }5Copiere
Reține că, dacă este apelat de mai multe ori, contorul nostru ar putea fi victima unui flux excesiv. Trebuie să utilizezi SafeMath Library cât mai mult pentru a te proteja de acest caz posibil.
Pentru a implementa contractul nostru, va trebui să furnizezi atât codul CounterFactory
, cât și Counter
. Când implementezi, de exemplu, în Remix, va trebui să selectezi CounterFactory.
Aici este codul complet:
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, "Nu ești proprietarul contractului");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Trebuie să utilizezi fabrica");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}59Afișează totCopiere
După compilare, în secțiunea Remix Deploy vei selecta fabrica ce va fi implementată:
După aceea, poți să te joci cu fabrica de contract și să verifici cum se modifică valorile. Dacă dorești să apelezi contractul inteligent de la o adresă diferență va trebui să modifici adresa din „Selectare cont” din Remix.