Рустам Хасаншин

Рустам Хасаншин

Вызов метода смарт-контракта в Go

Рассмотрим метод работы с блокчейном в Go. Предположим у вас есть контракт в эфириум блокчейне, и вы хотите вызвать его метод.

Основной библиотекой для работы с такими контрактами является https://pkg.go.dev/github.com/ethereum/go-ethereum/ethclient. Возьмем к примеру контракт токена моих знакомых запущенный на блокчейне бинанса (также основан на эфире) и попробуем вызвать метод для получения общего количества токенов.

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

import (
    ...
    "github.com/ethereum/go-ethereum/ethclient"
    ...
)

client, err := ethclient.Dial("https://bsc-dataseed.binance.org/")
if err != nil {
    fmt.Println(err)
    return
}

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

В джаваскрипте вызвать метод контракта достаточно просто — вы можете загрузить ABI контракта и все сделает библиотека web3. В Go придется повозиться.

Для того чтобы вызвать тот или иной метод в Go, нужно этот метод написать. Логично. И с контрактами работает таже логика. Но переписывать все методы это долго и муторно, и люди придумали генерацию кода этого контракта из его ABI. Для этого нам нужна библиотека abigen. На мак можно поставить через Homebrew.

$ brew tap ethereum/ethereum
$ brew install ethereum

Для проверки успешности установки, попробуйте вывести версию abigen.

$ abigen -v
> abigen version 1.10.25-stable

Abigen успешно установлен, пора сгенерировать Go код. Перейдем на страницу контракта в bscscan — https://bscscan.com/address/0xE0b1EF69BC4AB4173989C1190f0d77A813f3B726, и скачаем его ABI.

Contract -> Code -> Contract ABI -> Export ABI -> RAW/Text Format.

Сохраним файл в директории программы и назовем token.abi. Далее вызываем abigen. В параметре --abi указываем наш файл, в параметре --pkg указываем название пакета для нашего кода, в параметре --type имя структуры к которой будут привязаны все методы, в параметре --out - имя Go файла.

$ abigen --abi=token.abi --pkg=main --type=token --out=token.go

В директории должен появится файл token.go с кодом который уже можно использовать. Но сначала давайте создадим экземпляр адреса из нашего строкового адреса контракта. Он нам понадобится чтобы создать потом экземляр токена. Делается это с помощью библиотеки github.com/ethereum/go-ethereum/common.

import (
    ...
    "github.com/ethereum/go-ethereum/common"
    ...
)

address := common.HexToAddress("0xE0b1EF69BC4AB4173989C1190f0d77A813f3B726")

Теперь создаем собственно сам экземляр токена.

token, err := NewToken(address, client)
if err != nil {
    fmt.Println(err)
    return
}

И пробуем выполнить метод для получения общего количества токенов — TotalSupply. Но этот метод требует передачи параметров специального типа &bind.CallOpts{}. Параметры мы передавать не хотим, но пустое значение передать надо. А сам тип можно загрузить из библиотеки github.com/ethereum/go-ethereum/accounts/abi/bind.

import (
    ...
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    ...
)

totalSupply, err := token.TotalSupply(&bind.CallOpts{})
if err != nil {
    fmt.Println(err)
    return
}

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

decimals, err := token.Decimals(&bind.CallOpts{})
if err != nil {
    fmt.Println(err)
    return
}

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

s := totalSupply.String()
fmt.Println(s[:len(s)-int(decimals)] + "." + s[len(s)-int(decimals):])

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