Развертывание смарт-контракта в тестовой сети с помощью Taquito

На прошлом уроке мы изучили базовые и продвинутые типы данных языка программирования LIGO. Этих знаний хватит, чтобы написать код простого смарт-контракта. Но чтобы другие пользователи могли взаимодействовать с контрактом, его нужно разместить в блокчейне.

Для развертывания смарт-контрактов в блокчейне разработчики используют специальные инструменты. В этом уроке мы установим библиотеку Taquito и с ее помощью опубликуем контракт в тестовой сети Tezos.

Установка и пробный запуск Taquito

Taquito — это TypeScript-библиотека, которая упрощает взаимодействие с блокчейном Tezos и смарт-контрактами. Например, так выглядит развертывание контракта вручную через консоль клиента Tezos:

tezos-client originate contract <название контракта>
  for <user> transferring <количество tez> from <from_user>
  running <путь к файлу с кодом>
  --init '<состояние хранилища storage>'
  --burn-cap <максимальная комиссия>

Скрипт Taquito для развертывания контракта выглядит проще:

try {
  const op = await tezos.contract.originate({
    //код смарт-контракта
    code: `{
          `,
    //значение хранилища
    init: ``,
  });

Также с помощью Taquito можно отправлять токены, проверять балансы адресов и разработать простой веб-интерфейс для смарт-контракта.

Установка Taquito

Для работы Taquito нужна платформа nodeJS. Скачайте ее с официального сайта и установите на свой компьютер.

Затем установите пакетный менеджер yarn. Он загрузит Taquito и автоматически пропишет его в системе.

Для этого откройте консоль и выполните команду:

sudo npm install --global yarn

1

Средства для работы Taquito готовы, осталось протестировать ее работу. Откройте Visual Studio Code, создайте новую папку taq-test, а в ней — два файла с названиями app.ts и main.ts.

В app.ts мы будем записывать функции Taquito и основной код, а в main.ts — параметры и вызовы функций. Такой подход позволяет упростить структуру кода и не вызывать функции до их объявления.

2

Откройте терминал в VS Code и с помощью команды cd перейдите в папку taq-test. Затем добавьте в папку библиотеку Taquito:

cd ~/Documents/taq-test

yarn add @taquito/taquito

3

Если вы правильно установили NodeJS и yarn, терминал выдаст длинный список установленных файлов.

Откройте файл app.ts и добавьте в него следующий код:

import { TezosToolkit } from '@taquito/taquito'
export class App {
  public async main() {}
}

В файл main.ts добавьте:

import { App } from './app'

new App().main()

Не забудьте сохранить файлы с помощью Ctrl+S или ⌘+S, иначе среда исполнения будет считать их пустыми.

Расширение .ts указывает на то, что в файлах содержится код TypeScript. Для его работы нужно установить библиотеку TypeScript. Для этого в терминале VS Code выполните:

sudo npm install typescript --save-dev

4

При успешной установке терминал выдаст 3-4 предупреждения об отсутствии лицензии и описания проекта. Если в списке больше пунктов — вы забыли добавить sudo в начале команды или не находитесь в папке taq-test.

Последний шаг — выполните скрипт main.ts в виртуальном узле. Для этого введите в терминале команду:

npx ts-node main.ts

5

Терминал отчитается о запуске npx одной строкой. Это значит, что npx удачно импортировал в приложение Taquito. Так как главная функция app.ts пустая, npx сразу ее завершил. Другой результат при выполнении этого шага означает, что вы допустили ошибку во время установки TypeScript или Taquito.

При работе над смарт-контрактами вам нужно будет каждый раз устанавливать Taquito и TypeScript в папку проекта. В отличие от JavaScript, их нельзя установить глобально.

Получаем данные из блокчейна Tezos через Taquito

С помощью Taquito можно считывать балансы адресов в сети Tezos, отправлять токены и публиковать смарт-контракты. Для этого не нужно поднимать собственный узел Tezos: Taquito подключается к публичным узлам через RPC (Remote Procedure Call или вызов удаленных процедур).

Давайте подключимся к мейннету и запросим баланс адреса одного из бейкеров — валидаторов сети Tezos. Для этого в файле app.ts включим библиотеку TezosToolkit и подготовим константу для RPC-ссылки публичного узла Tezos:

import { TezosToolkit } from '@taquito/taquito'
export class App {
  //объявляем приватный модификатор tezos типа TezosToolkit
  private tezos: TezosToolkit

  //объявляем конструктор rpcUrl, который будет передавать адрес публичного узла Tezos в TezosToolkit
  constructor(rpcUrl: string) {
    this.tezos = new TezosToolkit(rpcUrl)
  }

  public async main() {}
}

В файл main.ts пропишем RPC-ссылку на публичный узел Tezos Mainnet и передадим ее классу App:

import { App } from './app'
//объявляем константу с адресом узла
const RPC_URL = 'https://mainnet.smartpy.io'
//запускаем App, который передает главной функции адрес узла
new App(RPC_URL).main()

При исполнении команды npx ts-node main.ts Taquito получит ссылку на узел, но ничего с ней не сделает, потому что в app.ts нет методов для взаимодействия с сетью. Добавим в app.ts метод для получения баланса адреса:

import { TezosToolkit } from '@taquito/taquito'
export class App {
  private tezos: TezosToolkit

  constructor(rpcUrl: string) {
    this.tezos = new TezosToolkit(rpcUrl)
  }

  //объявляем метод getBalance с входящим параметром address
  public getBalance(address: string): void {
    //Taquito отправляет узлу запрос баланса указанного адреса. Если узел исполнил запрос, скрипт выводит полученное значение в консоль. Если произошла ошибка — выдает «Address not found»
    this.tezos.rpc
      .getBalance(address)
      .then((balance) => console.log(balance))
      .catch((e) => console.log('Address not found'))
  }

  public async main() {}
}

Добавим адрес бейкера и вызовем метод getBalance в скрипте main.ts:

import { App } from './app'

const RPC_URL = 'https://mainnet.smartpy.io'
//объявляем константу с адресом бейкера Everstake
const ADDRESS = 'tz1aRoaRhSpRYvFdyvgWLL6TGyRoGF51wDjM'
//запускаем App, передаем ему ссылку на узел, вызываем метод getBalance и передаем ему адрес
new App(RPC_URL).getBalance(ADDRESS)

Запустим скрипт с помощью команды npx ts-node main.ts.

6

Taquito вернет количество свободных tez на адресе Everstake.

7

Полученный баланс совпадает с данными обозревателя tzStats.

Создаем аккаунт в тестовой сети Tezos с помощью Taquito

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

Чтобы не тратить настоящие tez, мы подключимся к Tezos Testnet. Сначала нужно получить адрес и тестовые tez на кране tzalpha. Перейдите по ссылке, подтвердите, что вы не робот, и нажмите кнопку Get Testnet tez.

8

Кран tzalpha создаст новый адрес и выдаст данные аккаунта: мнемоническую фразу, закрытый ключ и публичный адрес.

9

Данные аккаунта нужно сохранить в отдельном файле. Создайте в папке taq-test файл acc.json и вставьте в него код из tzalpha.

10

Теперь можно добавить в проект модуль signer. В VS Code откройте терминал, перейдите в папку taq-test и выполните команду:

yarn add @taquito/signer

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

11

Создадим новый файл tx.ts и добавим в него модуль signer:

import { TezosToolkit } from '@taquito/taquito'
//импортируем inMemorySigner. Он сохранит приватный ключ в оперативной памяти и будет подписывать им транзакции
import { InMemorySigner } from '@taquito/signer'
//объявляем константу acc, которая направит скрипт к файлу acc.json
const acc = require('./acc.json')
export class Tx {
  private tezos: TezosToolkit
  rpcUrl: string
  constructor(rpcUrl: string) {
    this.tezos = new TezosToolkit(rpcUrl)
    this.rpcUrl = rpcUrl

    //объявляем параметры с помощью метода fromFundraiser: почту, пароль и мнемоническую фразу, из которой можно получить приватный ключ
    this.tezos.setSignerProvider(InMemorySigner.fromFundraiser(acc.email, acc.password, acc.mnemonic.join(' ')))
  }
  //получаем публичный и приватный ключи и активируем аккаунт
  public async activateAccount() {
    const { pkh, secret } = acc
    try {
      const operation = await this.tezos.tz.activate(pkh, secret)
      await operation.confirmation()
    } catch (e) {
      console.log(e)
    }
  }
  public async main() {}
}

В файле main.ts вызываем метод activateAccount():

import { App } from './app'
//импортируем Tx.ts
import { Tx } from './tx'
//меняем RPC-ссылку из мейннета на тестовую сеть. Не пугайтесь smartpy в ссылке — это просто адрес сервера
const RPC_URL = 'https://florencenet.smartpy.io/'
const ADDRESS = 'tz1aRoaRhSpRYvFdyvgWLL6TGyRoGF51wDjM'
//вызываем функцию Tx, передаем ей ссылку на тестовую сеть и просим активировать аккаунт
new Tx(RPC_URL).activateAccount()

Осталось исполнить main.ts в терминале:

npx ts-node main.ts

12

Если тестовая сеть активирует аккаунт, консоль отчитается о запуске npx без сообщений об ошибках. Теперь можете проверить активацию в обозревателе тестовых сетей.

Откройте acc.json и скопируйте публичный ключ из поля «pkh». Перейдите на florence.tzstats и найдите свой аккаунт по публичному ключу. На балансе должно быть около сотни тестовых tez, а в истории транзакций — запись об активации.

13

Мы настроили Taquito и активировали тестовый аккаунт. Для работы в мейннете нужно будет сделать то же самое: создать файл .json с данными аккаунта и подключить его с помощью signer.

Публикуем контракт в тестнете

На прошлом уроке мы запускали смарт-контракт с помощью контейнера Docker и команды dry-run. Виртуальная машина с контрактом сразу закрывалась, и при этом не использовала хранилище storage.

Настоящие смарт-контракты токенов и децентрализованных приложений постоянно используют storage для хранения данных пользователей. Мы подробнее расскажем об этом в следующем уроке. А пока опубликуем контракт в тестовой сети и посмотрим, как storage сохраняет информацию.

Перейдите в VS Code и создайте файл deploy.js. Вставьте в него следующий код:

import { TezosToolkit } from '@taquito/taquito';
import { importKey } from '@taquito/signer';

const provider = 'https://florencenet.smartpy.io/';

async function deploy() {
 const tezos = new TezosToolkit(provider);
 await importKey(
   tezos,
   "", //почта
   "", //пароль
   [
     "", //мнемоника
   ].join(' '),
   ""//приватный ключ
 );

deploy();

Впишите в пустые места данные аккаунта: почту, пароль, слова мнемонической фразы и приватный ключ из файла acc.json.

14

После данных аккаунта добавьте код контракта. Мы будем использовать модифицированный контракт из первого урока:

​​function main (const num : int; const store : int) is
 ((nil : list (operation)),  store + num)

Этот смарт-контракт получает от пользователя параметр num типа int и суммирует его с числом в хранилище.

Скомпилируйте код контракта на язык Michelson. Чтобы не запускать Docker лишний раз, воспользуйтесь онлайн средой разработки.

Вставьте код в редактор, в выпадающем списке выберите пункт Compile Contract и нажмите кнопку Run. Компилятор выдаст код контракта на языке Michelson.

15

Вернитесь к файлу deploy.ts и после данных аккаунта вставьте следующий код:

try {
  const op = await tezos.contract.originate({
    //код смарт-контракта
    code: `{ parameter int ;
      storage int ;
      code { UNPAIR ; ADD ; NIL operation ; PAIR } }
          `,
    //значение хранилища
    init: `0`,
  });

Теперь опишите процесс развертывания контракта. Финальный код будет выглядеть так:

import { TezosToolkit } from '@taquito/taquito'
import { importKey } from '@taquito/signer'

const provider = 'https://florencenet.smartpy.io/'

async function deploy() {
  const tezos = new TezosToolkit(provider)
  await importKey(
    tezos,
    'hoqfgsoy.qyisbhtk@tezos.example.org', //почта
    'ZnnZLS0v6O', //пароль
    [
      'able', //мнемоника
      'public',
      'usual',
      'hello',
      'october',
      'owner',
      'essence',
      'old',
      'author',
      'original',
      'various',
      'gossip',
      'core',
      'high',
      'hire',
    ].join(' '),
    '2bed8dc244ee43a1e737096c4723263c269049d8' //приватный ключ
  )

  try {
    const op = await tezos.contract.originate({
      //код смарт-контракта
      code: `{ parameter int ;
       storage int ;
       code { UNPAIR ; ADD ; NIL operation ; PAIR } }
           `,
      //значение хранилища
      init: `0`,
    })

    //начало развертывания
    console.log('Awaiting confirmation...')
    const contract = await op.contract()
    //отчет о развертывании: количество использованного газа, значение хранилища
    console.log('Gas Used', op.consumedGas)
    console.log('Storage', await contract.storage())
    //хеш операции, по которому можно найти контракт в блокчейн-обозревателе
    console.log('Operation hash:', op.hash)
  } catch (ex) {
    console.error(ex)
  }
}

deploy()

Откройте консоль и выполните команду npx ts-node deploy.ts. Терминал выдаст отчет о публикации контракта.

16

Скопируйте хеш операции и найдите транзакцию развертывания контракта в обозревателе florence.tzstats. В ней будет указан адрес смарт-контракта в тестовой сети.

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

Создайте новый файл call.ts и вставьте в него код:

import { TezosToolkit } from '@taquito/taquito'
import { InMemorySigner } from '@taquito/signer'
const acc = require('./acc.json')
export class Call {
  private tezos: TezosToolkit
  rpcUrl: string

  constructor(rpcUrl: string) {
    this.tezos = new TezosToolkit(rpcUrl)
    this.rpcUrl = rpcUrl

    //объявляем параметры с помощью метода fromFundraiser: почту, пароль и мнемоническую фразу, из которой можно получить приватный ключ
    this.tezos.setSignerProvider(InMemorySigner.fromFundraiser(acc.email, acc.password, acc.mnemonic.join(' ')))
  }

  public add(add: number, contract: string) {
    this.tezos.contract
      .at(contract) //обращаемся к контракту, чтобы получить его точки входа
      .then((contract) => {
        console.log(`Adding ${add} to storage...`)
        //обращаемся к главной функции. В отличие от синтаксиса ligo, главная точка входа называется не main, а default
        return contract.methods.default(add).send()
      })
      .then((op) => {
        console.log(`Awaiting for ${op.hash} to be confirmed...`)
        return op.confirmation(1).then(() => op.hash) //ждем одно подтверждение сети, чтобы быстрее получить результат
      })
      .then((hash) => console.log(`Call done}`)) //успешный вызов
      .catch((error) => console.log(`Error: ${JSON.stringify(error, null, 2)}`))
  }
}

Теперь сделайте отдельный скрипт для вызова call.ts, передачи адреса контракта и значения аргумента. Для этого создайте файл maincall.ts и скопируйте в него код:

import { Call } from './call'

const RPC_URL = 'https://florencenet.smartpy.io/'
const CONTRACT = '' //адрес опубликованного контракта
const ADD = 5 //число, которое получит главная функция. Можете изменить его на другое
new Call(RPC_URL).add(ADD, CONTRACT)

Присвойте константе CONTRACT адрес опубликованного тестового контракта. Откройте консоль и выполните команду:

npx ts-node maincall.ts

Когда терминал выдаст сообщение о завершении операции, перейдите по ссылке или найдите свой контракт по адресу. Как видите, значение хранилища изменилось.

17

Подбиваем итоги

Taquito — это инструмент для работы с блокчейном Tezos через код на JavaScript и TypeScript. С помощью Taquito разработчики обращаются к точкам входа смарт-контрактов и к сети Tezos, как к обычным JS-методам. Также Taquito позволяет хранить данные нескольких аккаунтов в разных файлах и выносить часто используемый код в отдельные модули.

Библиотека Taquito нужна для разработки DApp на Tezos. Разработчикам проще интегрировать в веб-приложение JavaScript, чем переводить запросы пользователей в консольные команды клиента Tezos. В следующих уроках мы также будем использовать Taquito для создания взаимозаменяемого токена и NFT.

  • Автор — Павел Скоропляс
  • Продюсер — Светлана Коваль
  • Стили — Дмитрий Бойко
  • Иллюстрации — Кшиштоф Шпак
  • Верстка — Зара Аракелян
  • Разработка — Александр Пупко
  • Руководитель — Влад Лихута