Interakcje z innymi kontraktami od Solidity
Z poprzednich samouczków dowiedzieliśmy się jak wdrożyć swój pierwszy inteligentny kontrakt i dodać do niego kilka funkcji, takich jak kontrola dostępu za pomocą modyfikatorów lub obsługa błędów w Solidity. Z tego samouczka dowiemy się, jak wdrożyć inteligentny kontrakt z istniejącego kontraktu i pracować na nim.
Stworzymy kontrakt, który umożliwi każdemu posiadanie własnego inteligentnego kontraktu Counter
, tworząc dla niego fabrykę o nazwie CounterFactory
. Pierwszy jest kod naszego początkowego inteligentnego kontraktu 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}34Pokaż wszystkoKopiuj
Zwróć uwagę, że nieznacznie zmodyfikowaliśmy kod kontraktu, aby śledzić adres fabryki i adres właściciela umowy. Gdy wywołasz kod kontraktu z innego kontraktu, msg.sender odniesie się do adresu naszej fabryki kontraktowej. Jest to bardzo ważny punkt do zrozumienia, ponieważ używanie kontraktu do interakcji z innymi kontraktami jest powszechną praktyką. Dlatego w skomplikowanych przypadkach należy zadbać o to, kto jest nadawcą.
W tym celu dodaliśmy również modyfikator onlyFactory
, który zapewnia, że funkcja zmiany stanu może być wywołana tylko przez fabrykę, która przekaże oryginalny obiekt wywołujący jako parametr.
W naszej nowej CounterFactory
, która będzie zarządzać wszystkimi innymi licznikami, dodamy mapowanie, które skojarzy właściciela z adresem jego kontraktu counter:
1mapping(address => Counter) _counters;2Kopiuj
W Ethereum mapowanie jest równoważne obiektom w javascript, umożliwiają one mapowanie klucza typu A do wartości typu B. W tym przypadku mapujemy adres właściciela z instancją jego kontraktu counter.
Utworzenie nowego kontraktu Counter dla kogoś będzie wyglądać tak:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }5Kopiuj
Najpierw sprawdzamy, czy osoba jest już właścicielem Counter. Jeśli nie jest właścicielem counter, natychmiastowo przekazujemy jego adres do konstruktora Counter
i przypisujemy nowo utworzoną instancję do mapowania.
Aby uzyskać liczbę konkretnego Counter, powinno to wyglądać tak:
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}9Kopiuj
Pierwsza funkcja sprawdza, czy kontrakt Counter istnieje dla danego adresu, a następnie wywołuje metodę getCount
z instancji. Druga funkcja: getMyCount
to tylko krótki koniec do przekazania msg.sender bezpośrednio do funkcji getCount
.
Funkcja increment
jest dość podobna, ale przekazuje oryginalnego nadawcę transakcji do kontraktu Counter
:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }5Kopiuj
Zauważ, że jeśli zostaniesz wywołany wiele razy, nasz counter może paść ofiarą przepełnienia. Powinieneś użyć biblioteki SafeMath w możliwie największym stopniu, aby chronić przed tym przypadkiem.
Aby wdrożyć nasz kontrakt, musisz podać zarówno kod CounterFactory
, jak i Counter
. Podczas wdrażania na przykład w Remix musisz wybrać CounterFactory.
Oto pełny kod:
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}59Pokaż wszystkoKopiuj
Po skompilowaniu wybierz w sekcji wdrażanie Remix fabrykę do wdrożenia:
Następnie możesz pobawić się swoją fabryką kontraktową i sprawdzić, jak zmienia się wartość. Jeśli chcesz wywołać inteligentny kontrakt z innego adresu, musisz zmienić adres w wyborze konta w Remix.