Быстрые запросы баланса кошелька
Всем привет!
В этой статье я расскажу, как узнать баланс кошелька пользователя, почему перебор не работает, и почему 1inch умнички???
Если вы пользовались DEX обменником 1inch, то наверняка замечали, что баланс каждого ERC20 токена загружается мгновенно
Почему это важно?
Блокчейн эфириума устроен так, что для того, чтобы узнать количество токенов у пользователя, нужно делать запрос баланса по каждому контракту токена
Иными словами, если у вас есть список из 100 монет, и вы хотите узнать сколько монет держит пользователь, то необходимо сделать 100 запросов в блокчейн
Рассмотрим 3 метода получить количество токенов
В дальнейших примерах я буду использовать пакет 1inch multicall
npm install @1inch/multicall
yarn install @1inch/multicall
ПРОСТОЙ ПЕРЕБОР
Мы берем массив tokens
и методом map
пробегаем по массиву контрактов токенов
return provider.ethCall(tokenAddress, callData);
- возврашает нам количество токенов в hex формате
Далее идем на сайт перевода систем счисления и видим, что все сходится (это все в wei, а в эфире это как раз 0,001025eth)
НО!
Если у нас не три токена, а 1009, как у 1inch, то получается вот что
Поясняю, 2,7 секунд на запрос - это очень много для веб приложения
А если вы используете провайдера Alchemy/Infurra, то получите такое
Ребята из 1inch решили эту проблему, и вот как...
МНОЖЕСТВЕННЫЙ ЗАПРОС
Мы не отправляем 1009 транзакций в блокчейн, а разбиваем их на группы (chunks)
const params = { chunkSize: 100, retriesLimit: 3, blockNumber: 'latest', };
Далее вызываем асинхронный метод класса multiCallService
multiCallService.callByChunks(callDatas, params).then((res)=>{//тут логика})
И радуемся огромному массиву с балансами токенов)
НО!
Если вызов этого метода достигнет лимита по газу (даже у функций чтения имеется газлимит) при вызове контракта, то запрос развернут, и вы не получите ответа
Изначально в блоке 15 млн газа
Но если спрос высокий, то количество газа увеличивается до 30 млн газа
Транзакцию также развернут, если она занимает очень много времени
Поэтому есть еще более умный способ это сделать...
ЗАПРОС С ОПТИМИЗАЦИЕЙ ГАЗА
Чтобы не натыкаться на ограничения по газу на запрос, теперь мы будем узнавать у ноды лимиты на газ и разбивать запрос на чанки в соответсвии с лимитам
Отличие тут в том, что если какой-то чанк не поместится, весь запрос не отклонят, а отклонят только один чанк, и мы потом повторно попробуем получить данные
Вот объяснение на картиночках
У нас есть чанк с максимальным размером 6 запросов
Чтобы узнать лимит по газу на запрос мы берем минимум из максимального лимита на газ, который мы установили, и лимита на газ из ноды,
после этого вычитаем буффер (на схемке все видно)
Теперь мы знаем в какое количество газа нам нужно уложиться и добавляем в чанк запросы пока не достигнем лимита (это произошло на 5 и 11 запросе)
Допустим запросы 4, 9, 10, 12 не выполнились
Теперь мы уменьшаем максимальное количество запросов в чанке в 2 раза и еще раз пытаемся выполнить обращения к блокчейну
Ура! Все запросы выполнены, в конце просто их складываем и получаем ответ
PROFIT: Время запроса уменьшилось в 2 раза, и теперь мы не получаем ошибки из-за огромного количества обращений от Alchemy/Infurra
Как и обещал, вот ссылка на гитхаб https://github.com/chpotl/fastErc20Balance
Тут контракт 1inch - https://etherscan.io/address/0x8d035edd8e09c3283463dade67cc0d49d6868063#code
Тут репозиторий 1inch с этим пакетом - https://github.com/1inch/multicall