Interakcje z innymi kontraktami z poziomu Solidity
W poprzednich samouczkach wiele się nauczyliśmy, na przykład jak wdrożyć swój pierwszy inteligentny kontrakt, jak dodać do niego pewne funkcje, takie jak kontrola dostępu za pomocą modyfikatorówopens in a new tab czy obsługa błędów w Solidityopens in a new tab. W tym samouczku dowiemy się, jak wdrożyć inteligentny kontrakt z istniejącego kontraktu i wejść z nim w interakcję.
Stworzymy kontrakt, który umożliwi każdemu posiadanie własnego inteligentnego kontraktu Counter, tworząc dla niego fabrykę o nazwie CounterFactory. Na początek, oto 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, "Nie jesteś właścicielem tego kontraktu");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Musisz użyć fabryki");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}Pokaż wszystkoZwróć uwagę, że nieznacznie zmodyfikowaliśmy kod kontraktu, aby śledzić adres fabryki i adres właściciela kontraktu. Gdy wywołujesz kod kontraktu z innego kontraktu, msg.sender będzie odnosić się do adresu naszej fabryki kontraktów. To naprawdę ważna kwestia do zrozumienia, ponieważ używanie kontraktu do interakcji z innymi kontraktami jest powszechną praktyką. Dlatego w złożonych przypadkach należy uważać, kto jest nadawcą.
W tym celu dodaliśmy również modyfikator onlyFactory, który zapewnia, że funkcja zmieniająca stan może być wywołana tylko przez fabrykę, która przekaże pierwotnego wywołującego jako parametr.
Wewnątrz naszej nowej fabryki CounterFactory, która będzie zarządzać wszystkimi innymi kontraktami Counter, dodamy mapowanie, które powiąże właściciela z adresem jego kontraktu Counter:
1mapping(address => Counter) _counters;W Ethereum mapowania są odpowiednikiem obiektów w JavaScript. Umożliwiają mapowanie klucza typu A na wartość typu B. W tym przypadku mapujemy adres właściciela z instancją jego kontraktu Counter.
Instancjonowanie nowego kontraktu Counter dla kogoś będzie wyglądać następująco:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Najpierw sprawdzamy, czy dana osoba jest już właścicielem kontraktu Counter. Jeśli nie posiada on kontraktu Counter, tworzymy jego nową instancję, przekazując jego adres do konstruktora Counter i przypisując nowo utworzoną instancję do mapowania.
Aby uzyskać stan licznika dla konkretnego kontraktu Counter, należy postąpić następująco:
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}Pierwsza funkcja sprawdza, czy kontrakt Counter istnieje dla danego adresu, a następnie wywołuje metodę getCount z instancji. Druga funkcja, getMyCount, to tylko skrót do przekazania msg.sender bezpośrednio do funkcji getCount.
Funkcja increment jest dość podobna, ale przekazuje pierwotnego nadawcę transakcji do kontraktu Counter:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }Należy pamiętać, że przy zbyt wielu wywołaniach w naszym liczniku może dojść do przepełnienia. Powinieneś używać biblioteki SafeMathopens in a new tab tak często, jak to możliwe, aby zabezpieczyć się przed taką ewentualnością.
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, "Nie jesteś właścicielem tego kontraktu");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Musisz użyć fabryki");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}Pokaż wszystkoPo skompilowaniu, w sekcji wdrażania w Remix, wybierz fabrykę do wdrożenia:
Następnie możesz pobawić się swoją fabryką kontraktów i sprawdzić, jak zmienia się wartość. Jeśli chcesz wywołać inteligentny kontrakt z innego adresu, musisz zmienić adres w polu wyboru konta w aplikacji Remix.
Strona ostatnio zaktualizowana: 15 sierpnia 2023
