O terceiro artigo de nossa série que explica o modo Fusion concentra-se no componente onchain da resolução de swap.

Nos dois artigos anteriores da série discutimos, respectivamente, o conceito de um resolvedor e o componente offchain do processo de resolução de swap.

Vamos continuar de onde paramos. Estamos em um estágio do processo de resolução de swap em que o backend do resolvedor “decidiu” atender a uma ordem Fusion que recebeu do serviço retransmissor de 1 polegada, em um determinado bloco, com uma certa quantia do ativo trocado a ser devolvida ao do utilizador. Agora, passaremos pela parte onchain do processo de resolução de swap. Mas primeiro, descreveremos os participantes desse processo.

A parte onchain da execução do swap Fusion envolve interações bastante complexas entre as seguintes entidades de blockchain:

Um aviso muito importante: em alguns casos (nem sempre), um resolver faz vários preenchimentos em um lote, até 32. Normalmente, há vários saldos de token no contrato de trabalho do resolver e o backend pode receber vários pedidos do "fluxo" que o relayer fornece e fazer uma sequência de preenchimentos.

Vamos analisar o seguinte cenário.

Um resolvedor selecionou 3 ordens potencialmente lucrativas do retransmissor para atender:

  • 100 DAI por pelo menos 0,6 WETH

  • 0,6 WETH por pelo menos 100 DAI

  • 0,01 WBTC para pelo menos 36 UNI

O objetivo comercial do resolver é executar todas as 3 trocas de forma que os usuários obtenham pelo menos a quantia esperada. Deixaremos de fora as possíveis estratégias de ganho dos resolvers e simplificaremos os cálculos para que você possa entender a ideia geral.

Agora, o backend fornece informações sobre os pedidos selecionados para o contrato do trabalhador. O que acontece depois?

NB: este gráfico é uma continuação de um do artigo dedicado ao componente offchain da resolução de swap. Mas, para simplificar o entendimento, começamos do passo 1.

Passo 1

O contrato do trabalhador (ou carteira) chama o método settleOrders() do contrato de liquidação de 1 polegada, entregando as informações do pedido em um formato compactado leve de bytes; os argumentos calldata e tokensAndAmounts são usados ​​para armazenar essas informações.

Aqui, você pode notar alguns detalhes interessantes:

  • rateBump vem do cotador e efetivamente determina o retorno. É a diferença percentual entre o valor atual do leilão e o valor mínimo do leilão. Por exemplo, se o valor rateBump for 4_000_000 e o auctionEndAmount (retorno mínimo) for 500, o valor atual do leilão será 700.

  • totalFee é uma taxa que todos os resolvedores têm que pagar à 1inch Foundation (Importante: atualmente esse recurso não é usado - os resolvedores não pagam nenhuma taxa à 1inch Foundation).

  • limitOrderProtocol é uma instância do protocolo que é declarada no contrato de liquidação por meio de uma respectiva interface Solidity. É usada para chamar este contrato a partir do contrato de liquidação.

Passo 2

O contrato de liquidação chama o método fillOrderTo() do contrato de ordem limite, entregando os dados de uma das 3 ordens da lista - digamos, 100 DAI para pelo menos 0,6 WETH:

  • Interação - contém informações de que há mais 2 ordens em massa que terão que ser executadas posteriormente.

  • Making ou taking amount - literalmente quanto pagar ou quanto receber. Apenas um desses valores pode ser diferente de zero. Se você definir makingAmount, você especifica quanto gostaria de receber como um resolvedor. Alternativamente, ao definir takingAmount, você determina quanto gostaria de vender como um resolvedor. Um será calculado com base no outro.

  • Destino - o endereço do contrato do trabalhador que receberá os tokens de origem.

Passos 3-4

O contrato de ordem limite chama o contrato de token de origem para transferir o valor de swap do usuário para o contrato de trabalho do resolvedor, usando a interface ERC20 Solidity.

Uma parte de montagem que não é muito legível por humanos, forma calldata e chama o contrato de token. As partes de montagem do contrato são criadas para eficiência de gás e entrega de dados complexos.

Passos 5-6

O contrato de ordem de limite chama de volta o contrato de liquidação com um método chamado  fillOrderInteraction() e envia interactiveData contendo informações sobre ordens adicionais no lote (as informações recebidas anteriormente em interações). No lado da liquidação, o código decodifica interactiveData convertendo-o de volta para interações.

Se o lote não contiver mais pedidos (cenário 1 abaixo), ele finaliza a interação e chama o contrato do trabalhador do resolver "dizendo" para ele resolver os pedidos. Isso será possível porque o contrato do trabalhador importará nossa interface chamada IResolver. No Solidity, as interações entre contratos geralmente são feitas dessa maneira. Abordaremos o que o trabalhador faz nas etapas 16-17 abaixo.

Se quaisquer outras ordens permanecerem no lote, o contrato de liquidação chama o contrato de ordem limite novamente. Os dois contratos continuarão trocando dados até que todas as ordens tenham sido liquidadas e todos os fundos tenham sido coletados das contas dos usuários (100 DAI, 0,6 WETH e 0,01 WBTC em nosso exemplo).

Passo 7

Estamos quase terminando. O contrato worker-resolver recebeu todos os fundos dos usuários por meio dos contratos de liquidação de 1 polegada e ordem de limite. Agora, é hora de realmente resolver as ordens!

Aqui estão as propriedades dos pedidos:

  • Ordem 1: 100 DAI por pelo menos 0,6 WETH

  • Ordem 2: 0,6 WETH para pelo menos 100 DAI

  • Ordem 3: 0,01 WBTC para pelo menos 36 UNI

Parece que podemos literalmente combinar a ordem 1 e a ordem 2 sem nenhuma ação adicional. Então, enviaremos com segurança 0,6 WETH coletados do usuário que enviou a ordem 2 para o usuário que enviou a ordem 1, e vice-versa. Assim, as ordens 2 de 3 foram resolvidas usando os fundos próprios dos usuários.

A última ordem restante é uma troca de 0,01 WBTC por 36 UNI. O contrato do trabalhador não tem nenhuma UNI em seu saldo. Então, o contrato do trabalhador chama o contrato do roteador de agregação de 1 polegada da mesma forma que qualquer usuário faz em uma troca legada. O backend do resolver pode chamar nossa API de agregação para obter uma rota ótima e passá-la ao trabalhador para execução. O roteador executa essa troca e entrega 36 UNI ao resolver.

Neste exemplo simplificado, o resolver não ganhou nada, mas gastou fundos em gás para transferir dados entre contratos. Na vida real, como mencionado acima, o backend do resolver levaria todas as despesas em conta e garantiria que as negociações fossem lucrativas para eles e permanecessem dentro da estrutura fornecida pelo serviço de cotação. O resolver também tentaria tornar a troca o mais lucrativa possível para o usuário.

Passos 8-11

Agora, depois que o worker-resolver tem os tokens de destino, ele tem que responder ao callback e transferi-los para o contrato de liquidação. Mas espere... Por que ele não pode enviar os tokens diretamente para o usuário?

Não, eles não podem por causa da forma como a arquitetura é implementada. Lembre-se, a etapa 5 deste fluxo é um retorno de chamada para o método fillOrderTo() chamado pelo contrato de liquidação para o contrato de ordem limite. Então, a função ainda está sendo executada!

Como o chamador era o contrato de liquidação, nessa configuração, ele é considerado o tomador da ordem. Portanto, essa instância deve receber uma resposta do resolvedor e dos tokens transferidos. Então, ele fornece aprovação ao contrato de ordem de limite, que, por sua vez, chama transferFrom() para o contrato de token de destino e o valor do swap finalmente cai na carteira do usuário. Pronto!

Um exemplo real de Etherscan

Como exercício final, vamos rever um caso real de uma troca de Fusion: 1INCH para DAI, resolvido pelo resolver Seawise, e suas transferências de token ERC20. Para a melhor experiência, você precisa cruzar a imagem abaixo com o diagrama acima, combinando as etapas.

Na etapa 4, o contrato de ordem limite chama transferFrom() no contrato de token 1INCH para transferir o token de origem do endereço do criador (0x90…9044) para o endereço do resolvedor.

As etapas 5 e 6 não estão refletidas nesta lista porque não envolvem nenhuma transferência de token ERC20.

Na etapa 7, o Seawise, como resolvedor, troca 1INCH por DAI no pool Uniswap como fonte de liquidez.

Na etapa 8, a Seawise transfere o DAI para o contrato de liquidação de 1 polegada.

As etapas 9 e 10 também são ignoradas aqui porque são respostas de retorno de chamada internas entre o resolver e os contratos de 1 polegada.

Por fim, na etapa 11, o contrato de liquidação de 1 polegada transfere os tokens de destino para o criador.

Sinta-se à vontade para explorar esta transação no Etherscan:

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