Kiedy nieistniejące istnieje – funkcja fallback

Przemek/ 27 kwietnia, 2023

Kiedy osoba nieznająca Solidity przegląda różnego rodzaju smart kontrakty, może źle zinterpretować ich działanie. Często dzieje się tak z powodu “smaczków”, które powodują, że kod napisany w tym języku zachowuje się inaczej niż można by się spodziewać. Szczególnie, jeśli osoba ta ma doświadczenie w innych technologiach, może doznać nieprzyjemnego zaskoczenia. Jednym z takich “smaczków” jest tzw. funkcja fallback, a zrozumienie jej działania pozwoli na uniknięcie błędów, które mogą prowadzić do poważnych konsekwencji oraz pozwoli kontynuować w przyszłych wpisach wcześniej poruszane tematy.

Uwaga na awarię

Każdy kontrakt może, ale nie musi, posiadać maksymalnie jedną funkcję zwana fallback, która występuje pod dwoma dozwolonymi sygnaturami:

fallback() external payable

lub

fallback(bytes calldata input) external payable returns (bytes memory output)

ak widać, druga wersja pozwala na przyjęcie dodatkowych parametrów oraz zwracanie wartości. Ponadto, funkcja ta może być oznaczona słowem kluczowym “payable”, co jest wymagane, jeśli chcemy, aby wywoływana metoda mogła przyjmować ether. Nie ma potrzeby dodawania nic więcej, więc przejdźmy do omówienia, jak działa ta funkcja.

Funkcja ta zostanie wywołana jedynie wtedy, gdy spróbujemy “odpalić” na danym kontrakcie metodę, która nie istnieje w nim. Można powiedzieć, że jest to mechanizm awaryjny. Jest to zachowanie zupełnie inne niż w większości języków programowania. Aby lepiej zrozumieć, prześledźmy prosty przykład.

//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

contract Example {
   uint256 public value;

   fallback() external {
       value++;
   }
}

Powyżej znajduje się bardzo prosty kontrakt, który przechowuje jedną wartość oraz posiada zaimplementowaną funkcję fallback, która inkrementuje zmienną. Mimo że nie istnieje bezpośrednia funkcja, która umożliwiłaby zwiększenie liczby przechowywanej przez pole value, jesteśmy w stanie to zrobić przy pomocy naszego awaryjnego mechanizmu. Musimy tylko odpowiednio odwołać się do kontraktu. Zademonstruję to na dwa sposoby. Dla ułatwienia wszystko odbędzie się z udziałem środowiska Remix.

Po wgraniu kontraktu na blockchain (np. testowy), ukaże Ci się taki widok:

Jak widzisz, jedyną dostępną metodą w kontrakcie Example jest ta do odczytu wartości pola value. Jednak, jeśli naciśniemy przycisk Transact, to odpali się nasza funkcja fallback, ponieważ faktycznie, przy jego pomocy, próbujemy wykonać coś, co nie istnieje w kontrakcie. Po wykonaniu operacji, wartość powinna wzrosnąć o jeden.

Jeżeli kontrakt nie posiadałby zaimplementowanej funkcji fallback, wtedy nie byłoby możliwe naciśnięcie przycisku Transact. Polecam sprawdzić i się przekonać 🙂

Zróbmy to raz jeszcze, ale z użyciem innego podejścia, które jeszcze lepiej zobrazuje ten przykład. Dodajmy więc drugi kontrakt:

contract Magic {
   function doit() external {
   }
}

Jest on jeszcze prostszy, ponieważ posiada jedynie funkcję o nazwie doIt, która nic nie robi. Teraz, przy użyciu Remix’a, wykorzystamy interfejs tego kontraktu, aby wywołać tę funkcję na poprzednim kontrakcie. Aby to zrobić, w polu At address wpiszemy adres kontraktu Example i naciśniemy przycisk At address. Po wykonaniu tej operacji, powinien pojawić się taki obraz.

Teraz naciśnij przycisk doIt, a następnie sprawdź ponownie wartość pola value. Efektem będzie ponownie zwiększenie liczby.

Zastosowanie

Możesz zastanawiać się, do czego może służyć funkcja fallback w smart kontraktach. Jeśli masz doświadczenie w programowaniu w innych standardowych językach programowania, sytuacja kiedy wywołanie zadziała, mimo że kod nie posiada takiej metody, mogłaby wydawać się niepożądana. Jednakże w przypadku smart kontraktów istnieje kilka zastosowań związanych z ich specyfiką. Niektóre z nich mogą nie wydawać Ci się sensowne na pierwszy rzut oka, ale jeśli posiadasz już nieco większą wiedzę na temat blockchainowych rozwiązań, będą one oczywiste. W tej chwili skupię się jedynie na opisie tych zastosowań, bez odnoszenia się do przypadków z rzeczywistych protokołów.

Pierwsze zastosowanie, jakie przychodzi na myśl, to sytuacja, kiedy chcielibyśmy stworzyć domyślne zachowanie naszego kontraktu w przypadku, gdy ktoś spróbuje wywołać na nim funkcję, która nie istnieje. Na przykład mogłoby to być zwrócenie jakiejś informacji dotyczącej kodu lub podobnego komunikatu. Choć jest to raczej mało spotykany przypadek, to czasem może się przydać.

Drugie i chyba najczęściej wykorzystywane zastosowanie funkcji fallback polega na użyciu jej do implementacji “aktualizowanych” smart kontraktów w formie proxy. Do zrozumienia tego mechanizmu potrzebny będzie osobny wpis, który pojawi się wkrótce i będzie kontynuacją tematów związanych z modernizacją smart kontraktów. W tym momencie wystarczy zapamiętać, że dzięki funkcji fallback można stworzyć mechanizm, który w stosunkowo łatwy sposób pozwala na aktualizację kodu.

Trzecie zastosowanie polega na umożliwieniu kontraktowi przyjmowania etheru lub innej natywnej kryptowaluty na danym blockchainie. Innymi słowy, jeśli chcemy, aby ktoś mógł bezpośrednio zasilać nasz kontrakt monetami, to implementując funkcję fallback i oznaczając ją dodatkowo jako payable, jesteśmy w stanie to osiągnąć. Chociaż prawdę mówiąc, takie podejście stosowano w przeszłości w przypadku starszych wersji języka Solidity, obecnie istnieje inna “magiczna” funkcja, która jest przeznaczona do tego celu. Jej sygnatura wygląda następująco:

receive() external payable {}

Ten rodzaj funkcji, w odróżnieniu od fallback, uruchamia się tylko wtedy, gdy natywne monety zostaną przesłane bezpośrednio na adres kontraktu, i jest to jej jedyny cel. Co więcej, jeśli funkcje receive i fallback zostaną zaimplementowane w jednym kontrakcie, a ktoś wykona transfer środków na jego adres, to tylko funkcja receive zostanie wywołana.

Jednakże, jeśli w kodzie zostanie umieszczona tylko funkcja fallback z oznaczeniem payable, to środki również trafią do kontraktu.

Mam nadzieję, że udało mi się w tym wpisie wyjaśnić ten “smaczek” języka Solidity i że od teraz wiele rzeczy stanie się dla Ciebie bardziej oczywiste. Do zobaczenia kolejnym razem!

Share this Post
Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments