Перейти к основному содержанию
Этот туториал показывает, как использовать Web3 Action для отправки сообщения в канал Discord при создании нового пула на Uniswap V3. Вы можете использовать этот Web3 Action для уведомления сообщества, когда в цепочке происходит что-то важное.
Полный код доступен в этом Github-репозитории. Обратите внимание, что на данный момент он находится в отдельной ветке.

Обзор проекта

Цель этого проекта — научить вас использовать Web3 Actions для отправки сообщения в Discord всякий раз, когда создаётся пул Uniswap. Мы покажем два подхода: более удобный — использовать событие PoolCreated как триггер для нашего Web3 Action (см. функции, события и триггеры и документацию Uniswap).

План

  • Добавьте контракты в свой проект Tenderly — UniswapV3ContractFactory. Он уже верифицирован и присутствует в Tenderly, поэтому вам просто нужно добавить его в свой проект.
  • Сохраните URL webhook Discord в Secrets вашего Web3 Action.
  • Создайте Web3 Action, который будет реагировать на событие [PoolCreated](<https://developers.uniswap.org/protocol/reference/core/interfaces/IUniswapV3Factory#poolcreated>).

1: Добавьте контракты Uniswap в ваш проект Tenderly

Web3 Actions требуют, чтобы смарт-контракты, используемые для задания триггеров, были верифицированы в Tenderly. Web3 Actions требуют, чтобы смарт-контракты, используемые для задания триггеров, были верифицированы в Tenderly. Поскольку мы используем существующие контракты, вы можете найти их через строку поиска, введя имя или адрес контракта. Как вариант, перейдите на UniswapV2ContractFactory и нажмите «Add to Project». Сделайте то же для UniswapV2Pair.

2: Создайте Web3 Action для события PoolCreated

Ниже вы найдёте код нашего Web3 Action, а также объяснение, как он работает:
example.ts


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

    const eventLog = await getPoolCreatedEvent(txEvent);

    const tokensData = await getTokensData(eventLog.token0, eventLog.token1);
    console.log('Received Tokens Data: ', tokensData);

    await notifyDiscord(`${tokensData.token0.name} ↔️ ${tokensData.token1.name}`, context);
  } catch (error) {
    console.error(error);
  }
};

const getPoolCreatedEvent = async (txEvent: TransactionEvent) => {
  const ifc = new ethers.utils.Interface(UniswapV3FactoryAbi);
  const poolCreatedTopic = ifc.getEventTopic('PoolCreated');

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

  if (poolCreatedEventLog == undefined) {
    throw Error('PoolCreatedEvent missing');
  }
  return ifc.decodeEventLog(
    'PoolCreated',
    poolCreatedEventLog.data,
    poolCreatedEventLog.topics,
  ) as unknown as UniswapPool;
};

const getTokensData = async (token0: string, token1: string) => {
  const tokenFields = `id, name, symbol, totalValueLocked, totalSupply, derivedETH`;
  const theGraphQuery = `
{
	token0: token(id:"${token0.toLowerCase()}"){${tokenFields}},
	token1: token(id:"${token1.toLowerCase()}"){${tokenFields}}
}`;

  const UNISWAP_THE_GRAPH = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3';
  const resp = await axios.post(UNISWAP_THE_GRAPH, {
    query: theGraphQuery,
  });

  return resp.data.data as unknown as {
    token0: UniwswapToken;
    token1: UniwswapToken;
  };
};

const notifyDiscord = async (text: string, context: Context) => {
  console.log('Sending to Discord:', `🐥 ${text}`);
  const webhookLink = await context.secrets.get('discord.uniswapChannelWebhook');
  await axios.post(
    webhookLink,
    {
      content: `🐥 ${text}`,
    },
    {
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
    },
  );
};

type UniswapPool = {
  token0: string;
  token1: string;
  fee: number;
  tickSpacing: number;
  pool: number;
};

type UniwswapToken = {
  id: string;
  name: string;
  symbol: string;
  tokenValueLocked: number;
  totalSupply: number;
  derivedEth: number;
};
Получите токены во вновь созданном пуле. Чтобы получить информацию о двух токенах в новом пуле, нам нужно поискать по логам транзакции в функции getEventLog. Мы используем Ethers и ABI контракта: UniswapV3FactoryAbi. Перейдите к контракту UniswapV3Factory в Tenderly, нажмите «View ABI» и скопируйте его в локальный файл в каталоге actions. По сути, мы ищем запись лога в topic PoolCreate. Сначала берём ссылку на этот topic (poolCreatedTopic = ifc.getEventTopic("PoolCreated")), а затем фильтруем сами логи (tx.logs), пока не найдём запись, ссылающуюся на этот topic. Наконец, декодируем найденный лог (poolCreatedEventLog) с помощью Ethers. Это то самое событие, которое мы ищем, и оно содержит адреса токенов: token0 и token1. Получите данные о токенах через Graph API Uniswap на TheGraph. Uniswap предоставляет доступ к своим данным через TheGraph. Мы используем функцию getTokensData, чтобы обратиться к TheGraph и получить несколько полей для ID token0 и token1 — тех, что присутствуют в событии PoolCreated из первого шага. Уведомите сообщество Discord. Собрав всю необходимую информацию, мы готовы отправить сообщение в наш канал Discord. С функцией notifyDiscord нам нужно получить URL webhook из secrets. Мы используем Axios, чтобы сделать HTTP-запрос POST и запустить webhook, передав ему содержимое.

3: Настройте ваш Web3 Action

В файле tenderly.yaml нам нужно указать, что мы хотим запускать uniswapActions:onPoolCreatedEventEmitted при генерации события контрактом UniswapV3Factory (по адресу 0xd849b2af570ffa3033973ea11be6e01b7ba661d9) в транзакции, заминированной в Mainnet (network 1).
example.yaml
account_id: ''
actions:
  YOUR_USERNAME/YOUR_PROJECT_SLUG:
    runtime: v1
    sources: actions
    specs:
      uniswapNewPool:
        description: Runs when a new swap is made on Uniswap
        function: uniswapActions:onPoolCreatedEventEmitted
        trigger:
          type: transaction
          transaction:
            status:
              - mined
            filters:
              - network: 1
                eventEmitted:
                  contract:
                    address: 0xd849b2af570ffa3033973ea11be6e01b7ba661d9
                  name: PoolCreated
project_slug: ''

4: Сохраните URL webhook Discord в Secrets

Прежде чем запускать наш Web3 Action в Tenderly, необходимо задать значение для webhook Discord. В Tenderly Dashboard перейдите на страницу Actions вашего проекта и нажмите Secrets. Добавьте новый секрет с именем discord.uniswapChannelWebhook и вставьте webhook вашего канала Discord.

5: Разверните и опробуйте ваш Web3 Action

Разверните ваш Web3 Action, выполнив эту команду:
example.yaml
tenderly actions deploy
Чтобы проверить, работает ли ваш Web3 Action, перейдите на страницу Actions в Tenderly Dashboard. Выберите ваш action и нажмите Manual Trigger. Вот и всё! Надеемся, вы получили сообщение в своём канале Discord 🎉