Здравствуйте! В этой статье мы поговорим о том, что такое транзакция и как отправлять ее в Ethereum совместимых блокчейнах. Чуть ниже вы можете ознакомиться с содержанием и выбрать интересующий вас раздел. Если вы хорошо владеете javascript и знакомы с основными сущностями ethers.js (Contract, Provider) или прочли мою статью Ethers js - Основы, то можете смело читать дальше. В другом случае материал может быть сложен для восприятия. Сущность транзакция Отправка транзакции Покупка ERC-721 токена Обмен ERC-20 токенов Транзакция - это запись в блокчейне, которая обычно содержит информацию о переводе криптовалюты или любых других данных от одного участника сети к другому. Другими словами методы , которые осуществляют запись каких-либо данных в блокчейне. Транзакции используются для передачи активов, подтверждения прав собственности, голосования, взаимодействия со смарт-контрактами и других операций в блокчейне. Когда транзакция отправляется в блокчейн, она должна быть подтверждена участниками сети. В блокчейнах совместимых с Ethereum, транзакции подтверждаются узлами сети. В общем виде транзакция Ethereum состоит из нескольких ключевых элементов: from - адрес кошелька, откуда идет транзакция to - адрес кошелька, куда направляется транзакция value - количество ETH, которые отправляются в транзакции. gasPrice - количество ETH, которое отправитель платит за обработку транзакции узлами сети blockNumber - номер блока, в котором будет включена транзакция (если номер блока не указан, то транзакция попадет в первый свободный блок) signer - цифровая подпись, которая подтверждает авторство транзакции отправителем. После обработки транзакции появляются дополнительные поля: hash - уникальный идентификатор status - статус транзакция (pending, failed, success) Цифровая подпись создается с помощью приватного и публичного ключей отправителя. Она шифруется и может быть проверена с использованием публичного ключа, чтобы убедиться в том, что отправитель был именно тем, за кого себя выдавал.Однажды обработанная, транзакция становится неизменяемой и добавляется в блокчейн. Каждый последующий блок содержит новые транзакции, которые также проходят процесс обработки узлами сети. Процесс создания и отправки транзакции может  отличаться в зависимости от предназначения и аргументов, необходимых для записи данных.Условно можно выделить 2 случая:  Метод смарт-контракта принимает только ETH или не требует оплаты и необходима только плата за обработку транзакции Метод смарт-контракта имеет гибкую структуру, опционально может принимать токены определенного стандарта, ETH и/или ETH в качестве оплаты за обработку транзакции В обоих случаях сначала потребуется подключить или создать кошелек, чтобы иметь доступ к цифровой подписи отправителя. Поэтому для начала объявим переменные для провайдеров и реализуем функцию connect: В примере кода мы инициализируем экземпляры провайдера, необходимые для взаимодействия со смарт-контрактами. В случаях рассмотренных ниже мы будем считать метод connect вызванным, а следовательно провайдеры уже известными.  Ярким примером смарт-контракта, который ожидает получить только ETH является смарт-контракта ERC-721 токена.  ERC-721 обозначает стандарт токена который реализует определенные методы. В нашем случае мы будем работать с методом mint, который принимает ETH в качестве оплаты, выпускает ERC-721 токен и отправляет его на адрес отправителя.Как вы могли заметить метод mint именно выпускает токен, поэтому термин покупка не совсем корректен и использовался для простоты восприятия.Рассмотрим как выпустить ERC-721 на примере NFT-коллекции FrankenPunks в сети Ethereum. Реализуем функцию mint, которая и будет отправлять ETH и выпускать отправителю ERC-721 токен: Рассмотрим алгоритм кода выше: Инициализируем данные необходимые для инициализации экземпляра смарт-контракта (CONTRACT_ADDRESS, ABI),  а также аргумент необходимый для вызова метода (AMOUNT). Инициализируем функцию mint. Получаем цифровую подпись отправителя транзакции (signer) с помощью web3 провайдера ethers. Инициализируем экземпляр contract для чтения данных. Вызываем метод смарт-контракта getCost для получения стоимости выпуска токена. Аргументом передаем количество выпускаемых токенов. Данный метод не является стандартным для ERC-721 токенов и реализован непосредственно в самом смарт-контракте FrankenPunks для удобства. Инициализируем данные для переопределения транзакции. В разделе "Cущность транзакции" мы разбирали из чего состоит неподтвержденная транзакция. Ethers позволяет не указывать параметры и использует заранее подготовленные значения и мы можем просто вызывать методы смарт-контракт, передавая туда лишь аргументы самого метода. Однако в нашем случае нам нужно переопределить количество ETH, которые будут отправлены смарт-контракту. Поэтому указываем в value, полученный price. Обращаемся к свойству estimateGas экземпляра contract библиотеки ethers и имитируем  выпуск токена, чтобы получить комиссию необходимую для подтверждения транзакции. Данное свойство доступно у всех экземпляров contract библиотеки ethers. Далее мы можем вызываем метод смарт-контракта для записи (mint). Аргументами передаем список параметров, которые будут переданы методу смарт-контракта. И последним опциональным аргументом передаем данные для переопределения транзакции. Подключаемся к смарт-контракту с помощью метода connect, который доступен у всех экземпляров contract библиотеки ethers. С аргументом в виде цифровой подписи, которую ethers в дальнейшем сможет использовать для отправки транзакции. Данный метод возвращает новый экземпляр контракта для записи, который хранит в себе цифровую подпись отправителя.Вызываем метод смарт-контракта mint, который выпустит токен и отправит его отправителю транзакции. Или другими словами запишет в блокчейн владельца токена. Вызов аналогичен описанному расчету комиссии, но также добавили повышенный лимит на комиссию gasLimit. Делать это было не обязательно, но благодаря большей комиссии транзакция обработается быстрее. Данный метод вернет нам экземпляр транзакции ethers. У полученного экземпляра транзакции ethers вызываем метод wait. Который возвращает promise, ожидающий обработки транзакции. Из этого промиса после его разрешения можно также получить расширенный экземпляр транзакции с ее идентификатором (hash). Всем известным примером смарт-контракта, который ожидает получить различные токены является смарт-контракт для обмена токенов. ERC-20 также обозначает стандарт смарт-контракта с определенными методами.В нашем случае нас интересует метод approve, который выдает разрешение смарт-контракту на использование токенов кошелька. Существует множество смарт-контрактов, позволяющих производить обмен ERC-20. В данной статье мы рассмотрим обмен посредством смарт-контракта Uniswap. Мы также воспользуемся uniswap sdk, который содержит утилиты для более комфортного написания кода. Uniswap - это один из ведущих протоколов торговли криптовалютой, доступный на основных Ethereum совместимых блокчейнах.  Для начала инициализируем данные, которые нам понадобятся: Реализуем функцию approve для выдачи разрешения на использования токенов смарт-контракту Uniswap. В данном случае для отправки транзакции нам не нужно дополнительно читать данные с смарт-контракта, поэтому экземпляр контракта инициализируется с аргументом signer вместо provider. Благодаря этому в момент вызова метода у нас автоматически происходит и подпись транзакции. После того как разрешение успешно получено можно производить обмен. Для этого реализуем функцию swap, которая будет обменивать токен USDT на DAI в сети ethereum. В данном примере пристутствуют вычислительные операции, которые необходимы смарт-контракту uniswap для построения маршрута обмена. Самый простой маршрут может быть USDT-DAI.   Сложный может включать промежуточные обмены, если прямое предложение не найдено. Например: USDT-USDC-BUSD-DAI. В остальном алгоритм отправки транзакции такой же, как и у метода approve у ERC-20 токенов и схож с методом mint у ERC-721 токенов. На этом моменте, хотелось бы выразить свое уважение и порадовать, что у вас есть все знания, чтобы отправлять и отслеживать транзакции в javascript приложения. Думаю эта бонусная тема тоже будет для вас интересна, так как в документации ее я не нашел. Но на практике она часто ставит отправителя в ступор. Дело в том что после отправки транзакции мы получаем объект содержащий hash транзакции, который нужен, чтобы искать ее в explorer. В качестве своего рода чековой квитанции. Поэтому достаточно часто в децентрализованных приложениях есть отдельный раздел с историей транзакций, который содержит ссылки для быстрой навигации в explorer: Однако иногда возникает ситуация, когда отправитель ускоряет или вовсе отменяет транзакцию. В таком случае метод wait выкинет ошибку. Если в случае отмены еще все понятно, то в случае ускорения транзакция все равно будет выполнена. Но дело в том, что ускорение отменяет текущую транзакцию и создает новую, с увеличенной комиссией за обработку. Следовательно меняется и ее идентификатор (hash). Давай реализуем функцию handleTxErrors, которая будет обрабатывать эти случаи: Функция принимает в качестве аргументов ошибку и старую транзакцию, обрабатывает причину ошибки и устанавливает новый идентификатор транзакции (hash). Таким образом, подставив этот обработчик в блок catch можно обеспечить еще более приятный пользовательский опыт. В заключение, мы рассмотрели наиболее популярные кейсы работы с транзакциями в  Ethereum совместимых блокчейнах, включая отправку, покупку и обмен токенами, а также бонусную тему про отслеживание ускорения и отмену транзакций. Надеюсь, что данная статья была полезной для вас и помогла улучшить ваше понимание работы с транзакциями в Ethereum совместимых блокчейнах. Если у вас есть какие-то дополнительные вопросы или правки, не стесняйтесь писать их в комментариях.