Ethereum: кардинальные перемены для криптовалюты номер два уже 4 августа
Знаменитое обновление EIP 1559, одно из самых ожидаемых событий для Ethereum и всей криптовалютной индустрии в 2021 году, наконец-то должно быть развернуто 4 августа. Это обновление должно изменить структуру платы за газ при каждой транзакции, сделать эти сборы менее волатильными и одновременно уменьшить количество ETH в обороте.
20K открытий
Кроме того, создается новый подход к безопасности сети: если дефляционный механизм толкает цену актива вверх, то и цена атаки на децентрализованную сеть становится дороже. В результате меняется монетарная политика в Ethereum – не надо выпускать как можно больше токенов для обеспечения безопасности сети.
В результате, в ближайшем будущем благодаря EIP-1559 нас ждет обновление удобства использования и экономики, в EIP-3074 — потенциально большое обновление UX, а в EIP-3675 — масштабное обновление экономики и безопасности. Хотя эти 3 EIP развертываются в разное время, я ожидаю увидеть их все в сети Ethereum в течение следующих 12 месяцев.
После активации хардфорка London следующими шагами станут слияние Ethereum 1.0 и 2.0, а также развертывание решений второго уровня масштабируемости. Ожидается, что эти события произойдет к началу 2022 года, однако уже сейчас EIP 1559 является предметом споров среди майнеров. Поэтому переход к Ethereum 2.0 не обещает быть гладким.
Что такое EIP-1559, и как данное обновление исправит проблему высоких комиссий в сети Эфириума
В середине апреля Эфириум ждёт обновление под названием Berlin, которое оптимизирует использование газа в сети и предотвратит возможные атаки на блокчейн. Следующим большим апдейтом после него станет London, ориентировочно запланированный на июль. В него включён так называемый EIP-1559, готовый серьёзно изменить ситуацию с загруженностью сети и высокими комиссиями. И хотя для пользователей ETH это выгодно, майнеры высказались против внедрения улучшения. Рассказываем о сути происходящего и будущем блокчейна криптовалюты.
Для начала уточним, что оба упомянутых обновления относятся к нынешней сети Эфириума, которая работает за счёт механизма консенсуса Proof-of-Work — то есть майнеров с видеокартами.
Berlin и London не имеют отношения к цепочке eth2, запуск нулевой фазы которой состоялся первого декабря 2020 года. И хотя в итоге сети объединятся, сейчас актуальный Ethereum 1 и PoS-сеть Beacon Chain — разные вещи. А дальнейшее содержимое статьи будет относиться именно к нынешней сети Эфириума с майнерами.
Что такое EIP-1559
EIP расшифровывается как Ethereum Improvement Proposal, то есть “предложение по улучшению Эфириума”. По сути это точечные улучшения сети криптовалюты, из которых и состоят крупные апдейты. К примеру, апрельский Berlin будет состоять из четырёх EIP: 2565, 2929, 2718 и 2930. Каждый из них сфокусирован на определённой области работы сети, хотя в итоге они пересекаются друг с другом.
В частности, EIP-2930 “смягчит” увеличение расхода газа при использовании определённых кодов операции в сети, которое внедрит EIP-2929. Подробнее об этой теме мы писали в отдельном материале.
EIP-1559 стал одной из составляющих обновления London, которое будет внедрено в июле этого года. В целом он меняет принцип формирования комиссий в сети Эфириума. В результате это улучшит опыт взаимодействия с блокчейном и сэкономит расходы на проведение переводов.
Сейчас комиссии в сети ETH формируются по принципу аукциона. Поскольку майнеры добавляют в блок и подтверждают первыми те транзакции, за которые пользователи заплатили большую комиссию, это вынуждает последних тратить больше. И когда сеть становится загруженной, плата за проведение может быть абсурдной.
К примеру, 22 февраля 2021 года на фоне обвала рынка стоимость газа в Эфириуме превышала 1000 гвеев. А значит стоимость обычного перевода исчислялась десятками долларов и в том числе достигала 51 доллара.
Стоимость газа в сети Эфириума 22 февраля 2021 года
Свапы на децентрализованной бирже Uniswap и вовсе могли обойтись в эквивалент 600 долларов. Естественно, при таких расценках платформа становилась бесполезной для большинства пользователей, которые не владеют десятками или сотнями тысяч долларов в крипте.
Стоимость свапа в сети Эфириума 22 февраля 2021 года
Однако высокие комиссии — это праздник для майнеров, заработок которых напрямую зависит от загруженности сети. В связи с этим популярность добычи криптовалют в этом году серьёзно выросла.
К примеру, количество майнеров на пуле 2Miners превысило отметку в 50 тысяч человек. 28 тысяч из них занимается как раз добычей Эфириума.
Количество майнеров на пуле 2Miners
То есть в этом году сеть ETH по сути достигала пика своей загруженности, из-за чего пользоваться ей становилось дорого. Это препятствует развитию блокчейна и делает его менее популярным. Разработчики хотят исправить проблему — и здесь пригодится EIP-1559.
Что меняет EIP-1559 в сети Эфириума
EIP-1559 полностью пересматривает принцип формирования комиссий за проведение транзакций. Старая система аукциона станет неактуальной: вместо неё введут понятие базовой комиссии за газ, которая будет расти или снижаться в каждом блоке по мере загруженности сети. При формировании комиссии сеть будет использовать формулу расчёта, которая ориентируется на объём газа, использованный в предыдущем блоке.
Ранее данный показатель называли лимит газа, однако теперь его переименовали в “цель газа” — “gas target”.
В итоге базовая стоимость газа будет расти, когда блоки будут превышать цель газа, и снижаться, когда показатель будет ниже определённой планки. При этом базовая комиссия в виде ETH будет сжигаться, то есть монеты, потраченные на проведение транзакций, будут убираться из оборота. В перспективе это сделает Эфириум дефляционным активом — таким, объём которого уменьшается.
Комиссии майнерам остаются: теперь их называют “платой за включение” в блок. Авторы транзакций будут указывать максимальную комиссию, которая будет идти на оплату базовой комиссии и вознаграждения майнерам за включение перевода в блок.
Каждая транзакция будет оплачивать базовую комиссию за газ с учётом показателя блока, в который она попадёт, после чего остаток средств будет идти на плату за включение в блок, предназначенную майнерам. Это будет происходить в каждом случае. Исключением станут ситуации, когда сумма двух комиссий будет превышать максимальную комиссию, указанную пользователем.
Как отмечают авторы EIP-1559, он решит многие проблемы для пользователей:
- несоответствие между волатильностью комиссий за проведение транзакции и социальной стоимостью транзакций — при полном заполнении блоков стоимость проведения дополнительной операции существенно и непропорционально возрастает;
- ненужные задержки для пользователей — порой пользователям нужно ждать много блоков, включая часы и дни, прежде чем получить подтверждение транзакций. А значит выгоду из-за жёсткого лимита газа участники сети не получают;
- неэффективность модели аукциона — транзакция пользователя, заплатившего относительно небольшую комиссию за проведение перевода час назад, окажется в блокчейне позже того, кто заплатил больше одну минуту назад. И так может продолжаться долго, что создаёт существенные неудобства для участников сети;
- нестабильность блокчейнов без фиксированной награды за блок — в дальнейшем криптовалюты по типу Биткоина избавятся от награды за блок из-за ограниченности максимального объёма криптовалюты. Это значит, что майнеры будут зарабатывать исключительно на комиссиях за переводы. Разработчики Эфириума считают, что это станет причиной нестабильности, поскольку майнеры BTC заинтересуются в добыче так называемых сестринских блоков для кражи комиссий. EIP-1559 избавляет сеть ETH от данной угрозы, ведь базовые комиссии будут сжигаться.
Однако для майнеров данная инициатива закончится проседанием доходности. По предварительным расчётам, после внедрения EIP-1559 в Эфириум, который в данный момент является одной из самых выгодных криптовалют для майнинга, начнёт приносить майнерам приблизительно на 50 процентов меньше средств.
Доходность майнинга криптовалют
Как отреагировали майнеры Эфириума на EIP-1559
Перспектива потери половины дохода майнеров Эфириума не порадовала, поэтому они решили “показать силу”. Речь идёт об инициативе майнера под псевдонимом Red Panda: он предложил перевести свои вычислительные мощности на майнинг-пул Ethermine, который выступил против внедрения EIP-1559. Сделать он это решил первого апреля.
Майнер уточнил, что не призывает атаковать сеть Эфириума, однако таким образом хочет подчеркнуть важную роль майнеров в блокчейне и призвать с ними считаться. И хотя об атаке речи не шло, всё выглядело именно как угроза ради отмены внедрения EIP-1559.
Однако инициатива закончилась неожиданно. Вскоре после выхода предложения перевести вычислительные мощности на определённый пул в комьюнити Эфириума узнали об активности Виталика Бутерина. Он опубликовал документ под названием “Изменения в быстрый мёрдж посредством форка”.
Речь идёт об ускорении перехода Эфириума на Proof-of-Stake, который изначально был запланирован на конец 2021 года. Особенность заключается в том, что так называемая фаза 1.5 предполагает отказ от PoW-майнинга.
Таким образом Бутерин дал понять, что майнинг ETH на видеокартах может закончиться значительно раньше, чем предполагалось раньше. В итоге аналитики посчитали эти действия ответом разработчика на инициативу некоторых майнеров, которая была рассмотрена как угроза сети.
На этом история не закончилась. В итоге майнеры предложили внедрить так называемый EIP-3368, который увеличил бы награду за блок Эфириума с нынешних 2 до 3 ETH и в дальнейшем снижал бы её до 1 ETH в течение двух лет. Идею авторы мотивировали возможным падением хешрейта сети после внедрения EIP-1559, который бы спровоцировало снижение вознаграждения за майнинг.
Представители Эфириум-сообщества отнеслись к идее критично. Вот цитата сотрудника платформы Deribit Insights под ником Hasu.
Предложение совершенно бестолковое. Ни автор, ни его сторонники не представили убедительных причин, в соответствии с которыми сокращение вознаграждения майнеров на 20-30 процентов должно создать риски для сети Ethereum. Всё же награда за блок Биткоина уменьшается вдвое каждые четыре года. Сам Эфириум раньше дважды уменьшал награду за блок — на 60 и 33 процента соответственно.
В итоге позиция сводилась к тому, что снижение награды за майнинг в криптовалюте не заставит майнеров массово уйти из сети, тем самым сделав её более уязвимой. Особенно это актуально в случае роста курса криптовалюты.
Действительно, снижение награды за блок Биткоина с 12.5 до 6.25 BTC состоялось в мае 2020 года. С тех пор курс криптовалюты вырос в несколько раз. То же самое касается хешрейта сети: и хотя он упал непосредственно после халвинга, в итоге показатель вырос, а блокчейн BTC стал как никогда безопасным.
Хешрейт сети Биткоина и отмеченная дата халвинга криптовалюты
Когда активируют EIP-1559
В итоге внедрение EIP-1559 состоится ориентировочно в июле 2021 года. Это сделает сеть Эфириума более дешёвой и удобной для использования, поскольку комиссии станут ниже. Вдобавок определённый объём криптовалюты будет постоянно сжигаться, что сделает ETH дефляционным активом.
И как ранее отметил аналитик исследовательской фирмы Messari Райан Уоткинс, превращение Эфириума в дефляционный актив вместе с переходом на Proof-of-Stake позволит криптовалюте стать лидером рынка, то есть обойти Биткоин.
Эфириум и другие криптовалюты
Позитивный прогноз также дают представители Grayscale. По их версии, внедрение EIP-1559 существенно скажется на стоимости Эфириума. Причём в лучшую сторону.
Мы считаем, что улучшение сети Эфириума действительно хорошо скажется на будущем криптовалюты. Блокчейн будет лучше справляться с пиковыми нагрузками, а пользователям не придётся выкладывать огромные суммы за проведение простых переводов. Вдобавок само предложение ETH будет снижаться, что является отличным свойством для цифрового актива.
В итоге внедрение EIP-1559 станет подготовительным этапом для перехода сети на новую версию блокчейна. Благодаря обновлению и переходу на Proof-of-Stake сеть увеличит свою пропускную способность в тысячи раз.
Чтобы быть в курсе ситуации, присоединяйтесь к нашему крипточату миллионеров. Там обсудим и другие важные события в нише криптовалют.
Что, черт возьми, такое EIP-1559?
Не о снижении ли транзакционных сборов в ETH, наконец, пойдет речь? Да, но только лишь отчасти! EIP-1559 включает в себя несколько изменений, которые помогут сократить сборы и увеличить скорость расчетов. Но не стоит слишком сильно рассчитывать на это. Ниже я объясню почему.
Краткий обзор
Предложение по улучшению Эфириума под номером 1559 (сокращенно EIP-1559) впервые было озвучено Виталиком Бутериным в 2018 году и является самым долгожданным обновлением в истории Ethereum. Прежде чем мы перейдем к деталям, вот краткое объяснение того, что такое EIP-1559:
Больше нет «никаких» сборов
Вместо этого есть две платы: одна называется Базовой платой, а вторая — Платой за включение, или чаевыми для майнеров. Базовая плата сжигается, в то время как плата за включение (чаевые) идет майнерам.
Эластичность размеров блоков
Размеры блоков теперь динамичны и будут увеличиваться или уменьшаться в зависимости от состояния перегруженности сети. Максимальный лимит газа на блок теперь составит 25 миллионов, что в 2 раза превышает текущий лимит газа на блок в 12,5 миллиона.
Однако целевой лимит газа на блок будет сохранен на уровне 12,5 миллиона, но в случае временной перегрузки бОльшие по размеру блоки будут использоваться для накопления бОльшего количества транзакций.
Вот в чем по большей части заключается это предложение по улучшению!
Теперь давайте копнем чуть глубже, чтобы понять механизм и влияние EIP-1559.
Модель аукциона первой цены и некорректная оценка размера комиссии
Помимо стандартного вознаграждения за блок, майнеры, или производители блоков, также получают вознаграждение за транзакцию. Каждая транзакция включает в себя комиссию, и майнеры отдают приоритет транзакциям с более высокими комиссиями, чтобы максимизировать свой доход.
Если вы хотите, чтобы ваша транзакция обрабатывалась быстрее, вы просто предлагаете более высокую цену. Это называется моделью аукциона первой цены, когда шансы вашей транзакции, включенной в ближайший блок, максимизируются, если вы предлагаете более высокую цену (АПЦ – закрытый аукцион, при котором победителем является участник с самой высокой ценой и именно эта цена подлежит уплате).
Модель аукциона первой цены рассчитана на то, чтобы в первую очередь дать «зеленый свет» более дорогостоящим сделкам и не допустить перегрузку блокчейна менее дорогими. Но здесь есть одна проблема: пока нет способа рассчитать оптимальную цену одной сделки.
Допустим, вы предлагаете 10 долларов за транзакцию, потому что до вас предлагали 8 долларов, но если все вокруг ставят 5 долларов, вам выгоднее предложить 6 долларов, сэкономив сразу 4 бакса!
Кошелек ethereum, который вы используете, в его текущем виде не может корректно оценить размер комиссии за транзакцию, и, как правило, люди почти все время переплачивают.
Одним из решений этой проблемы может быть аукцион единой цены, где следующая ставка всегда выше предыдущей. Однако в этом случае существует большая вероятность, что майнеры начнут включать свои собственные транзакции в блок для создания искусственного ажиотажа на цены в целях обогащения.
Как EIP-1559 решает эту проблему?
EIP-1559 вводит новый механизм ценообразования для устранения тех недочетов, которые мы могли наблюдать в первой модели ценового аукциона. Теперь предлагаются две платы – базовая плата и плата за включение.
Базовая плата будет фиксированной сетевой платой за каждый блок, которую должна включать в себя каждая транзакция. Для тех, кто разбирается: эта плата будет зависеть от газа, используемого в родительском блоке, и цели газа (ранее известной как предел газа) родительского блока.
Если блоки находятся выше целевого предела газа, эта базовая плата будет увеличиваться, а когда они будут ниже целевого предела газа, базовая плата будет уменьшаться. Целевой предел газа установлен на уровне 12,5 миллиона, но он может достигать максимального предела газа на блок в 25 миллионов (2 раза выше целевого уровня газа).
Когда перегрузка сети выше, то и базовая плата больше, а когда меньше, то и базовая плата меньше. Однако транзакции будут проходить немного быстрее, потому что блоки с более высоким лимитом газа будут включать в себя больше транзакций, подлежащих обработке.
Поскольку изменения, связанные с базовой платой (БП), имеют свои ограничения, максимальная разница в размере БП от блока к блоку предсказуема. Это позволит кошелькам автоматически устанавливать плату за газ для пользователей.
Дефляционное влияние на Эфириум
Базовая плата будет сжигаться, тем самым оказывая дефляционное влияние на блокчейн Ethereum. Чтобы представить это в перспективе, эмиссионная процентная ставка в Ethereum составляет 4,5% в год, и, по оценкам, после реализации предложения она составит 0,5 — 1%. Это просто ПОТРЯСАЮЩЕ!
Если бы предложение EIP-1559 было реализовано вчера, уже было бы сожжено где-то 17 000 ETH. В год это составило бы более 6,2 миллиона! Это окажет гораздо более сильный эффект, чем эффект халвинга на Bitcoin.
Перспектива для майнеров и плата за включение
EIP-1559 – это, без сомнения, плохая новость для майнеров!
В прошлом месяце майнеры Ethereum заработали колоссальные 1,3 миллиарда долларов, и почти 50% из них пришлось на транзакционные сборы.
Представьте себе, что вы теряете большую часть своего бизнеса: это звучит болезненно! Наряду с базовой платой существует плата за включение, которая будет напрямую поступать майнерам. Однако люди могут не добавлять плату за включение.
Два из трех крупнейших майнинговых пулов Ethereum заняли публичную позицию по EIP-1559. Sparkpool (совокупный хешрейт выше 50% в настоящее время) занял оппозиционную позицию, в то время как F2Pool (11% хешрейта сети) поддерживает это решение.
Несмотря на это, EIP-1559 дает майнерам больше контроля над размером блока и делает их доход более предсказуемым. EIP-1559 пройдет вместе с хардфорком «Лондон» в июле этого года, не зависимо от того, нравится это или нет майнинговому сообществу.
Узнайте больше о EIP-1559
Если вы хотите узнать больше об этом предложении по улучшению, ознакомьтесь со следующими ресурсами:
- EIP-1559 GitHub (официальный источник)
- Ethereum Notes (официальный источник)
- CoinDesk (краткое руководство)
Пишем gas station для EIP-1559 транзакций
При переходе на отправку транзакций в формате EIP-1559 столкнулись с задачей по оценке комиссии за транзакцию в зависимости от ожидаемой скорости. Работали долгое время с одним известным источником транзакций, пока не начали приходить ошибки на запросы. Поиск альтернатив, которые бы дали возможность оценить стоимость комиссии в зависимости от ожидаемой скорости не нашлось. Было принято решение еще раз погрузиться в процесс изучения возможных подходов к решению. Задача стоит в том, чтобы сделать оценку в виде комиссии для скоростей fastest, fast, average, safeLow. О том, как решали задачу и к чему пришли под катом.
Немного о EIP-1559
Для начала поговорим о самом изменении, почему нам нужен новый механизм расчета стоимости топлива за транзакцию. Данное изменение принесло новый способ расчета и определения комиссии за транзакцию. Данное изменения включено с хардфорка London. В новой системе расчета стоимость газа складывается из base fee — базовая комиссия, которая будет сожжена, и комиссии за включение блока (inclusion fee). Значение для base fee зависит от заполненности предыдущего блока и рассчитывается по понятному алгоритму, при отправке транзакции на это значение не удастся повлиять. При этом baseFee не столь сильно может изменяться в цене по сравнению со старой аукционной моделью. Это делает новых подход более предсказуемым.
Составляющая комиссии представлена двумя значениями:
- maxFeePerGas — максимальная стоимость, которую вы готовы заплатить за газ, состоит из baseFee + priorityFee
- maxPriorityFeePerGas — составляющая priorityFee
У ноды появился новый эндпоинт eth_maxPriorityFeePerGas, которые позволяет получить ожидаемое значение для priorityFee для включения в новый блок.
Подход к определению цены на газ для транзакции
Для начала возьмем за основу наивный подход без попыток угадать приоритеты. Примеры будут с кодом на Go. Для получения исторических данных по блокам я использовал API Alchemy.
Для управления скоростью можно использовать различные комбинации параметров, управляющих комиссией для майнеров. Значение для maxPriorityFeePerGas получаем из eth_maxPriorityFeePerGas. Далее рассчитываем maxFeePerGas = maxPriorityFeePerGas + baseFee * 2 . Значение для baseFee берем из последнего блока. Умножение значения на 2 позволяет не учитывать возможные увеличения комиссии в зависимости от заполненности блоков, а сразу заложить максимальное значение. Такой подход позволит включить транзакцию в блок, и вероятность будет зависеть только от попадания maxPriorityFeePerGas в ожидания майнеров. То есть комбинацией maxFeePerGas и maxPriorityFeePerGas можно наложить ограничение на включение в блок в зависимости от роста/падения baseFee.
Рассмотрим описанный пример в коде.
package main import ( "context" "fmt" "github.com/ethereum/go-ethereum/ethclient" "os" ) const EthMainnetEndpoint = "" func weiToGwe(wei int64) float64 < gWei := float64(wei) / 1e9 return gWei >func determineEndpoint() string < endpoint, present := os.LookupEnv("ENDPOINT") if present == false < endpoint = EthMainnetEndpoint >return endpoint > func initiateClient(endpoint string) (*ethclient.Client, error) < client, err := ethclient.Dial(endpoint) if err != nil < return nil, err >fmt.Println("Successfully made connection with endpoint") return client, nil > func getBaseFeeValue(client *ethclient.Client) (int64, error) < lastBlockRec, err := client.BlockByNumber(context.Background(), nil) if err != nil < return 0, fmt.Errorf("error getting last block data: %w", err) >lastBlockBaseFeeWei := lastBlockRec.BaseFee().Int64() return lastBlockBaseFeeWei, nil > func getSuggestedFee(client *ethclient.Client) (int64, error) < suggestedFee, err := client.SuggestGasTipCap(context.Background()) if err != nil < return 0, fmt.Errorf("error getting SuggestGasTipCap: %w", err) >return suggestedFee.Int64(), nil > func calculateBasicFees(client *ethclient.Client) < nodeLastBlock, err := client.BlockNumber(context.Background()) if err != nil < fmt.Printf("Error getting BlockNumber: %v", err) >if err != nil < fmt.Printf("error creating client: %v", err) return >baseFeeValue, err := getBaseFeeValue(client) if err != nil < fmt.Printf("error getting base fee value: %v", err) >suggestedFeeValue, err := getSuggestedFee(client) if err != nil < fmt.Printf("error getting suggested fee value: %v", err) >maxFeePerGas := suggestedFeeValue + baseFeeValue*2 fmt.Printf("For block %d calculated:\\\\n\\\\tbase fee: %f\\\\n\\\\tmaxFeePerGas: %f\\\\n\\\\tmaxPriorityFeePerGas: %f\\\\n", nodeLastBlock, weiToGwe(maxFeePerGas), weiToGwe(maxFeePerGas), weiToGwe(suggestedFeeValue)) fmt.Println("----") >
Функция getBaseFeeValue получает значение baseFee из последнего блока. В getSuggestedFee запрашивается значение для maxPriorityFeePerGas. Далее просто рассчитываем maxFeePerGas по формуле. Пример вывода:
For block 14372134 calculated: base fee: 35.902469 maxFeePerGas: 72.804938 maxPriorityFeePerGas: 1.000000
Данный подход подходит для простого подсчета комиссии. Но не делает отправку транзакции дешевой. Если предыдущие блоки были заполнены, то будет действовать максимальная ставка для baseFee. Скорее всего в этой ситуации и значение maxPriorityFeePerGas будет достаточно высокое. Для экономии комиссии (жертвуя скорость), можно управлять комбинацией maxFeePerGas и maxPriorityFeePerGas. Указав низкое значение для maxPriorityFeePerGas получим ситуацию, когда транзакция становится мало привлекательной. Ограничив maxFeePerGas получаем верхний порог для baseFee = maxFeePerGas — maxPriorityFeePerGas
Расчет стоимости для управления скоростью транзакции
Предыдущая реализация позволит рассчитать комиссию с запасом для практически гарантированного добавления транзакции в следующий блок (особенно есть щедро указать значение для maxPriorityFeePerGas, диапазон для baseFee и так оставили широкий). Но, как правило, необходимо иметь возможность управлять соотношением скорость-цена транзакции, двигая «ползунок» в определенную сторону. В начале статьи указал ожидания по вариантам скорости: fastest, fast, average, safeLow. До введения EIP-1559 достаточно было проанализировать значения для gasPrice транзакций из предыдущего блока для понимания диапазона стоимости газа для транзакции. После изменения мы получили 2 плавающие составляющие: baseFee и priorityFee. Так как повлиять на значение baseFee мы не можем, оно вычисляется протоколом, остается манипулировать значением priorityFee.
Для расчета стоимостей комиссии выполним анализ предыдущих блоков. Необходимо ответить на вопросы:
- насколько заполнены предыдущие блоки?
- с какими комиссиями транзакции включены в блок?
В Alchemy есть API, который позволяет получить выгрузку исторических данных по блокам. Если нет желания использовать сторонний API, данный функционал можно реализовать самостоятельно, получая последние блоки и анализируя транзакции из них.
Alchemy предоставляет RPC интерфейс для получения истории блоков, но обработчик не входит в стандартный go-ethereum. Поэтому реализован свой JSON-RPC запрос с использованием библиотеки github.com/ybbus/jsonrpc/v2.
Для вызова метода getHistoryBlocks передаем эндпоинт, количество блоков и процентиль для значений priorityFees из блоков. Преобразуем ответ в более удобный для работы формат, разбив значения для каждого блока. Получаем массив HistoryBlockFees:
- BlockNumber — номер блока
- BaseFee — значение baseFee блока
- PriorityFeesPerGas — значения priorityFees по заданным процентилям
- UsedRatio — насколько заполнен блок — из этого значения можно предсказать baseFee
package main import ( "fmt" "github.com/ybbus/jsonrpc/v2" "strconv" ) type HistoryRewards struct < OldestBlock string `json:"oldestBlock"` Reward [][]string `json:"reward"` BaseFeePerGas []string `json:"baseFeePerGas"` GasUsedRatio []float64 `json:"gasUsedRatio"` >// HistoryBlockFees - history fees from block. all fees are in wei type HistoryBlockFees struct < BlockNumber int64 `json:"block_number"` BaseFee int64 `json:"base_fee"` PriorityFeesPerGas map[int]int64 `json:"priority_fees_per_gas"` // In percentiles UsedRatio float64 `json:"used_ratio"` >func hexToInt(hexVal string) (int64, error) < hexValStr := hexVal[2:] value, err := strconv.ParseInt(hexValStr, 16, 64) if err != nil < return 0, err >return value, nil > func convertRewardsPercentiles(rewards []string, percentiles []int) (map[int]int64, error) < if len(rewards) != len(percentiles) < return nil, fmt.Errorf("lengths of rewards and percentiles are different") >results := make(map[int]int64) for i := 0; i < len(percentiles); i++ < rewardVal, err := hexToInt(rewards[i]) if err != nil < return nil, fmt.Errorf("error converting reward to int64: %w", err) >percentile := percentiles[i] results[percentile] = rewardVal > return results, nil > func getHistoryBlocks(endpoint string, blocksCount int, expectedRewardsPercentiles []int) ([]HistoryBlockFees, error) < callParams := []interface<> rewards := &HistoryRewards<> rpcClient := jsonrpc.NewClient(endpoint) err := rpcClient.CallFor(rewards, "eth_feeHistory", callParams) if err != nil < return nil, fmt.Errorf("error making call: %w", err) >blockNumHex := rewards.OldestBlock[2:len(rewards.OldestBlock)] blockNum, err := strconv.ParseInt(blockNumHex, 16, 64) if err != nil < fmt.Printf("error converting block hex number %s to int64\\n", blockNumHex) >fmt.Printf("Latest block in %d\\n", blockNum) baseFees := make([]int64, 0, 4) for _, hexVal := range rewards.BaseFeePerGas < hexStr := hexVal[2:] value, err := strconv.ParseInt(hexStr, 16, 64) if err != nil < fmt.Printf("error converting %s to int64\\n", hexVal) >baseFees = append(baseFees, value) > blocksRewards := make([]HistoryBlockFees, 0, blocksCount) var i int64 for i = 0; i < int64(blocksCount); i++ < blockBaseFeeGas := rewards.BaseFeePerGas[i] blockBaseFeeGasValue, err := hexToInt(blockBaseFeeGas) if err != nil < fmt.Printf("error converting %s to int64\\\\n", blockBaseFeeGas) >priorityFeesPerGas, err := convertRewardsPercentiles(rewards.Reward[i], expectedRewardsPercentiles) if err != nil < fmt.Printf("error converting priorite fees per gas for block: %e\\\\n", err) >resultRec := HistoryBlockFees < BlockNumber: blockNum - i, BaseFee: blockBaseFeeGasValue, PriorityFeesPerGas: priorityFeesPerGas, UsedRatio: rewards.GasUsedRatio[i], >blocksRewards = append(blocksRewards, resultRec) > return blocksRewards, nil >
Пример выполнения запроса для одного блока:
< "block_number": 14372299, "base_fee": 33552954122, "priority_fees_per_gas": < "10": 1447045878, "20": 1500000000, "30": 1500000000, "40": 1500000000, "50": 1500000000, "60": 1500000000, "70": 2000000000, "80": 2500000000, "90": 4336870765 >, "used_ratio": 0.9341605645390547 >
Мы видим, что блок почти полностью заполнен. Разброс комиссий в 90% процентиль практически в 2 выше соседней. При этом в середине мы видим одинаковые значения в 1.5 GWei.
Связь между baseFee и usedRatio блока заложена в логику EIP-1559. Если кратко, то логика изменения значения такова: если блок заполнен более чем на половину, значение baseFee возрастет в пределах 12.5%. Если блок заполнен менее чем на половину, комиссия уменьшится в пределах 12.5%.
Первичный вариант реализации — использовать процентиль для расчета примерных комиссий. То есть взять за основу определенные уровни комиссий и посчитать средние по ним за последние блоки. Это и будет значением для priorityFees в зависимости от скорости.
Реализованный с вводом стандарта EIP-1559 метод eth_maxPriorityFeePerGas обещает расчет комиссии на уровне, который позволит включить транзакцию в следующий блок. То есть можно принять результат вызова этого метода за safeLow уровень. Для начала попробуем реализовать аналог данному методу, потом перейдем к реализации различных уровней по скорости.
Код подсчета средних комиссий:
func calculateFeeFromHistory(endpoint string) < blocksCount := 10 expectedRewardsPercentiles := []inthistoryItems, err := getHistoryBlocks(endpoint, blocksCount, expectedRewardsPercentiles) if err != nil < fmt.Printf("error getting history items: %v", err) return >perPercentilesLevels := make(map[int][]int64) for _, percentile := range expectedRewardsPercentiles < perPercentilesLevels[percentile] = make([]int64, 0, blocksCount) >for _, blockRec := range historyItems < for percent, feeLevel := range blockRec.PriorityFeesPerGas < perPercentilesLevels[percent] = append(perPercentilesLevels[percent], feeLevel) >> feesAverages := make(map[int]int64) for percentile, feesValues := range perPercentilesLevels < var total int64 for _, number := range feesValues < total = total + number >average := total / int64(len(feesValues)) feesAverages[percentile] = average > for percent, average := range feesAverages < fmt.Printf("\\\\tfor %d average value is %f\\\\n", percent, weiToGwe(average)) >>
В итоге получаем:
- для 1% процентиль: 0.793284
- для 5% процентиль: 0.793284
- для 10% процентиль: 1.367154
- для 15% процентиль: 1.417776
При этом получаемое с ноды значение для maxPriorityFeePerGas: 1.034000. То есть где-то между 5% и 10%. Подсчет ближайших значений показал, что лучше всего подходит 5 процентиль. Примем его за safeLow значение.
Далее стоит задача выбрать уровни для остальных требуемых скоростей. Для анализа выполнили выбор уровней с шагом в 5% и проанализировали, в какие моменты есть существенные изменения в ценах. В примере для одного блока видим, что в середине диапазона довольно часть значения комиссий одинаковые. Следующим шагом примем следующие уровни для расчета комиссий для скоростей:
- fastest — 85%
- fast — 55%
- average — 10%
Значения выбраны на основании статистики по изменениям комиссии в зависимости от уровней. Также замерена статистика по сравнению с результатами API BlockNative. Данные значения гибко настраиваются и могут быть в любой момент изменены по результатам исторических замеров реальных транзакций.
Дальнейшие изменения
Чтобы не вызывать API на каждый запрос, нужно кэшировать ответы, и обновлять ожидания скорости для каждого нового блока. Также, для более точного предсказания величины комиссии можно строить модели предсказаний, например, основываясь на заполненности блока и изменении baseFee.
В итоговой реализации взяты уровни для maxPriorityFeePerGas на основании средних за последние 10 блоков. Значение для maxFeePerGas вычисляется c запасом на рост baseFee:
maxFeePerGas = maxPriorityFeePerGas + baseFee * 2
Значение для baseFee берется из последнего блока.
Финальная реализация с HTTP API и кэшированием выложена на github. В данной реализации имеется сервер для ответов на запросы по ценам и воркер, который периодически обращается к ноде и рассчитывает стоимость для нового блока.
Буду рад комментариям, замечаниям, предложениям по улучшению и, конечно же, pull request на гитхабе.