Вызов метода смарт-контракта в 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):])
На этом собстевенно и все. Мы научились подключаться к блокчейну и выполнять метод контракта с возвращаемым значением. Исходный код можно найти на гитхабе.