Как создать список всех транзакций с токенами в кошельке

Как создать список всех транзакций с токенами в кошельке

28 января 2023 г.

Недавно меня попросили создать список всех транзакций с токенами, совершенных в кошельке моей компании. Естественно, я начал с Etherscan в надежде, что смогу загрузить и экспортировать список транзакций — только чтобы обнаружить, что такая функциональность ограничена 5000 строками. Как вы можете догадаться, интересующий меня кошелек имел значительно большее количество транзакций. Какой облом. Это означало, что единственный способ получить нужный мне список — это написать скрипт, собирающий нужную мне информацию.

Я решил поделиться своими знаниями с помощью скрипта с открытым исходным кодом, потому что я считаю, что эта проблема может быть достаточно распространенной, чтобы другие могли извлечь из нее пользу. В конце концов, у всех компаний в сфере блокчейна одинаковые проблемы.

<цитата>

Вы можете найти скрипт и его исходный код на Github

В этой статье я использую свой личный кошелек для упрощения примеров. В моем личном кошельке всего 17 транзакций, но скрипт на моем Github хорошо работает со значительно большим количеством транзакций, так что не стесняйтесь его использовать. Я буду работать над своими транзакциями, связанными со стабильной монетой DAI. Давайте проверим историю моего кошелька на Etherscan.

Как видите, я сделал кучу переводов и несколько взаимодействий смарт-контракта (такие методы, как Deposit< /em>, Вывод средств, Порядок файлов, Обмен токеном и т. д.). Я упоминаю взаимодействия смарт-контрактов, потому что их сложнее обрабатывать из-за кодирования, но это становится неуместным, если вы будете следовать моему руководству.

Отказ от ответственности

Все примеры в этой статье написаны на Python. Я использую Python v3.10.5 и широко использую библиотеку под названием web3.py. . Эта библиотека позволяет мне взаимодействовать с Ethereum, и вы можете установить ее с помощью PyPI, введя следующую команду в своем терминале:

$ pip install web3

Если вы пишете только на JavaScript, не волнуйтесь. Вы можете перевести мои фрагменты кода на JavaScript и использовать библиотеку web3.js. Есть некоторые различия в пространствах имен и именовании, но руководство в основном применимо.

Начнем!

Первое, что должен сделать скрипт, это установить соединение с блокчейном Ethereum. Для этого нам нужно найти Ethereum API. Если у вас нет собственного узла, вы можете получить бесплатный API от Infura, QuickNode или Moralis. Выберите один, я использую Infura для своих личных проектов (как видите, я вставил его в строку 9).

import asyncio
from web3 import Web3, AsyncHTTPProvider
from web3.eth import AsyncEth, Eth
from web3.net import AsyncNet


async def runner():
    ethereum_api_url = "https://mainnet.infura.io/v3/MY_SUPER_SECRET_TOKEN"

    async_web3 = Web3(
        AsyncHTTPProvider(ethereum_api_url),
        modules={
            "eth": (AsyncEth,),
            "net": (AsyncNet,),
        },
        middlewares=[],
    )
    web3_modules = get_default_modules()
    web3_modules["eth"] = Eth

    block = await async_web3.eth.get_block("latest")
    print("Current block:", block.number)

    # All code goes here
    await asyncio.sleep(0)


if __name__ == "__main__":
    asyncio.run(runner())

Я создал асинхронный поставщик web3 и буду использовать его для взаимодействия с blockchain. Вы могли заметить, что во фрагменте выше я извлекаю последний блок. Хотя это и не требуется для выполнения моей задачи, я делаю это, чтобы убедиться, что мой провайдер web3 подключен к Ethereum.

Когда вы запустите этот скрипт, вы увидите следующий результат:

$ python runner.py
Current block: 16436276

Теперь мы готовы сканировать блокчейн.

Журналы охоты

Давайте еще раз заглянем в историю транзакций моего кошелька, особенно на мою самую первую транзакцию. Как видите, первая транзакция — 0xe3d38...cefaa2e, и она была отправлена ​​в блоке 13352962. На момент написания этой статьи Ethereum добыл блок 16436276; это означает, что моя первая транзакция была выполнена более 3 миллионов блоков назад! Очевидно, что получение всех транзакций путем запроса каждого отдельного блока на самом деле не вариант. Не поймите меня неправильно - это возможно, но требует очень много времени. Вся обработка займет не менее нескольких часов (или дней!). Это слишком медленно.

Давайте копнем немного глубже и сосредоточимся на вкладке Журналы в Etherscan. Как видите, у этой транзакции есть одно событие — событие Transfer.

Ethereum индексирует журналы каждой транзакции. Эта индексация означает, что она позволяет запрашивать определенные журналы, предоставляя более широкие диапазоны блоков и адреса смарт-контрактов. Диапазон блоков может быть широким, но выходные данные не могут превышать 10 000 журналов за вызов. Разве это не удивительно?

Вернемся к нашему событию Transfer. Вы можете заметить, что у него есть три темы: тема 0 — это подпись события Transfer. Он сообщает нам, что все события Transfer имеют сигнатуру 0xddf252ad...f523b3ef. Эта информация имеет решающее значение, мы будем использовать ее, чтобы найти все переводы в наш кошелек, но я вернусь к этому позже. Давайте сейчас сосредоточимся на теме 1 и 2.

Большинство токенов на Ethereum (за исключением NFT и Ether) являются токенами ERC-20, и DAI ничем не отличается. Самый простой способ узнать о темах, затронутых событием Transfer, — прочитать смарт-контракт токена ERC-20. К счастью, некоторые из них имеют открытый исходный код, и мы можем найти их исходный код на Github. Мы будем изучать интерфейсы и искать событие Transfer и его определение.

Как видите, у события Transfer есть два индексированных аргумента — from и to — и value, которое не т индексируется. Проиндексированные аргументы отображаются как темы, и мы можем запросить связанные с ними журналы.

Теперь мы знаем, что тема 1 – это адрес от, а тема 2 – это адрес куда. адрес. Давайте запросим все входящие транзакции DAI в мой кошелек от блока 13 352 962 (моя первая транзакция DAI) до блока 16 436 276 (текущий блок).

Если вы внимательно посмотрите на этот фрагмент, вас могут смутить две вещи. Первое — это поле адрес. Это не адрес моего кошелька, но адрес DAI (смарт-контракт) . Если бы вы указали там адрес кошелька, вы бы ничего не нашли. Кошельки не реализуют никаких событий с подписью Transfer.

Во-вторых, адрес кошелька начинается с 24 нулей. Это связано с EVM - типы данных пользователя 32 байта темы и адреса кошелька хранятся в 20 байтах. Это означает, что мне нужно заполнить «свободное» место нулями.

Если вы запустите код фрагмента, вы увидите массив журналов, который выглядит следующим образом:

[
    AttributeDict({
        'address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 
        'blockHash': HexBytes('0xa67b8ceaeb79ec2592e161ee2efee6fba3fd329c87131d9335ccaa869cc857ec'), 
        'blockNumber': 13352962, 
        'data': '0x0000000000000000000000000000000000000000000000356ea11fcb4975c000', 
        'logIndex': 85, 
        'removed': False, 
        'topics': [
            HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), 
            HexBytes('0x00000000000000000000000021a31ee1afc51d94c2efccaa2092ad1028285549'), 
            HexBytes('0x000000000000000000000000d5e73f9199e67b6ff8dface1767a1bdadf1a7242')
        ], 
        'transactionHash': HexBytes('0xe3d380b3647abee2f8e2980d8e3bf6e9a43b00a0f4a388765585df13ecefaa2e'), 
        'transactionIndex': 41
    }), 
    ...
]

Если вы внимательно прочитаете его, то сможете определить отправителя и получателя каждого журнала (тема 1 и 2). Единственная отсутствующая часть — это стоимость транзакции, верно? На самом деле он хранится в поле data, но закодирован. Его можно расшифровать с помощью метода toInt из библиотеки web3.py.

Давайте попробуем.

from web3 import Web3

value = Web3.toInt(hexstr="0x0000000000000000000000000000000000000000000000356ea11fcb4975c000")
print(value) # 985649123680000000000

Скрипт вернул 985649123680000000000, и это число кажется слишком большим, верно?

Не совсем так, DAI имеет 18 знаков после запятой — это означает, что вам нужно разделить это число на 10 в степени 18 (10^18), чтобы получить «человекочитаемое» число. . Если вы сделаете это, вы обнаружите, что стоимость этой транзакции составила 985,64912368 DAI. Вы можете подтвердить этот номер транзакцией на Etherscan.

Заключение

Обработка Ethereum по блокам может быть утомительной и ненужной. Часто мы можем искать в журналах события, связанные с транзакциями, которые мы ищем. Как я сказал в самом начале, вы можете найти полноценный скрипт на Github со всеми инструкциями о том, как запустить его. В статье только самое интересное.

Если у вас есть какие-либо вопросы, вы можете начать обсуждение на Github или связаться со мной в Twitter. Не стесняйтесь подписываться на меня — я публикую твиты, связанные с Ethereum и разработкой программного обеспечения.

Для меня было бы очень важно, если бы вы поделились этой статьей в своих социальных сетях.

Спасибо!

:::информация Также опубликовано здесь.

:::


Оригинал