Введение

Доброе утро!
В этой статье мы поговорим о том, что такое транзакция и как ее отправить в блокчейнах, совместимых с Ethereum. Чуть ниже вы можете ознакомиться с содержанием и выбрать интересующий вас раздел.

Для кого статья?

Если вы владеете javascript и знаете основные сущности ethers.js (Контракт, Поставщик) или читаете мою статью Эфир js — Основытак что смело читайте дальше. В противном случае материал может быть затруднен для понимания.

Содержание

  • операция объекта

  • Отправка операции

  • Покупка токенов ERC-721

  • Обмен токенов ERC-20

операция сущности

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

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

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

В целом транзакция Ethereum состоит из нескольких ключевых элементов:

  • С — адрес кошелька, с которого происходит транзакция

  • Для — адрес кошелька, куда отправляется транзакция

  • ценить — количество ETH, которое отправляется в транзакциях.

  • Цены на газ — сумма ETH, которую отправитель платит за транзакцию, которая будет обработана узлами сети

  • номер блока — номер блока, в который будет включена транзакция (если номер блока не указан, транзакция попадет в первый свободный блок)

  • подписант — цифровая подпись, подтверждающая авторство транзакции отправителем.

После обработки транзакции появляются дополнительные поля:

  • нарезать — уникальный идентификатор

  • Положение дел — статус транзакции (ожидание, сбой, успех)

Цифровая подпись создается с использованием закрытого и открытого ключей отправителя. Он зашифрован и может быть проверен с помощью открытого ключа, чтобы убедиться, что отправитель был тем, за кого себя выдавал.

После обработки транзакция становится неизменной и добавляется в блокчейн. Каждый последующий блок содержит новые транзакции, которые также обрабатываются узлами сети.

Отправка операции

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

Обычно бывает 2 случая:

  • Метод смарт-контракта принимает только ETH или не требует оплаты, и требуется только плата за обработку транзакции.

  • Метод смарт-контракта имеет гибкую структуру и может опционально принимать токены определенного стандарта, ETH и/или ETH в качестве оплаты за обработку транзакции.

В любом случае вам сначала нужно будет войти в систему или создать кошелек, чтобы получить доступ к цифровой подписи отправителя.

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

import { ethers } from 'ethers'

let web3 = null
let provider = null

async connect () {
    // проверяем есть ли кошелек
    if (window.ethereum) { 
      // сохраняем экземпляр для дальнейшей инициализации web3Provider
      provider = window.ethereum 
      try {
        // получаем учетные записи кошелька
        await window.ethereum.request({ method: 'eth_requestAccounts' })
      } catch (e) {
        console.error(e)
        if (e.code === 4001) return
      }
    } else if (window.web3) {
      provider = window.web3.currentProvider
    }
    if (provider) {
        // инициалзируем web3 provider
        web3 = new ethers.providers.Web3Provider(provider, 'any')
      }
  }

В примере кода мы инициализируем экземпляры провайдера, необходимые для взаимодействия со смарт-контрактами.

В рассмотренных ниже случаях будем рассматривать метод соединять называется, а значит провайдеры уже известны.

Покупка токенов ERC-721

Отличным примером смарт-контракта, который ожидает только получение ETH, является смарт-контракт токена ERC-721.

ЧИТАТЬ   Бренд одежды Lacoste предлагает покупателям систему вознаграждений NFT: подробности

ERC-721 представляет собой стандарт токенов, реализующий определенные методы.

В нашем случае будем работать по методу мята, который принимает ETH в качестве оплаты, выпускает токен ERC-721 и отправляет его на адрес отправителя.

Как видите метод монетный двор он выпускает токен, поэтому термин купить не совсем корректен и использовался для простоты восприятия.

Рассмотрим как разместить ERC-721 на примере коллекции NFT франкенпанки В сети Эфириум.

Реализация функции монетный дворкоторый отправит ETH и выдаст отправителю токен ERC-721:

import { ethers } from "ethers"
// адрес смарт-контракта
const CONTRACT_ADDRESS = "0x1FEC856e25F757FeD06eB90548B0224E91095738"
// описание методов смарт-контракта
const ABI = [
  {
      // описание аргументов метода (кол-во выпускаемых ERC-721)
      "inputs": [
        {
          "internalType": "uint256",
          "name": "numToMint",
          "type": "uint256"
        }
      ],
      "name": "getCost",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      // view означает что выполняет чтение
      "stateMutability": "view",
      "type": "function"
    },
    {
        // описание аргументов метода (кол-во выпускаемых ERC-721)
        "inputs": [
          {
            "internalType": "uint256",
            "name": "_quantity",
            "type": "uint256"
          }
        ],
        "name": "publicMint",
        "outputs": [],
        // payable означает что выполняет запись и ожидает получить ETH
        "stateMutability": "payable",
        "type": "function"
      }
]
// кол-во выпускаемых токенов
const AMOUNT = 1

async function mint() {
      // получение цифровой подписи
      const signer = web3.getSigner()
      // инициализация контракта для чтения
      const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)
      try {
        // получения стоимости выпуска токена
        const price = await contract.getCost(AMOUNT)

        // данные для переопределения параметров транзакцийй
        const txOverrides = {
          value: price,
        }

        // получение комиссии, необходимой для подтверждения транзакции
        const gasPrice = await contract
          .estimateGas.mint(AMOUNT, txOverrides)

        // инициализация контракта для записи
        const contractWithSigner = await contract.connect(signer)

        // выпуск токена
        const tx = await contractWithSigner.mint(AMOUNT, {
          ...txOverrides,
          gasLimit: parseInt(gasPrice * 1.1)
        })

        // ожидание обработки транзакции
        const txReceipt = await txResponse.wait();
        console.log(`Transaction hash: ${txReceipt.hash}`);


      } catch (e) {
        console.log(e)
      }
    }
  }

Рассмотрим код алгоритма выше:

Мы инициализируем данные, необходимые для инициализации экземпляра смарт-контракта (КОНТРАКТ_АДРЕС, АБИ) вместе с аргументом, необходимым для вызова метода (КОЛИЧЕСТВО).

Функция инициализации монетный двор

Получить цифровую подпись отправителя операции (подписант) с помощью эфирного провайдера web3.

Инициализировать экземпляр контракта для чтения данных.

Вызов метода смарт-контракта получить стоимость Для получить стоимость выпуска токена. Аргументом является количество выпущенных токенов. Этот метод нестандартен для токенов ERC-721 и реализован непосредственно в самом смарт-контракте. франкенпанки для комфорта.

Инициализировать данные для переопределения транзакции. В разделе «Суть транзакции» мы разобрали, из чего состоит неподтвержденная транзакция. Эфиры позволяет не указывать никаких параметров и использует заранее подготовленные значения, и мы можем просто вызывать методы смарт-контракта, передавая только аргументы самого метода. Однако в нашем случае нам нужно переопределить количество ETH, которое будет отправлено на смарт-контракт. Поэтому мы указываем в ценитьполученный цена.

Доступ к свойству цитатаГаз пример договор библиотеки эфиры и подражать выдача токена для получения комиссия, необходимая для подтверждения транзакции. Это свойство доступно во всех экземплярах. договор библиотеки эфиры. Затем мы можем вызвать метод смарт-контракта для записи (монетный двор). В качестве аргументов мы передаем список параметров, которые будут переданы в метод смарт-контракта. И необязательный последний аргумент передает данные для переопределения транзакции.

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

Вызов метода смарт-контракта мята, ВОЗ выпустит токен и отправит его отправителю транзакции. Или другими словами, он запишет владельца токена в блокчейн. Вызов аналогичен описанному расчету комиссии, но также добавлен повышенный лимит на комиссию Газлимит. В этом не было необходимости, но за счет более высокой комиссии транзакция будет обработана быстрее. Этот метод вернет нам экземпляр транзакции эфиры.

Для полученного экземпляра транзакции эфиры вызвать метод ждать. Это возвращается обещать, незавершенная транзакция. Из этого промиса после его разрешения можно также получить расширенный экземпляр транзакции с его id (нарезать).

ЧИТАТЬ   Вулкан Шивелуч на Камчатке выбросил пепел на высоту до 15 км. Небо покрыто темными тучами

Обмен токенов ERC-20

Известным примером смарт-контракта, который предполагает получение разных токенов, является смарт-контракт обмена токенами.

ERC-20 также означает стандарт смарт-контрактов с определенными методами.
В нашем случае нас интересует метод утвердить, который разрешает смарт-контракту использовать токены кошелька.

Существует множество смарт-контрактов, которые позволяют торговать ERC-20. В этой статье мы обсудим обмен через смарт-контракт Унисвоп. Мы также будем использовать uniswap sdk, который содержит утилиты для более удобного написания кода.

Uniswap — один из ведущих протоколов торговли криптовалютой, доступных на основных блокчейнах, совместимых с Ethereum.

Во-первых, давайте инициализируем данные, которые нам нужны:

const ERC_20_ABI = [
  {
        // описание аргументов метода (
        // адрес смарт-контракта которому выдается разрешение
        // кол-во токенов разрешенных для взымания
        // )
        "inputs": [
            {
                "internalType": "address",
                "name": "spender",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "value",
                "type": "uint256"
            }
        ],
        "name": "approve",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        // nonpayable метод не взымает ETH
        "stateMutability": "nonpayable",
        "type": "function"
    },
]

const UNISWAP_ROUTER_ABI = [
  {
      // описание аргументов метода (
      // кол-во токенов на обмен
      // кол-во токенов взамен
      // [
      // адрес токена на обмен, 
      // адрес токена, получаемого взамен
      // ]
      // адрес получателя 
      // максимальное время ожидания
      // )
      "inputs":[
         {
            "internalType":"uint256",
            "name":"amountIn",
            "type":"uint256"
         },
         {
            "internalType":"uint256",
            "name":"amountOutMin",
            "type":"uint256"
         },
         {
            "internalType":"address[]",
            "name":"path",
            "type":"address[]"
         },
         {
            "internalType":"address",
            "name":"to",
            "type":"address"
         },
         {
            "internalType":"uint256",
            "name":"deadline",
            "type":"uint256"
         }
      ],
      "name":"swapExactTokensForTokens",
      "outputs":[
         {
            "internalType":"uint256[]",
            "name":"amounts",
            "type":"uint256[]"
         }
      ],
      // nonpayable означает что не ожидает получить ETH,
      // в данном случае т.к. запись происходит
      // в смарт-контрактах ERC-20 токенов
      "stateMutability":"nonpayable",
      "type":"function"
   },
]

// адресс смарт-контракта uniswap для обменя
const UNISWAP_ROUTER_ADDRESS = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"

// адреса токенов для обмена
const USDT_CONTRACT_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
const DAI_CONTRACT_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"

Мы внедряем функцию утверждения для авторизации использования токенов в смарт-контракте Uniswap.

import { ethers } from 'ethers'

function approve() {
  // получение цифровой подписи
  const signer = web3.getSigner()
  // инициализация контракта токена USDT
  const tokenContract = new ethers.Contract(USDT_CONTRACT_ADDRESS, ERC_20_ABI, signer);
  
  try {
    // выдаем разрешение на использование токена USDT смарт-контракту Uniswap
    const tx = await tokenContract.approve(
      UNISWAP_ROUTER_ADDRESS,
      // указываем в разрешении макс допустимое значение,
      // чтобы не просить у отправителя разрешение каждый раз
      ethers.constants.MaxUint256
    );
    // ожидание обработки транзакции
    const txReceipt = await txResponse.wait();
    console.log(`Transaction hash: ${txReceipt.hash}`);


  } catch (e) {
    throw new Error(e)
  }
}

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

После того, как авторизация была успешно получена, обмен может быть осуществлен. Для этого реализуем функцию менять, кто будет обменивать токен USDT на ПОМОГАТЬ В сети эфирный.

import { ethers } "ethers"
import {
  ChainId,
  Token,
  TokenAmount,
  Pair,
  Route,
  Trade,
  TradeType,
  Percent } from "@uniswap/sdk"

// токен для обменя
const TOKEN_IN = new Token(ChainId.MAINNET, USDT_CONTRACT_ADDRESS, 18)
// токен получаемые взамен
const TOKEN_OUT = new Token(ChainId.MAINNET, DAI_CONTRACT_ADDRESS, 18)

// кол-во токена для обменя
const AMOUNT_IN = ethers.utils.parseUnits('1', 18)
// преобразование кол-ва в вид необходимый смарт-контракту
const TOKEN_AMOUNT_IN = new TokenAmount(TOKEN_IN, AMOUNT_IN.toString())

function swap () {
  // получение цифровой подписи
  const signer = web3.getSigner()

  // инициализация контракта для записи
  const contractWithSigner = new ethers.Contract(
    UNISWAP_ROUTER_ADDRESS,
    UNISWAP_ROUTER_ABI,
    signer
  )

  // получения данных пары необходимых для создания маршрута обмена
  const pair = await Pair.fetchData(TOKEN_AMOUNT_IN.token, TOKEN_OUT, provider)
  // инициализация маршрута обмена
  const route = new Route([pair], TOKEN_AMOUNT_IN.token)
  // инициализация класса для расчета кол-ва получаемого взамен токена
  const trade = new Trade(route, TOKEN_AMOUNT_IN, TradeType.EXACT_INPUT)
  // инициализация процента проскальзывания
  // (насколько кол-во получаемого токена может отличаться)
  const slippageTolerance = new Percent('1', '100') // 1%
  // расчет кол-ва получаемого токена
  const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw.toString()
  // инициализация максимального времени ожидания обмена
  const deadline = Math.floor(Date.now() / 1000) + 60 * 10 // 10 minute
  try {
    // вызов метода для обмена токенов, инициализация транзакции
    const txReceipt = await contractWithSigner.swapExactTokensForTokens(
      amountIn.toString(),
      amountOutMin,
      [tokenIn.address, tokenOut.address],
      signer.getAddress(),
      deadline,
    )

    // ожидание обработки транзакции
    const txReceipt = await txResponse.wait()

    console.log(`Transaction hash: ${txReceipt.hash}`)

    } catch (e) {
        throw new Error(e)
    }
    
  }

В этом примере для смарт-контракта uniswap требуются вычислительные операции для построения маршрута обмена. Самый простой маршрут может быть USDT-DAI. В комплекс могут быть включены промежуточные обмены, если прямого предложения не найдено. Например: USDT-USDC-BUSD-DAI.

ЧИТАТЬ   Почему так сложно сказать "Я люблю тебя" - Burning Hut

В остальном алгоритм отправки транзакции аналогичен методу утверждения для токенов ERC-20 и аналогичен методу чеканки для токенов ERC-721.

Бонус: ускорение транзакций и отслеживание откатов

На этом этапе я хотел бы выразить свое уважение и порадоваться тому, что у вас есть все знания, необходимые для отправки и отслеживания транзакций в приложении javascript. Думаю, эта бонусная тема будет вам интересна, так как я не смог найти ее в документации. Но на практике это часто ставит отправителя в ступор.

Дело в том, что после отправки транзакции мы получаем объект, содержащий хэш транзакции, который необходим для его поиска в проводнике. Как своего рода чек-квитанция. Поэтому очень часто в децентрализованных приложениях есть отдельный раздел с историей транзакций, который содержит ссылки для быстрой навигации в проводнике:

const link = `https://etherscan.io/tx/${transactionHash}`

Однако иногда возникает ситуация, когда отправитель ускоряет или вовсе отменяет транзакцию. В этом случае метод ожидания вернет ошибку. Если по откату еще все понятно, то по ускорению транзакция все равно будет выполняться. Но дело в том, что ускорение отменяет текущую транзакцию и создает новую, с повышенной комиссией за обработку. Поэтому его идентификатор тоже меняется (нарезать).

Реализуем функцию обрабатыватьTxErrors, кто будет заниматься этими делами:

function handleTxErrors (err, tx) {
  const newTx = { ...tx, ...err.replacement }
  switch (err.reason) {
    case 'cancelled':
      if (err.cancelled) {
        onsole.log(`Transaction cancelled: ${newTx.hash}`)
      }
    break
    case 'repriced':
      if (!err.cancelled) {
        console.log(`Transaction repriced: ${newTx.hash}`)
      break
    default:
      throw new Error(err)
    }
}

Функция принимает ошибку и старую транзакцию в качестве аргументов, обрабатывает причину ошибки и устанавливает новый идентификатор транзакции (нарезать). Таким образом, заменив этот обработчик в блоке catch, вы можете обеспечить еще более приятный пользовательский интерфейс.

Заключение

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

Source

От admin