Przejdź do głównej zawartości

Interakcje z innymi kontraktami od Solidity

inteligentne kontraktysolidityremixfabrykiwdrożeniekompozycyjność
Zaawansowane
jdourlens
EthereumDev(opens in a new tab)
5 kwietnia 2020
3 minuta czytania minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

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(opens in a new tab) lub obsługa błędów w Solidity(opens in a new tab). 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;
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, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
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
Kopiuj

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;
Kopiuj

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 }
Kopiuj

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}
5
6function getMyCount() public view returns (uint256) {
7 return (getCount(msg.sender));
8}
Kopiuj

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 }
Kopiuj

Zauważ, że jeśli zostaniesz wywołany wiele razy, nasz counter może paść ofiarą przepełnienia. Powinieneś użyć biblioteki SafeMath(opens in a new tab) 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;
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, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
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
Kopiuj

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

Wybór fabryki do wdrożenia w Remix

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.

Ostatnia edycja: @Beas(opens in a new tab), 31 października 2023

Czy ten samouczek był pomocny?