Przejdź do głównej treści

Interakcja z innymi kontraktami z poziomu Solidity

inteligentne kontrakty
Solidity
Remix
wdrażanie
kompozycyjność
Zaawansowany
jdourlens
5 kwietnia 2020
4 minut czytania

W poprzednich samouczkach dowiedzieliśmy się wiele o tym, jak wdrożyć swój pierwszy inteligentny kontrakt i dodać do niego pewne funkcje, takie 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). W tym samouczku dowiemy się, jak wdrożyć inteligentny kontrakt z poziomu istniejącego kontraktu i wejść z nim w interakcję.

Stworzymy kontrakt, który umożliwi każdemu posiadanie własnego inteligentnego kontraktu Counter poprzez utworzenie dla niego fabryki, której nazwa będzie brzmieć CounterFactory. Na początek oto kod naszego początkowego inteligentnego kontraktu Counter:

Zauważ, że nieznacznie zmodyfikowaliśmy kod kontraktu, aby śledzić adres fabryki oraz adres właściciela kontraktu. Kiedy wywołujesz kod kontraktu z innego kontraktu, msg.sender będzie odnosić się do adresu naszej fabryki kontraktów. Jest to bardzo ważna kwestia do zrozumienia, ponieważ używanie kontraktu do interakcji z innymi kontraktami jest powszechną praktyką. W złożonych przypadkach należy zatem zwracać uwagę na to, kto jest nadawcą.

W tym celu dodaliśmy również modyfikator onlyFactory, który upewnia się, ż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 CounterFactory, która będzie zarządzać wszystkimi innymi licznikami (Counters), dodamy mapowanie, które powiąże właściciela z adresem jego kontraktu licznika:

mapping(address => Counter) _counters;

W Ethereum mapowania są odpowiednikiem obiektów w języku JavaScript, umożliwiają one zmapowanie klucza typu A na wartość typu B. W tym przypadku mapujemy adres właściciela na instancję jego licznika (Counter).

Utworzenie instancji nowego licznika dla kogoś będzie wyglądać następująco:

  function createCounter() public {
      require (_counters[msg.sender] == Counter(0));
      _counters[msg.sender] = new Counter(msg.sender);
  }

Najpierw sprawdzamy, czy dana osoba posiada już licznik. Jeśli nie posiada licznika, tworzymy instancję nowego licznika, przekazując jej adres do konstruktora Counter i przypisujemy nowo utworzoną instancję do mapowania.

Pobranie wartości konkretnego licznika będzie wyglądać tak:

function getCount(address account) public view returns (uint256) {
    require (_counters[account] != Counter(0));
    return (_counters[account].getCount());
}

function getMyCount() public view returns (uint256) {
    return (getCount(msg.sender));
}

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:

function increment() public {
      require (_counters[msg.sender] != Counter(0));
      Counter(_counters[msg.sender]).increment(msg.sender);
  }

Zauważ, że jeśli zostanie wywołany zbyt wiele razy, nasz licznik może paść ofiarą przepełnienia. Należy w miarę możliwości używać biblioteki SafeMath (opens in a new tab), aby zabezpieczyć się przed takim przypadkiem.

Aby wdrożyć nasz kontrakt, będziesz musiał dostarczyć zarówno kod CounterFactory, jak i Counter. Podczas wdrażania na przykład w Remix, będziesz musiał wybrać CounterFactory.

Oto pełny kod:

Po kompilacji, w sekcji wdrażania Remix wybierzesz fabrykę do wdrożenia:

Selecting the factory to be deployed in Remix

Następnie możesz pobawić się swoją fabryką kontraktów i sprawdzić zmieniającą się wartość. Jeśli chciałbyś wywołać inteligentny kontrakt z innego adresu, będziesz musiał zmienić adres w polu wyboru konta (Account) w Remix.