Il terzo articolo della nostra serie che spiega la modalità Fusion è incentrato sulla componente onchain della risoluzione degli swap.
Nei due articoli precedenti della serie abbiamo discusso, rispettivamente, del concetto di risolutore e della componente offchain del processo di risoluzione degli swap.
Riprendiamo da dove ci eravamo interrotti. Siamo in una fase del processo di risoluzione dello swap in cui il backend del risolutore ha "deciso" di eseguire un ordine Fusion ricevuto dal servizio 1inch Relayer, in un determinato blocco, con una certa quantità dell'asset scambiato da restituire al utente. Ora esamineremo la parte onchain del processo di risoluzione dello swap. Ma prima descriveremo i partecipanti a questo processo.
La parte onchain dell'esecuzione dello swap Fusion comporta interazioni piuttosto complesse tra le seguenti entità blockchain:

Un avviso molto importante: in alcuni casi (non sempre), un risolutore effettua diversi riempimenti in un batch, fino a 32. Normalmente, ci sono più saldi di token nel contratto di lavoro del risolutore e il backend può prendere diversi ordini dallo "stream" fornito dal relè ed eseguire una sequenza di riempimenti.
Esamineremo il seguente scenario.
Un risolutore ha selezionato 3 ordini potenzialmente redditizi dal relè da eseguire:
100 DAI per almeno 0,6 WETH
0,6 WETH per almeno 100 DAI
0,01 WBTC per almeno 36 UNI
L'obiettivo aziendale del risolutore è eseguire tutti e 3 gli scambi in modo che gli utenti ottengano almeno l'importo previsto. Tralasceremo le possibili strategie di guadagno dei risolutori e semplificheremo i calcoli in modo che tu possa comprendere l’idea generale.
Ora, il backend fornisce informazioni sugli ordini selezionati al contratto del lavoratore. Cosa succede dopo?
NB: questo grafico è la continuazione di uno dell'articolo dedicato alla componente offchain della risoluzione degli swap. Ma, per semplicità di comprensione, partiamo dal passaggio 1.

Passo 1
Il contratto di lavoro (o portafoglio) richiama il metodo settleOrders() del contratto di liquidazione da 1 pollice, fornendo le informazioni sull'ordine in un formato leggero e compresso di byte; gli argomenti calldata e tokensAndAmounts vengono utilizzati per memorizzare queste informazioni.
Qui potresti notare alcuni dettagli interessanti:
rateBump proviene dal quotatore e determina effettivamente il rendimento. È la differenza percentuale tra l'importo dell'asta corrente e l'importo minimo dell'asta. Ad esempio, se il valore rateBump è 4_000_000 e auctionEndAmount (rendimento minimo) è 500, l'importo corrente dell'asta sarà 700.
totalFee è una commissione che tutti i risolutori devono pagare alla 1inch Foundation (Importante: attualmente questa funzione non è utilizzata: i risolutori non pagano alcuna commissione alla 1inch Foundation).
limitOrderProtocol è un'istanza del protocollo dichiarato nel contratto di transazione tramite la rispettiva interfaccia Solidity. È usato per chiamare questo contratto dal contratto di transazione.

Passo 2
Il contratto di regolamento richiama il metodo fillOrderTo() del contratto di ordine limite, fornendo i dati di uno dei 3 ordini dall'elenco, ad esempio 100 DAI per almeno 0,6 WETH:
Interazione: contiene l'informazione che ci sono altri 2 ordini in blocco che dovranno essere eseguiti successivamente.
Fare o prendere importi: letteralmente quanto pagare o quanto ottenere. Solo uno di questi importi può essere diverso da zero. Se imposti makingAmount, specifichi quanto desideri ricevere come risolutore. In alternativa, impostando takeAmount, determini quanto vorresti vendere come risolutore. Uno verrà calcolato in base a un altro.
Target: l'indirizzo del contratto del lavoratore che riceverà i token di origine.

Passaggi 3-4
Il contratto di ordine limitato chiama il contratto del token di origine per trasferire l'importo dello swap dall'utente al contratto di lavoro del risolutore, utilizzando l'interfaccia ERC20 Solidity.
Una parte dell'assembly che non è molto leggibile dall'uomo, forma calldata e chiama il contratto del token. Le parti di assemblaggio del contratto sono create per l’efficienza del gas e la fornitura di dati complessi.

Passaggi 5-6
Il contratto di ordine limitato richiama il contratto di liquidazione con un metodo chiamato fillOrderInteraction() e invia interactiveData contenente informazioni su ulteriori ordini nel batch (le informazioni precedentemente ricevute nelle interazioni). Dal lato della liquidazione, il codice decodifica interactiveData riconvertendolo in interazioni.
Se il batch non contiene più ordini (scenario 1 di seguito), finalizza l’interazione e chiama il contratto di lavoro del risolutore “dicendogli” di risolvere gli ordini. Ciò sarà possibile perché il contratto del lavoratore importerà la nostra interfaccia denominata IResolver. In Solidity, le interazioni tra contratti incrociati vengono solitamente eseguite in questo modo. Tratteremo cosa fa il lavoratore nei passaggi 16-17 di seguito.
Se nel batch rimangono altri ordini, il contratto di liquidazione richiama nuovamente il contratto di ordine limite. I due contratti continueranno a scambiarsi dati finché tutti gli ordini non saranno stati liquidati e tutti i fondi saranno stati raccolti dai conti degli utenti (100 DAI, 0,6 WETH e 0,01 WBTC nel nostro esempio).

Passaggio 7
Abbiamo quasi finito. Il contratto lavoratore-risolutore ha ricevuto tutti i fondi dagli utenti tramite i contratti di regolamento da 1 pollice e di ordine limite. Ora è il momento di risolvere effettivamente gli ordini!
Ecco le proprietà degli ordini:
Ordina 1: 100 DAI per almeno 0,6 WETH
Ordine 2: 0,6 WETH per almeno 100 DAI
Ordine 3: 0,01 WBTC per almeno 36 UNI
Sembra che possiamo letteralmente abbinare l'ordine 1 e l'ordine 2 senza alcuna azione aggiuntiva. Pertanto, invieremo in tutta sicurezza 0,6 WETH raccolti dall'utente che ha inviato l'ordine 2 all'utente che ha inviato l'ordine 1 e viceversa. Pertanto, gli ordini 2 su 3 sono stati risolti utilizzando i fondi propri degli utenti.
L'ultimo ordine rimasto è uno swap di 0,01 WBTC per 36 UNI. Il contratto di lavoro non ha alcuna UNI in bilancio. Pertanto, il contratto del lavoratore chiama il contratto del router di aggregazione da 1 pollice nello stesso modo in cui lo fa qualsiasi utente in uno scambio legacy. Il backend del risolutore può chiamare la nostra API di aggregazione per ottenere un percorso ottimale e passarlo al lavoratore per l'esecuzione. Il router esegue questo scambio e consegna 36 UNI al risolutore.
In questo esempio semplificato, il risolutore non ha guadagnato nulla ma ha speso fondi in gas per trasferire dati tra contratti. Nella vita reale, come accennato in precedenza, il backend del risolutore terrà conto di tutte le spese e si assicurerà che le operazioni siano redditizie per lui e rimangano nel quadro fornito dal servizio di quotazione. Il risolutore proverebbe anche a rendere lo scambio il più redditizio per l'utente.
Passaggi 8-11
Ora, dopo che il lavoratore-risolutore ha i token di destinazione, deve rispondere alla richiamata e trasferirli nel contratto di transazione. Ma aspetta... Perché non possono inviare i token direttamente all'utente?
No, non possono a causa del modo in cui è implementata l’architettura. Ricorda, il passaggio 5 di questo flusso è un callback al metodo fillOrderTo() richiamato dal contratto di liquidazione al contratto di ordine limitato. Quindi, la funzione è ancora in esecuzione!
Poiché il chiamante era il contratto transattivo, in questa configurazione è considerato l'acquirente dell'ordine. Pertanto, si suppone che questa istanza riceva una risposta dal risolutore e dai token trasferiti. Quindi, fornisce l'approvazione al contratto dell'ordine limitato, che, a sua volta, chiama transferFrom() al contratto del token di destinazione e l'importo dello swap arriva infine nel portafoglio dell'utente. Sono state fatte!
Un esempio Etherscan nella vita reale
Come esercizio finale, esaminiamo un caso reale di Fusion swap: da 1INCH a DAI, risolto dal risolutore Seawise e i relativi trasferimenti di token ERC20. Per un'esperienza ottimale, è necessario incrociare l'immagine seguente con il diagramma sopra, facendo corrispondere i passaggi.

Al passaggio 4, il contratto dell'ordine limite chiama transferFrom() sul contratto del token 1INCH per trasferire il token di origine dall'indirizzo del produttore (0x90…9044) all'indirizzo del risolutore.
I passaggi 5 e 6 non si riflettono in questo elenco perché non comportano alcun trasferimento di token ERC20.
Al passaggio 7, Seawise in qualità di risolutore scambia 1INCH in DAI nel pool Uniswap come fonte di liquidità.
Al passo 8, Seawise trasferisce DAI al contratto di liquidazione da 1 pollice.
Anche i passaggi 9 e 10 vengono saltati qui perché si tratta di risposte di callback interne tra i contratti del risolutore e quelli da 1 pollice.
Infine, al passaggio 11, il contratto di regolamento da 1 pollice trasferisce i token di destinazione al produttore.
Sentiti libero di esplorare questa transazione su Etherscan:
https://etherscan.io/tx/0x55e621337837f4f69f0c398ad5e9072a24811bbfd8cb2b208d621b940c9689b5
