Przejdź do głównej zawartości

Interakcje z innymi kontraktami z poziomu Solidity

smart kontrakty
solidity
remix
wdrażanie
kompozycyjność
Zaawansowane funkcje
jdourlens
5 kwietnia 2020
4 minuta czytania

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;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Nie jesteś właścicielem tego kontraktu");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Musisz użyć fabryki");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
Pokaż wszystko

Zwróć 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}
5
6function 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;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Nie jesteś właścicielem tego kontraktu");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Musisz użyć fabryki");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
Pokaż wszystko

Po skompilowaniu, w sekcji wdrażania w Remix, wybierz fabrykę do wdrożenia:

Wybór fabryki do wdrożenia w Remix

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

Czy ten samouczek był pomocny?