Как создать список всех транзакций с токенами в кошельке
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 a> и его определение.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
...
}
Как видите, у события Transfer есть два индексированных аргумента — from и to — и value, которое не т индексируется. Проиндексированные аргументы отображаются как темы, и мы можем запросить связанные с ними журналы.
Теперь мы знаем, что тема 1 – это адрес от, а тема 2 – это адрес куда. адрес. Давайте запросим все входящие транзакции DAI в мой кошелек от блока 13 352 962 (моя первая транзакция DAI) до блока 16 436 276 (текущий блок).
incoming_logs = await async_web3.eth.get_logs(
{
"fromBlock": 13352962,
"toBlock": 16436276,
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", # DAI
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
None,
"0x000000000000000000000000d5e73f9199e67b6ff8dface1767a1bdadf1a7242",
],
}
)
Если вы внимательно посмотрите на этот фрагмент, вас могут смутить две вещи. Первое — это поле адрес. Это не адрес моего кошелька, но адрес 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 и разработкой программного обеспечения.
Для меня было бы очень важно, если бы вы поделились этой статьей в своих социальных сетях.
Спасибо!
:::информация Также опубликовано здесь.
:::
Оригинал