Trzeci artykuł z serii objaśniającej tryb Fusion skupia się na komponencie onchain rozstrzygania transakcji swap.

W dwóch poprzednich artykułach z tej serii omawialiśmy odpowiednio koncepcję resolvera i komponent pozałańcuchowy procesu rozwiązywania swapów.

Kontynuujmy tam, gdzie skończyliśmy. Jesteśmy na etapie procesu rozwiązywania swapów, gdy zaplecze resolvera „zdecydowało” wypełnić zamówienie Fusion, które otrzymało od usługi 1inch relayer, w pewnym bloku, pewną ilością zamienionego zasobu, która ma zostać zwrócona użytkownikowi. Teraz przejdziemy przez część onchain procesu rozwiązywania swapów. Ale najpierw opiszemy uczestników tego procesu.

Część on-chain realizacji transakcji Fusion Swap obejmuje dość złożone interakcje pomiędzy następującymi podmiotami blockchain:

Bardzo ważna uwaga: w niektórych przypadkach (nie zawsze) resolver wykonuje kilka wypełnień w jednej partii, aż do 32. Zazwyczaj w kontrakcie roboczym resolvera znajduje się wiele sald tokenów, a zaplecze może przyjąć kilka zamówień ze „strumienia” dostarczanego przez relayera i wykonać sekwencję wypełnień.

Przeanalizujemy następujący scenariusz.

Osoba rozwiązująca zlecenie wybrała 3 potencjalnie lukratywne zlecenia od osoby przekazującej zlecenie do realizacji:

  • 100 DAI za co najmniej 0,6 WETH

  • 0,6 WETH za co najmniej 100 DAI

  • 0,01 WBTC za co najmniej 36 UNI

Celem biznesowym resolvera jest wykonanie wszystkich 3 swapów w taki sposób, aby użytkownicy otrzymali co najmniej kwotę, której się spodziewali. Pominiemy możliwe strategie zarobkowe resolverów i uprościmy obliczenia, abyś mógł zrozumieć ogólną ideę.

Teraz back-end dostarcza informacji o wybranych zamówieniach do umowy pracownika. Co dzieje się dalej?

NB: ten wykres jest kontynuacją wykresu z artykułu poświęconego komponentowi offchain rozwiązywania swapów. Jednak dla uproszczenia zrozumienia zaczynamy od kroku 1.

Krok 1

Kontrakt roboczy (lub portfel) wywołuje metodę setstleOrders() kontraktu rozliczeniowego 1inch, dostarczając informacje o zamówieniu w lekkim, skompresowanym formacie bajtów; argumenty calldata i tokensAndAmounts służą do przechowywania tych informacji.

Tutaj możesz zauważyć kilka interesujących szczegółów:

  • rateBump pochodzi od oferenta i skutecznie określa zwrot. Jest to różnica procentowa między bieżącą kwotą aukcji a minimalną kwotą aukcji. Na przykład, jeśli wartość rateBump wynosi 4_000_000, a auctionEndAmount (minimalny zwrot) wynosi 500, bieżąca kwota aukcji wynosi 700.

  • totalFee to opłata, którą wszyscy resolverzy muszą zapłacić Fundacji 1inch (Ważne: obecnie ta funkcja nie jest używana — resolverzy nie płacą żadnych opłat Fundacji 1inch).

  • limitOrderProtocol to wystąpienie protokołu, który jest zadeklarowany w kontrakcie rozliczeniowym za pośrednictwem odpowiedniego interfejsu Solidity. Służy do wywoływania tego kontraktu z kontraktu rozliczeniowego.

Krok 2

Kontrakt rozliczeniowy wywołuje metodę fillOrderTo() kontraktu zlecenia z limitem, dostarczając dane jednego z 3 zleceń z listy, np. 100 DAI za co najmniej 0,6 WETH:

  • Interakcja – zawiera informację, że w zbiorze znajdują się jeszcze 2 zamówienia, które trzeba będzie później zrealizować.

  • Kwoty tworzenia lub pobierania - dosłownie ile zapłacić lub ile otrzymać. Tylko jedna z tych kwot może być różna od zera. Jeśli ustawisz makingAmount, określisz, ile chcesz otrzymać jako resolver. Alternatywnie, ustawiając takingAmount, określisz, ile chcesz sprzedać jako resolver. Jedna kwota zostanie obliczona na podstawie drugiej.

  • Cel – adres kontraktu pracownika, który będzie otrzymywał tokeny źródłowe.

Kroki 3-4

Kontrakt zlecenia limitowanego wywołuje kontrakt tokena źródłowego w celu przesłania kwoty swapu od użytkownika do kontraktu roboczego resolvera, korzystając z interfejsu ERC20 Solidity.

Część montażowa, która nie jest zbyt czytelna dla człowieka, tworzy calldata i wywołuje kontrakt tokena. Części montażowe kontraktu są tworzone w celu zapewnienia wydajności gazu i dostarczania złożonych danych.

Kroki 5-6

Kontrakt zlecenia limitowanego wywołuje kontrakt rozliczeniowy za pomocą metody fillOrderInteraction() i wysyła interaktywne dane zawierające informacje o kolejnych zleceniach w partii (informacje wcześniej otrzymane w interakcjach). Po stronie rozliczeniowej kod dekoduje interaktywne dane, konwertując je z powrotem na interakcje.

Jeśli partia nie zawiera więcej zamówień (scenariusz 1 poniżej), finalizuje interakcję i wywołuje kontrakt roboczy resolvera, „mówiąc” mu, aby rozwiązał zamówienia. Będzie to możliwe, ponieważ kontrakt roboczy zaimportuje nasz interfejs o nazwie IResolver. W Solidity interakcje między kontraktami są zwykle wykonywane w ten sposób. Omówimy, co robi worker w krokach 16–17 poniżej.

Jeśli w partii pozostaną jakieś inne zlecenia, kontrakt rozliczeniowy ponownie wywoła kontrakt zlecenia limitowanego. Oba kontrakty będą kontynuować wymianę danych, dopóki wszystkie zlecenia nie zostaną rozliczone, a wszystkie środki nie zostaną pobrane z kont użytkowników (w naszym przykładzie 100 DAI, 0,6 WETH i 0,01 WBTC).

Krok 7

Już prawie skończyliśmy. Kontrakt worker-resolver otrzymał wszystkie środki od użytkowników za pośrednictwem kontraktów 1inch settlement i limit order. Teraz czas na faktyczne rozwiązanie zleceń!

Oto właściwości zamówień:

  • Zamówienie 1: 100 DAI za co najmniej 0,6 WETH

  • Zamówienie 2: 0,6 WETH za co najmniej 100 DAI

  • Zamówienie 3: 0,01 WBTC za co najmniej 36 UNI

Wygląda na to, że możemy dosłownie dopasować zamówienie 1 i zamówienie 2 bez żadnych dodatkowych działań. Tak więc bezpiecznie wyślemy 0,6 WETH zebrane od użytkownika, który złożył zamówienie 2, do użytkownika, który złożył zamówienie 1 i odwrotnie. Zatem zamówienia 2 z 3 zostały rozwiązane przy użyciu własnych środków użytkowników.

Ostatnie pozostałe zamówienie to zamiana 0,01 WBTC za 36 UNI. Kontrakt roboczy nie ma żadnego UNI na swoim saldzie. Tak więc kontrakt roboczy wywołuje kontrakt routera agregacji 1inch w taki sam sposób, w jaki robi to każdy użytkownik w starszej zamianie. Zaplecze resolvera może wywołać nasze API agregacji, aby uzyskać optymalną trasę i przekazać ją do workera w celu wykonania. Router wykonuje tę zamianę i dostarcza 36 UNI do resolvera.

W tym uproszczonym przykładzie resolver nie zarobił nic, ale wydał pieniądze na gaz, aby przesyłać dane między kontraktami. W rzeczywistości, jak wspomniano powyżej, zaplecze resolvera brałoby pod uwagę wszystkie wydatki i upewniało się, że transakcje są dla niego opłacalne i mieszczą się w ramach zapewnianych przez usługę quoter. resolver próbowałby również sprawić, aby swap był maksymalnie opłacalny dla użytkownika.

Kroki 8-11

Teraz, gdy pracownik-rozwiązywacz ma tokeny docelowe, musi odpowiedzieć na wywołanie zwrotne i przesłać je do kontraktu rozliczeniowego. Ale czekaj… Dlaczego nie mogą wysłać tokenów bezpośrednio do użytkownika?

Nie, nie mogą ze względu na sposób implementacji architektury. Pamiętaj, że krok 5 tego przepływu to wywołanie zwrotne metody fillOrderTo() wywoływanej przez kontrakt rozliczeniowy dla kontraktu zlecenia limitowanego. Funkcja ta jest więc nadal wykonywana!

Ponieważ wywołującym był kontrakt rozliczeniowy, w tej konfiguracji jest on uważany za przyjmującego zlecenie. Stąd ta instancja powinna otrzymać odpowiedź od resolvera i przesłanych tokenów. Następnie udziela zatwierdzenia kontraktowi zlecenia limitowanego, który z kolei wywołuje transferFrom() do kontraktu tokena docelowego, a kwota swapu ostatecznie trafia do portfela użytkownika. Gotowe!

Przykład Etherscan w prawdziwym życiu

Jako ostatnie ćwiczenie przeanalizujmy rzeczywisty przypadek wymiany Fusion: 1INCH do DAI, rozwiązany przez resolver Seawise i jego transfery tokenów ERC20. Aby uzyskać najlepsze wrażenia, musisz porównać poniższy obraz z powyższym diagramem, dopasowując kroki.

W kroku 4 kontrakt zlecenia limitowanego wywołuje metodę transferFrom() w kontrakcie tokena 1INCH w celu przeniesienia tokena źródłowego z adresu twórcy (0x90…9044) na adres resolvera.

Kroki 5 i 6 nie są uwzględnione na tej liście, ponieważ nie wiążą się z transferami tokenów ERC20.

W kroku 7 Seawise, jako resolver, zamienia 1INCH na DAI w puli Uniswap jako źródło płynności.

W kroku 8 Seawise transferuje DAI do kontraktu rozliczeniowego 1inch.

Kroki 9 i 10 również są tutaj pomijane, ponieważ stanowią wewnętrzne odpowiedzi zwrotne pomiędzy resolverem a kontraktami 1-calowymi.

Na koniec, w kroku 11, kontrakt rozliczeniowy 1inch przekazuje tokeny docelowe do twórcy.

Zachęcamy do zapoznania się z tą transakcją na Etherscan:

https://etherscan.io/tx/0x55e621337837f4f69f0c398ad5e9072a24811bbfd8cb2b208d621b940c9689b5