> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tenderly.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Как получить доступ к Node RPC из Web3 Actions

> Узнайте, как использовать Web3 Actions и Node RPC для поиска контрактов с одинаковым байткодом, развёрнутых другим контрактом.

В этом туториале мы создадим Web3 Action, который будет запускаться каждый раз, когда `UniswapV2Factory` развёртывает новый контракт `UniswapV2Pair`. Web3 Action будет сравнивать байткод нового развёрнутого контракта с байткодом другого контракта `UniswapV2Pair`. В данном случае мы используем контракт пары USDC.

Для получения байткодов мы будем использовать [Node RPC](/node-rpc/overview) в качестве нашего провайдера ноды. Если байткоды совпадают, Web3 Action сохранит адрес пары и байткод контракта в Storage вашего Web3 Action.

Если вы хотите пропустить туториал, вот полное решение:

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}



type UniswapPair = {
  token0: string;
  token1: string;
  pair: string;
};

const USDC_PAIR_CONTRACT_ADDRESS = '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc';

export const onPairCreatedEventEmitted: ActionFn = async (context: Context, event: Event) => {
  try {
    const txEvent = event as TransactionEvent;
    const newPair = await getPairCreatedEvent(txEvent);

    const gatewayURL = context.gateways.getGateway(Network.MAINNET);
    const provider = new ethers.providers.JsonRpcProvider(gatewayURL);

    const usdcPairContract = await provider.getCode(USDC_PAIR_CONTRACT_ADDRESS);
    const newPairContract = await provider.getCode(newPair.pair);

    if (usdcPairContract === newPairContract) {
      const pairContracts = await context.storage.getJson('PairContracts');
      pairContracts[newPair.pair] = newPairContract;
      await context.storage.putJson('PairContracts', pairContracts);
    }
  } catch (error) {
    console.error(error);
  }
};

const getPairCreatedEvent = async (txEvent: TransactionEvent): Promise<UniswapPair> => {
  const i = new ethers.utils.Interface(UniswapV2FactoryAbi);
  const pairCreatedTopic = i.getEventTopic('PairCreated');

  const pairCreatedEventLog = txEvent.logs.find(log => {
    return log.topics.find(topic => topic == pairCreatedTopic) !== undefined;
  });

  if (pairCreatedEventLog == undefined) {
    throw Error('PairCreatedEvent missing');
  }

  return i.decodeEventLog(
    'PairCreated',
    pairCreatedEventLog.data,
    pairCreatedEventLog.topics,
  ) as unknown as UniswapPair;
};
```

### Предварительные требования

<Note>
  Механизм триггеров Web3 Actions работает только если контракт, используемый в определении
  триггера, верифицирован в Tenderly. Узнайте, [как верифицировать контракты в Tenderly
  здесь](/contract-verification/overview).
</Note>

В этом примере мы будем использовать контракты `UniswapV2Factory` и `UniswapV2Pair`. Поскольку оба контракта публично верифицированы в Tenderly, нет необходимости верифицировать их вручную.

Перейдите к [UniswapV2Factory](https://dashboard.tenderly.co/contract/mainnet/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f) и нажмите кнопку **Add to Project**, чтобы добавить контракт в ваш проект. Повторите тот же шаг для [UniswapV2Pair](https://dashboard.tenderly.co/contract/mainnet/0xd849b2af570ffa3033973ea11be6e01b7ba661d9).

## Шаг 1: Определите триггер

Всякий раз, когда вызывается [функция `createPair`](https://developers.uniswap.org/contracts/v2/reference/smart-contracts/factory#createpair) контракта `UniswapV2Factory`, происходит следующее:

* Создаётся новый контракт [UniswapV2Pair](https://developers.uniswap.org/contracts/v2/reference/smart-contracts/pair) для пары токенов (если такого ещё не существует)
* Генерируется событие [`PairCreated`](https://developers.uniswap.org/contracts/v2/reference/smart-contracts/factory#paircreated)

Мы можем определить триггер для запуска Web3 Action каждый раз, когда транзакция, обращающаяся к контракту, вызывает функцию, генерирующую конкретное событие.

В нашем случае мы напишем триггер, срабатывающий каждый раз, когда контракт `UniswapV2Factory` генерирует событие `PairCreated`. Другими словами, всякий раз при развёртывании нового контракта `UniswapV2Pair`.

Конфигурация триггера выглядит так и должна быть сохранена в файле `tenderly.yaml`:

```yaml title="example.yaml" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
account_id: ''
actions:
  YOUR_USERNAME/YOUR_PROJECT_SLUG:
    runtime: v2
    sources: actions
    specs:
      uniswapNewPair:
        description: Runs when a new pair is created on uniswap
        function: uniswapActions:onPairCreatedEventEmitted
        execution_type: parallel
        trigger:
          type: transaction
          transaction:
            status:
              - mined
            filters:
              - network: 1
                eventEmitted:
                  contract:
                    address: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
                  name: PairCreated
project_slug: ''
```

## Шаг 2: Определите логику Web3 Action

Когда наш Web3 Action запускается, он получает два входа:

* Объект [`context`](/monitoring/web3-actions/references/context), обеспечивающий доступ к Node RPC и Storage
* Полезная нагрузка [`transactionEvent`](/monitoring/web3-actions/references/functions-events-triggers#transaction-event), содержащая данные, относящиеся к транзакции, вызвавшей Web3 Action

Мы будем использовать объект `context` для доступа к `gateways`, которые будут нашим провайдером ноды для Ethers, и Storage для хранения совпавших байткодов.

<Note>
  Узнайте, [как использовать Node RPC в Web3
  Actions](/monitoring/web3-actions/references/node-rpc-access) для доступа к любой
  поддерживаемой сети, включая Mainnet, без хлопот с управлением URL или секретными ключами.
</Note>

Что касается полезной нагрузки `transactionEvent`, мы используем её свойство `logs`, чтобы найти лог события, соответствующий topic `PairCreated`.

### Шаг 2.1: Декодируйте событие PairCreated

При срабатывании Web3 Action нужно найти событие `PairCreated` в логах полезной нагрузки. Этот код мы поместим в функцию `getPairCreatedEvent`.

Чтобы получить хеш topic'а `PairCreated`, нам нужен ABI контракта `UniswapV2Factory`. Для этого перейдите к [контракту `UniswapV2Factory` в Tenderly](https://dashboard.tenderly.co/contract/mainnet/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f), нажмите **View ABI** и скопируйте его в локальный файл в каталоге `actions`.

<Note>
  Изучите [структуру проекта Web3 Action
  здесь](/monitoring/web3-actions/references/project-structure).
</Note>

Теперь мы можем получить ссылку на этот topic со следующим кодом:

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const i = new ethers.utils.Interface(UniswapV2FactoryAbi);
const pairCreatedTopic = i.getEventTopic('PairCreated');
```

Далее нам нужно пройти по логам транзакции и найти запись, ссылающуюся на этот topic, вот так.

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const pairCreatedEventLog = txEvent.logs.find(log => {
  return log.topics.find(topic => topic == pairCreatedTopic) !== undefined;
});
```

Наконец, декодируем запись лога в тип `UniswapPair` с помощью Ethers.

Результат функции `getPairCreatedEvent` — это объект, содержащий следующее:

* Адреса токенов, составляющих пару, — свойства `token0` и `token1`
* Адрес развёрнутого контракта `UniswapV2Pair` — свойство `pair`

## Шаг 3: Сравните байткоды

Теперь, когда у нас есть адрес вновь развёрнутого контракта (содержащийся в свойстве `pair` типа `UniswapPair`), мы можем использовать его для получения байткода контракта.

Мы будем использовать байткод контракта `USDC UniswapV2Pair`, чтобы определить, является ли байткод вновь созданного контракта валидным контрактом `UniswapV2Pair`. Их байткоды должны совпадать в точности.

Чтобы получить байткоды этих контрактов, нам нужно использовать функцию `getCode` Ethers.

Для выполнения этих шагов нам сначала нужно настроить провайдера ноды, Node RPC: `gateways`.

Обратившись к свойству `gateways` объекта `context`, мы можем легко получить JSON-RPC URL для нужной сети (`MAINNET` в нашем случае), вызвав функцию `getGateway()` и используя её как провайдер для Ethers.

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const gatewayURL = context.gateways.getGateway(Network.MAINNET);
const provider = new ethers.providers.JsonRpcProvider(gatewayURL);
```

Это позволит нам получить байткод как новой пары, так и пары USDC.

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const usdcPairContract = await provider.getCode(USDC_PAIR_CONTRACT_ADDRESS);
const newPairContract = await provider.getCode(newPair.pair);
```

Если есть совпадение, мы хотим сохранить байткод новой пары в формате `pair_address: bytecode` под свойством JSON `PairContracts` в Storage нашего проекта:

```tsx title="example.tsx" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
if (usdcPairContract === newPairContract) {
  const pairContracts = await context.storage.getJson('PairContracts');
  pairContracts[newPair.pair] = newPairContract;
  await context.storage.putJson('PairContracts', pairContracts);
}
```

Дав Web3 Action поработать некоторое время, мы можем проверить Storage проекта и увидеть созданные пары:

<figure>
  <img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/web3-actions/how-to-access-web3-gateway-from-web3-actions-1.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=f28fc08829c5047fd416162aa0a02f39" alt="Tenderly Docs" width="1216" height="752" data-path="images/web3-actions/how-to-access-web3-gateway-from-web3-actions-1.webp" />

  <figcaption />
</figure>

Ключи — это адреса вновь развёрнутых контрактов `UniswapV2Pair`, а значения все одинаковые и представляют собой байткод контракта `UniswapV2Pair`.
