Dlaczego interakcja z kontraktem zawodzi po udanej symulacji

2026-09-03

Dlaczego interakcja z kontraktem zawodzi po udanej symulacji

Portfel pokazuje podgląd wymiany, podaje, ile otrzymasz, i nie zgłasza żadnego problemu. Podpisujesz, a transakcja trafia do łańcucha jako nieudana i mimo to pobiera gas. Podgląd cię nie okłamał. Odpowiedział na pytanie o jeden moment, a twoja transakcja została wykonana w innym.

Dlaczego interakcja z kontraktem zawodzi po udanej symulacji: najważniejsze punkty

Co naprawdę uruchamia symulacja

Podgląd w portfelu to przebieg na sucho. Węzeł wykonuje twoje wywołanie na kopii stanu łańcucha, zgłasza, co by się stało, a potem wyrzuca wynik. Dokumentacja dla programistów Ethereum opisuje stojącą za tym metodę jako wykonującą nowe wywołanie wiadomości natychmiast, bez tworzenia transakcji w łańcuchu bloków, a towarzyszącą jej metodę gazową jako zwracającą oszacowanie, przy czym sama transakcja nie zostanie dodana do łańcucha bloków.

Z tego opisu wynikają dwie własności i obie przydadzą się dalej. Przebieg na sucho jest wykonywany względem wybranego bloku, więc jego odpowiedź jest przypięta do stanu w tym bloku. I biegnie sam: między wywołaniem a wynikiem nic innego się nie wykonuje.

Stan, na którym symulowałeś, to nie stan, w który wpadasz

Między podglądem a wykonaniem transakcja musi odbyć podróż. Jest podpisywana, rozgłaszana, przetrzymywana w mempoolu, wybierana przez producenta bloku i dopiero wtedy wykonywana według reguł tego bloku, który ją zawiera. Każdy krok tej podróży zajmuje czas, a łańcuch nie zatrzymuje się na ten czas.

Kod kontraktu czyta stan w chwili wykonania, nigdy w chwili symulacji. Rezerwy puli, odczyt wyroczni, przyznany limit wydatków, wpis na białej liście, flaga pauzy, limit na adres, zakończona aukcja: każda z tych wartości może mieć jedną wartość, gdy działa podgląd, i inną, gdy budowany jest blok. Kontrakt, który sprawdza taką wartość i zatrzymuje się, gdy warunek nie jest spełniony, zachowuje się w obu chwilach identycznie. Zmieniło się wejście.

Minimalne wyjście i termin: kontrole, które zawodzą celowo

Wywołanie wymiany może nieść dwa zabezpieczenia wewnątrz samego wywołania. Pierwsze to dolna granica tego, co musisz otrzymać, wyprowadzona z twojej tolerancji poślizgu. Drugie to znacznik czasu, po którym wywołanie przestaje być ważne. Oba są argumentami, które podpisujesz, więc oba są zamrożone na wartościach wyliczonych przez podgląd.

Załóżmy, że podgląd wycenia sprzedawane przez ciebie tokeny na 10 000 USDC, a twoja tolerancja jest ustawiona na 0,5 %. Wywołanie niesie wtedy granicę 9 950 USDC, a kontrakt dostaje polecenie, by porzucić całą interakcję, zamiast dostarczyć mniej. Kiedy cena odchodzi dalej niż twoja tolerancja, gdy transakcja jest w drodze, zabezpieczenie robi dokładnie to, o co je poprosiłeś. Termin działa tak samo: wywołanie, które wisi niepotwierdzone dłużej niż jego własny znacznik czasu, zostaje odrzucone po przybyciu, choć to samo wywołanie przeszłoby kilka minut wcześniej.

To przypadek, w którym niepowodzenie jest działającą ochroną. Zabezpieczenie, które nigdy się nie uruchamia, pozwoliłoby interakcji rozliczyć się po dowolnej cenie, do jakiej rynek zdążył odpłynąć do chwili zbudowania bloku.

Kolejność: ta sama transakcja w innym miejscu

Blok jest sekwencją, a kolejność transakcji decyduje, która interakcja widzi jaki stan. Twój podgląd ustawił wywołanie na początku pustej kolejki. Blok stawia je za wszystkim, co producent tam umieścił, a ci sąsiedzi mogą zużyć dokładnie tę płynność, ten limit wydatków albo tę pozostałą podaż, na które liczyło twoje wywołanie.

Mint z twardym limitem pokazuje to najprościej. Dziesięć portfeli może z osobna pomyślnie zasymulować ostatni dostępny egzemplarz, bo każdy podgląd biegnie na stanie, w którym ten egzemplarz jest jeszcze wolny. Jeden z nich trafia do bloku pierwszy, a pozostałych dziewięć spotyka wyprzedany kontrakt. Z tymi dziewięcioma podglądami nic nie było nie tak. Odpowiedziały na pytanie, które miało jedną odpowiedź, zanim blok powstał, i inną potem.

Gas: oszacowanie to nie rezerwacja

Oszacowanie gazu powstaje tak samo jak podgląd: przez jednokrotne wykonanie wywołania i zmierzenie go. Ta sama dokumentacja ostrzega, że oszacowanie może być znacząco większe niż ilość gazu faktycznie zużyta przez transakcję, a boli kierunek przeciwny: oszacowanie zdjęte z taniej ścieżki może nie wystarczyć na ścieżkę, którą transakcja ostatecznie pójdzie.

To właśnie na rozgałęzieniach wykonania otwiera się ta luka. Wymiana prowadzona przy podglądzie przez jedną pulę może przy wykonaniu przejść przez dwie; pierwszy zapis do komórki pamięci kosztuje więcej niż późniejszy zapis do tej samej komórki; pętla, która dotknęła trzech pozycji, może dotknąć dziewięciu. Jeśli podpisany przez ciebie limit skończy się w środku wykonania, wykonana praca zostaje cofnięta, a gas i tak jest zużyty, i jest to ta sama arytmetyka, która dotyczy każdej nieudanej transakcji. Zapas ponad oszacowanie nie podnosi opłaty, gdy pozostaje niewykorzystany, ponieważ jak wyceniany jest gas oddziela ilość pracy od ceny za jednostkę pracy.

Kiedy symulacja nie symulowała transakcji, którą wysłałeś

Czasem podgląd i wykonanie to w ogóle nie to samo wywołanie. Symulacja jest przeprowadzana względem jednego punktu końcowego w jednej sieci, więc portfel wycelowany w inny łańcuch albo w węzeł z nieaktualnym stanem odpowiada o innym świecie niż ten, do którego trafia twój podpis.

Twoja własna kolejka oczekujących to drugie źródło tej rozbieżności. Transakcje z jednego konta wykonują się w kolejności nonce, więc wcześniejsze oczekujące wywołanie z tego samego portfela biegnie pierwsze i może zmienić stan, od którego zależy późniejsze wywołanie. Gdy tym wcześniejszym wywołaniem jest niepotwierdzone zatwierdzenie, stojąca za nim interakcja bywa symulowana na limicie, którego się spodziewasz, a wykonana na limicie, który faktycznie masz.

Co pokazał podgląd Co zmieniło się do chwili wykonania Gdzie szukać
Wycenioną kwotę wyjścia Przesunęły się rezerwy albo odczyt wyroczni Argument minimalnego wyjścia w podpisanym wywołaniu
Ważne wywołanie Minął podpisany znacznik czasu Argument terminu i czas spędzony w oczekiwaniu
Dostępną podaż albo płynność Inna transakcja w bloku wzięła to pierwsza Pozycja twojej transakcji wewnątrz jej bloku
Oszacowanie gazu Wykonanie poszło dłuższą gałęzią Zużyty gas wobec limitu gazu na potwierdzeniu
Czysty przebieg na sucho Portfel był w innej sieci albo na innym węźle Identyfikator łańcucha i punkt końcowy przy podpisie

Jak niepowodzenie wygląda potem

Interakcja, która zatrzymuje się w ten sposób, zostaje zapisana. Zajmuje pozycję w bloku, zużywa gas, a jej potwierdzenie niesie pole statusu: EIP-658 zastąpił pośredni korzeń stanu w potwierdzeniu kodem statusu, w którym zero oznacza niepowodzenie, a jeden powodzenie. Eksploratory zamieniają to pole w etykietę cofnięta.

Etykieta nazywa wynik, a nie przyczynę. Niektóre kontrakty dołączają przy zatrzymaniu łańcuch znaków z powodem i eksplorator albo ślad wykonania potrafi go pokazać; inne nie dołączają nic. Czytanie nieudanej transakcji razem z argumentami, które faktycznie podpisałeś, zamienia kod statusu w diagnozę, bo to właśnie tych argumentów potwierdzenie nie odtworzy za ciebie.

Co naprawdę obniża odsetek niepowodzeń

Skróć lukę. Podgląd zdjęty chwilę przed podpisem opisuje stan bliższy temu, który znajdzie się w bloku, a transakcja potwierdzana szybko ma mniej czasu, by ktoś ją wyprzedził.

Dobieraj zabezpieczenia do aktywa, a nie do przyzwyczajenia. Granica na tyle ciasna, by odrzucać zwykły ruch jednej minuty na cienkim rynku, będzie stale zatrzymywać twoje interakcje, a granica na tyle luźna, by przyjąć wszystko, oddaje tę ochronę, dla której ją ustawiałeś. Ten sam osąd dotyczy terminu, który musi być dość długi, by przetrwać okres zatoru.

Traktuj powtarzające się niepowodzenie jako informację. Gdy ta sama interakcja z tymi samymi argumentami zatrzymuje się kilka razy, kontrakt zgłasza warunek, którego obecnie nie da się spełnić, a ponowne wysłanie tego samego wywołania wydaje gas na tę samą odpowiedź.

Podsumowanie

Symulacja odpowiada na pytanie, co by się stało, gdyby to wywołanie pobiegło teraz, samotnie, na tym bloku. Wykonanie w łańcuchu odpowiada na pytanie, co się stało, gdy wywołanie pobiegło później, wśród innych transakcji, na innym bloku. Niepowodzenie po czystym podglądzie to odległość między tymi dwoma pytaniami, a podpisane przez ciebie zabezpieczenia zamieniają tę odległość w zatrzymanie zamiast w złe wypełnienie. Sprawdź argumenty w podpisanym wywołaniu, pozycję transakcji wewnątrz jej bloku i zużyty gas wobec limitu gazu, a przyczyna będzie w jednym z tych trzech miejsc. Aby dalej poznawać podstawy, czytaj kolejne materiały Bitbase Academy.

Powiązane artykuły

Inne artykuły Bitbase na ten temat:

- Jak bezpiecznie zmienić endpoint RPC

- Maksymalna opłata za transakcję przekroczona: co oznacza to ostrzeżenie portfela

- Przekroczony limit zapytań RPC i co z tym zrobić

- FIFO i LIFO w podstawie kosztowej krypto

- Premia memetyczna: dlaczego memy wpływają na ceny

Zastrzeżenie: ten artykuł to treść edukacyjna Bitbase Academy, wyłącznie w celach informacyjnych. Nie stanowi porady inwestycyjnej, handlowej, podatkowej ani finansowej. Kryptoaktywa są zmienne — samodzielnie oceń ryzyko. Napisano w wrześniu 2026 r.; sprawdzaj aktualne oficjalne informacje.

Źródła

[1] Dokumentacja dla programistów Ethereum.org, JSON-RPC API (eth_call, eth_estimateGas) ethereum.org

[2] Ethereum Improvement Proposals, EIP-658: Embedding transaction status code in receipts eips.ethereum.org

[3] Dokumentacja dla programistów Ethereum.org, „Transakcje” (Transactions) ethereum.org