Руководство для начинающих по созданию бота MEV: Создание арбитражного бота в основной сети Ethereum
Арбитраж - возможность получать прибыль от разницы цен между одним и тем же активом на разных платформах ( как один из видов).
В MEV все немного сложнее. В блокчейнах транзакции передаются в mempool, который, по большей части, является общедоступным, для включения в следующий блок. Это означает, что трейдеры могут видеть незавершенные транзакции в mempool и знать, каковы будут последствия, как только они будут завершены.
Эта наглядность приводит к тому, что MEV арбитраж работает несколько иначе, чем традиционный арбитраж, потому что не нужно ждать, пока транзакции будут запущены в цепочку, чтобы найти возможный спред. В результате для MEV арбитража требуется постоянная проверка mempool’а в режиме реального времени для анализа каждой транзакции по мере ее поступления.
Как только вы обнаружите транзакцию, которая может вызвать возможность арбитража, когда она попадет в цепочку, вы можете отправить две свои транзакции, которые завершат возможность арбитража, непосредственно в mempool или в частный ретранслятор flashbots в виде пакета. Если все сделано правильно, это означает, что все 3 транзакции будут добыты в одном блоке. В результате, в тот момент, когда создается возможность арбитража, она также завершается следующими двумя вашими транзакциями.
Давайте разберем это на совершенно нереалистичном, но, надеюсь, наглядном примере.
Предположим, вы отслеживаете mempool и обнаруживаете транзакцию, которая повлияет на пул ликвидности в Uniswap V2 для токенов A и B. Поскольку вы знаете, что пулы ликвидности управляются уравнением x * y = k (в этом примере я игнорирую сборы), тогда вы знаете, когда видите, что кто-то отправляет обмен 2 токена A на как можно больше токена B, вы можете рассчитать (на основе резервов этих токенов в пуле ликвидности, которые в данном случае равны 10 и 10), что они получат взамен 1,7 токена B.
Ключевым моментом является то, что вы знаете, что эта транзакция повлияет на цену между токенами A и B на Uniswap V2, но не на другой DEX, такой как Sushiswap… по крайней мере, до тех пор, пока кто-нибудь не воспользуется возможностью арбитража. В тот момент, когда эта транзакция будет добыта, токен A будет дешевле на Uniswap, чем на Sushiswap, поэтому тот, кто может продать токен A на Sushiswap за токен B, а затем продать эти токены B на Uniswap за токен A, должен в конечном итоге получить больше токена A, чем они начали.
Поиск возможностей для арбитража
Проводя некоторые исследования различных возможностей в MEV, я наткнулся на готовый смарт-контракт в этом потрясающем репозитории github, который расскажет вам, как оптимально рассчитать правильную сумму, необходимую для арбитража между двумя DEXS Uniswap V2 — например, Uniswap V2 и Sushiswap.
Поскольку этот проект на самом деле не был посвящен глубокому погружению в solidity, это репо идеально подходило для проверки концепции. Ключевым моментом этого смарт—контракта является то, что вы можете предоставить ему два пула — один пул Uniswap и один пул Sushiswap - и сообщить, есть ли какая-либо прибыль от арбитража между двумя пулами. Однако расчет производится путем анализа запасов этих двух пулов в их текущем состоянии (т.е. последнее состояние блока).
Но что, если бы вы знали, как изменятся эти пулы до следующего блока? Тогда вы будете знать о возможностях арбитража до того, как они появятся в цепочке, и сможете отправить свою арбитражную транзакцию в правильном положении, чтобы воспользоваться этими возможностями. Теперь моя цель в этом проекте состояла в том, чтобы написать базовый скрипт, который модифицирует этот смарт-контракт для успешного обнаружения возможностей арбитража с использованием расчета изменения чистого баланса платформы Blocknative Simulation.
Написание базового арбитражного скрипта
Здесь учитывается влияние транзакции в mempool’е при расчете потенциальной прибыли с арбитража. Чтобы включить эту корректировку, я создал новую структуру под названием ‘Adjustments’ , в которой будет размещен пул, который необходимо скорректировать, направление, в котором его необходимо скорректировать, и количество корректировок для каждого токена в паре токенов. Затем вы заметите, что getProfit полагается на getOrderedReserves для расчета резервов каждого пула и правильного направления сделок. Поэтому я добавил некоторую логику в getOrderedReserves, которая будет принимать данные mempool обмена токенами на Uniswap или Sushiswap, а затем корректировать резервы токенов этой пары, чтобы при вызове getProfit он включал данные mempool, а не только последнее состояние цепочки.
Теперь, когда я настроил смарт-контракт так, чтобы он мог включать данные mempool в реальном времени, я могу завершить свой сценарий, используя mempool API от Blocknative и частную ретрансляцию flashbots.
Используя платформу моделирования, каждое моделирование выполняется изолированно от текущего состояния блока, что означает, что существует некоторая степень вероятности, связанная с полезными нагрузками ‘ожидающего моделирования’. Однако, если транзакция находится в верхней части блока, то это фактически означает, что она выполняется против текущего состояния блока, поскольку между текущим состоянием блока и этим состоянием не будет никаких транзакций. Лучший способ гарантировать, что он попадет в начало блока (или, по крайней мере, близко к нему), - это использовать частную ретрансляцию flashbots.
Для этого проекта я просмотрел ожидающие события моделирования на маршрутизаторах Uniswap V2 и Sushiswap, используя Javascript SDK от Blocknative. Ожидающие загрузки моделирования включают изменения чистого баланса по всем задействованным адресам (например, пул ликвидности), что позволяет мне сделать две вещи:
1. Проверьте, существует ли эта пара на другой бирже.
2. Используйте изменения баланса в качестве входных данных в качестве "корректировок" в моей функции getProfit в моем смарт-контракте.
Когда я получаю эти события, я перебираю список netBalanceChange в нашей полезной нагрузке платформы моделирования и пропускаю, когда адрес равен фактическому маршрутизатору (потому что я ищу только адреса пула ликвидности) или если netBalanceChange[index].address не содержит 2 элементов (если у него есть только 1 элемент, то это не может быть пул, потому что пул будет иметь 2 или более резервов токенов с изменениями чистого баланса).
Если один из адресов в поле netBalanceChange не является одним из адресов маршрутизатора и имеет 2 разных адреса токенов, которые имеют изменения чистого баланса, то это должен быть адрес пула. Теперь у меня есть адрес пула, задействованные адреса токенов и изменения чистого баланса каждого токена. Теперь я могу проверить, каков адрес пула ликвидности другого DEX, чтобы я мог указать его в своем смарт-контракте. Если у другого DEX нет пула ликвидности с той же парой токенов, то я не могу проводить арбитраж между ними, поэтому я двигаюсь дальше.
Аналогично, если у другого DEX есть пул ликвидности с той же парой токенов, то я могу провести арбитраж между ними. Используя мой скрипт, это означает, что я обнаружил отложенный своп либо на Uniswap, либо на Sushiswap в mempool, и я обнаружил тот же пул ликвидности на другом DEX. Теперь у меня есть вся информация, необходимая для предоставления моего смарт-контракта, чтобы увидеть, есть ли возможность получения прибыли.
Результат, который я получаю от своего смарт-контракта, не включает газ, поэтому я делаю наивный расчет, чтобы узнать, какова будет общая стоимость газа (включая мои дополнительные оплаты майнерам на ретрансляции flashbots). Я знал, что две мои транзакции обойдутся примерно в 240 тысяч долларов за газ, поэтому я жестко запрограммировал это для расчета чистой прибыли. Что касается моей цены на газ, я предполагал, что мне придется отказаться от 95% своей валовой прибыли, чтобы участвовать в частной эстафете. 95% были жестко запрограммированы и произвольны (есть возможности для улучшения!).
Если чистый расчет больше 0, я успешно нашел возможность арбитража, где я получу 5% чистой прибыли и отдам майнеру 95%!
Создание flash-ботов для арбитража
Затем мне нужно было создать свой пакет flashbots для отправки на ретранслятор. Поскольку обнаруженная транзакция mempool может быть транзакцией типа 0 или типа 2, у меня должны быть сценарии для обоих. Для получения дополнительной информации об использовании транзакций mempool в ваших пакетах вы можете обратиться к нашему предыдущему сообщению об использовании Block native с Flashbots.
Я использовал свой модифицированный смарт-контракт для построения своей транзакции, которая включала бы либо две внутренние транзакции, либо два свопа между двумя DEX.
Построив транзакцию, я затем создал пакет flashbots и смоделировал его, чтобы гарантировать, что транзакция не завершится неудачей по какой-либо причине. Я также создал окончательный расчет чистой прибыли, чтобы убедиться, что я все еще получаю прибыль. Если моя транзакция прошла симуляцию, и я все еще получал прибыль, то я отправлял ее на ретрансляцию flash bots для включения.
Я запускал свой скрипт в течение нескольких дней, и он работал точно так, как задумано — он обнаруживал выгодные возможности с помощью mempool API Blocknative и отправлял их флешботам, чтобы побороться за включение майнером. Однако превзойти других ботов ему удавалось с трудом.
Главной причиной этого было то, что большинство смарт-контрактов других ботов были гораздо более экономичны по расходу газа, чем мо, что позволяло им платить майнеру больше за включение. Кроме того, использование flashswaps довольно неэффективно по сравнению с простым обменом токенами, которыми вы уже владеете. Существует целый ряд других оптимизаций газа, но это очень конкурентная, жесткая игра.
Другие области, которые я не оптимизировал:
1. Выбор правильного газа для отправки майнеру.
2. Использование только двух дексов.
3. Рассмотрение арбитража только между двумя токенами. Алгоритм поиска пути для многих пар может привести к увеличению потенциальной прибыли.
4. Тестирование бота в других EVM сетях.