Хотите прочитать интеллектуальные транзакции кошелька? Вам нужно взломать дюну

Хотите прочитать интеллектуальные транзакции кошелька? Вам нужно взломать дюну

31 июля 2025 г.

Если вы когда -либо пытались проанализировать транзакции абстракции учетной записи (AA) на Dune Analytics, вы, вероятно, попали в стену. Дюна фантастическая для большинства аналитиков блокчейна, но когда дело доходит до декодирования комплекса 4337 Smart Account CallData, он не достигнет.

Вот задача:Дюна не может изначально декодировать вызовы v7 v7.Эти транзакции содержат глубоко вложенныеPackedUserOperation[]Массивы, каждый со своими собственными Calldata, которые нуждаются в дальнейшем декодировании.

Чтобы сделать это ограничение, нам понадобится следующее:

  1. Raw Calldata (Dune) - захватите с помощью Dune API
  2. Локальное декодирование (с использованием библиотеки VIEM) - основная задача
  3. Чистые данные (CSV) - должны содержать
  4. Вернуться в дюну - как набор данных дюны
  5. Создайте любые диаграммы, которые вы хотите

Извлечение CallData

Нам нужен запрос на дюну, который извлекает Calldata. Затем мы создадим конечную точку API из этого самого запроса, чтобы взять этот Calldata и создать локальноcalldata.jsonфайл.

Здесь мы извлекаем 100 строк rentpoint v7 calldata в сети Ethereum:

SELECT 
  block_time,
  hash AS tx_hash,
  data AS calldata
FROM 
  ethereum.transactions
WHERE 
  "to" = 0x0000000071727de22e5e9d8baf0edac6f37da032
  AND block_time >= TIMESTAMP '2025-01-01'
  AND block_number BETWEEN {{start_block_number}} AND {{end_block_number}}
LIMIT 100 

Вы заметите, что я включил владельцы мест для номеров блоков. Вам не нужно этого делать:

SELECT 
  block_time,
  hash AS tx_hash,
  data AS calldata
FROM 
  ethereum.transactions
WHERE 
  "to" = 0x0000000071727de22e5e9d8baf0edac6f37da032
  AND block_time BETWEEN TIMESTAMP '2025-01-01 00:00:00' AND TIMESTAMP '2025-06-01 00:00:00'
  
ORDER BY block_time DESC
LIMIT 100 

Но если мы хотим пойти по многоколу, лучше всего использовать время для простоты:

SELECT * FROM (
  -- Ethereum
  SELECT * FROM (
    SELECT
      'ethereum' AS network,
      block_time,
      block_number,
      hash AS tx_hash,
      "to",
      data AS calldata
    FROM ethereum.transactions
    WHERE "to" = 0x0000000071727de22e5e9d8baf0edac6f37da032
      AND block_time BETWEEN TIMESTAMP '2025-01-01 00:00:00' AND TIMESTAMP '2025-06-01 00:00:00'
    ORDER BY block_time DESC
    LIMIT 1000
  )

  UNION ALL

  -- Base
  SELECT * FROM (
    SELECT
      'base' AS network,
      block_time,
      block_number,
      hash AS tx_hash,
      "to",
      data AS calldata
    FROM base.transactions
    WHERE "to" = 0x0000000071727de22e5e9d8baf0edac6f37da032
      AND block_time BETWEEN TIMESTAMP '2025-01-01 00:00:00' AND TIMESTAMP '2025-06-01 00:00:00'
    ORDER BY block_time DESC
    LIMIT 1000
  )

  UNION ALL

  -- Arbitrum
  SELECT * FROM (
    SELECT
      'arbitrum' AS network,
      block_time,
      block_number,
      hash AS tx_hash,
      "to",
      data AS calldata
    FROM arbitrum.transactions
    WHERE "to" = 0x0000000071727de22e5e9d8baf0edac6f37da032
      AND block_time BETWEEN TIMESTAMP '2025-01-01 00:00:00' AND TIMESTAMP '2025-06-01 00:00:00'
    ORDER BY block_time DESC
    LIMIT 1000
  )
)
ORDER BY block_time DESC
LIMIT 3000;

Мы втягиваем это в calldata.json (должно быть что -то вроде ниже) и начинаем декодировать.

На самом деле декодирование

Нам понадобится основной декодер, который организует весь процесс декодирования CallData для транзакций AA. Таким образом, наш главный декодер действует как контроллер дорожного движения, который:

  • Обнаруживает, какой стандарт AA используется через селектор функций
  • Маршруты к соответствующему сценарию специализированного декодера, который у нас есть (ERC7579, Alchemy, Smartvault)
  • Обрабатывает «трюк с нулевым адресом», где реальные цели вложены глубже
Single Execute: прямые вызовы функций

Single Execute - это простой подход, когда интеллектуальный кошелек делает ровно один вызов функции ровно к одному целевому контракту.

Селектор функций для выполнения - это первые 4 байта CallData. Это рассчитывается как первые 4 байта хэша Keccak-256 функции подписи:

execute(address target,uint256 value,bytes data)

После селектора функций параметры кодируются в этом порядке:

  1. цель(Адрес, 32 байта): адрес контракта на звонок
  2. ценить(Uint256, 32 байта): сумма ETH для отправки с вызовом
  3. Calldata(Байты, динамические): CallData для отправки в целевой контракт. Эта предсказуемая структура делает отдельные выполнения транзакции как газоэффективными, так и простыми в декодировании.
Декодирование примера Zerodev

У нас есть следующее событие, если Calltypeexecute(0x00) с 0x01decodeBatch:

if (callType === "0x00") {
    // Single‐call path unchanged
    const targetAddress = `0x${executionCalldata.slice(2, 42)}`;
    const value = BigInt(`0x${executionCalldata.slice(42, 106)}`);
    const callData = `0x${executionCalldata.slice(106)}`;
    console.log("Single call →");
    console.log("  Target:  ", targetAddress);
    console.log("  Value:   ", value.toString());
    console.log("  CallData:", callData);

И иметь следующие Calldata:

const data =
    "0xe9ae5c53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000078c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000001e0049783f008a0085193e00003d00cd54003c7100000000000000000000000000000000000000000000000000000000000000000000000000000000"; 

Мы получаем:

Если вы хотите разыграть вручную/на местном уровне, вотERC7579 Single Execute ScriptПолем

Packation Exepute: организованные многорезовые операции

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

Техническая сложность пакетного выполнения заключается в своей структуре данных. Вместо простого линейного расположения параметров, Packation выполняет массив категорий выполнения, где каждый кортеж содержит целевой адрес, значение и данные вызовов.

0x[function_selector][offset_to_array][array_length][struct1_target][struct1_value][struct1_data_offset][struct1_data_length][struct1_calldata][struct2_target][struct2_value][struct2_data_offset][struct2_data_length][struct2_calldata]...

АexecuteBatchУпаковывает несколько структур выполнения в один массив, причем каждая структура, содержащая одинаковые три поля (цель, значение, CallData), в качестве одного вызова выполнения.

При декодировании пакетных транзакций процесс становится более вовлеченным, потому что мы имеем дело с массивом переменной длины, а не с фиксированными смещениями. Декодер должен сначала понять структуру ABI, затем проанализировать длину массива и, наконец, выполнить через каждый кортеж для выполнения, чтобы извлечь отдельные целевые адреса, значения и данные вызова.

Декодирование примера алхимии

У нас есть следующее событие, если мы обнаружимexecuteBatch:

case "executeBatch": {
            const raw = args[0] as Array<{
                target: string;
                value: bigint;
                data: `0x${string}`;
            }>;
            return raw.map(({ target, value, data }) => ({
                target,
                value,
                callData: data,
            }));
        }

И иметь следующие Calldata:

const data = "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000040b35800bb3e536aee3dc5dbd46d8f0a39c4dffc000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000039008584ef5a94fba2d6d27669429ab47c1fc8e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

// Decode & print
const calls = decodeAlchemyAccountCall(data as `0x${string}`);
console.log(`Decoded ${calls.length} call(s):`);
calls.forEach((c, i) => {
    console.log(`\nCall #${i + 1}`);
    console.log("  target:  ", c.target);
    console.log("  value:   ", c.value.toString());
    console.log("  callData:", c.callData);
});

И выводит нас:

Хотя целевой адрес такой же, Calldata не то, что он, несмотря на то, что он выглядит почти идентично. Иногда может быть даже более двух звонков. ВотСкрипт для алхимииПолем

Но подождите, как мне вручную проверить на Ops.calldata?

Ну, давайте все еще возьмем этот случай алхимии. Из TX HASH:0x59e64f302d912a7f8e091ff4aea503d1628f4973dd793183554a46c631f95b38, пойти вEtherscan.io, Прокрутите вниз до входных данных, выберите «Вход» как исходные и декодирующие входные данные.

Это должно выглядеть как выше.

Работа с различными реализациями и функциями селекторов

Теперь мы должны помнить, что нам нужно справиться с тремя основными типами »реализации:ERC7579ВERC6900и пользовательские реализации.

Я сделал этот очень маленький сценарий для расчета селекторов функций, просмотраздесь! Это определенно не полностью куратор, а хорошая отправная точка.

Принимая функциональные подписи (напримерexecute(address,uint256,bytes)) и преобразовать их в их 4-байтовые шестигранные селекторы (например, 0x34fcd5be), мы можем немедленно знать, какой селектор функций соответствует какой реализации AA. Например, два наиболее распространенных из них:

  • Алхимия (ERC6900): execute(address,uint256,bytes)→ 0xb61d27f6 (Single Execute)
  • Зеродев (ERC7579): execute(bytes32,bytes)→ 0xe9ae5c53 (одиночный выполнение)

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

Заключение

После обработки и декодирования мы создаемdecoded_results.csvПолем И вот как вы получаете целевые адреса!

Чтобы полностью использовать эти данные, нам нужно будетзагрузитьэто для дюны и создания пользовательских запросов на этот набор данных, который должен быть чем -то вродеdune.username.name_of_datasetПолем

Хотя полных деталей реализации здесь нет, я действительно надеюсь, что эта небольшая рецензия будет полезна для кого -то для кого -то! Лично это была крутая кривая обучения, которая немного разбила мой мозг, ха -ха.

Не стесняйтесь DMмнеили ответьте ниже, если у вас есть какие -либо отзывы/комментарии.

~ та та


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE