Начал работать впервые со смарт контрактами на golang и мне не нравится то что у меня получается.
Основная проблема это обработка пользовательских ошибок из solidity, использую abigen и потом чтобы мне обработать на каждый запрос ошибку приходится отдельно используя ethclient через CallContract предварительно делать запрос к контракту и собирать для него msg, а для msg надо собрать calldata и т.д. а потом парсить ошибки
Пример говнокода:
callOpts := &bind.CallOpts{
From: sender,
Context: context.Background(),
}
contractABI, err := abi.JSON(strings.NewReader(contract.ContractABI))
if err != nil {
return fmt.Errorf("failed to parse ABI: %v", err)
}
callData, err := contractABI.Pack("registerProvider", provider)
if err != nil {
return fmt.Errorf("failed to pack call data: %v", err)
}
msg := ethereum.CallMsg{
From: callOpts.From,
To: &s.contractAddr,
Gas: auth.GasLimit,
GasPrice: auth.GasPrice,
Value: auth.Value,
Data: callData,
}
res, err := s.client.CallContract(context.Background(), msg, nil)
if err != nil {
log.Printf("CallContract failed with error: %v", err)
log.Printf("Revert data (hex): %s", hex.EncodeToString(res))
log.Printf("CallMsg details: From=%s, To=%s, Value=%s, Gas=%d", msg.From.Hex(), msg.To.Hex(), msg.Value.String(), msg.Gas)
if strings.Contains(err.Error(), "execution reverted") {
reason, parseErr := parseRevertReason(err, res)
if parseErr != nil {
return fmt.Errorf("execution reverted: unable to parse reason: %v (raw data: %s)", parseErr, hex.EncodeToString(res))
}
return fmt.Errorf("execution reverted: %s", reason)
}
return fmt.Errorf("call failed: %v", err)
}
tx, err := s.contract.RegisterProvider(auth, provider)
if err != nil {
if tx != nil {
receipt, err := bind.WaitMined(context.Background(), s.client, tx)
if err != nil {
return fmt.Errorf("failed to get receipt: %v", err)
}
if receipt.Status == 0 {
reason, parseErr := parseRevertReason(err, nil)
if parseErr != nil {
return fmt.Errorf("transaction reverted: unable to parse reason: %v", parseErr)
}
return fmt.Errorf("transaction reverted: %s", reason)
}
}
return fmt.Errorf("transaction failed: %v", err)
}
log.Printf("Transaction sent: %s", tx.Hash().Hex())
func parseRevertReason(err error, revertData []byte) (string, error) {
if len(revertData) == 0 {
if err.Error() == "execution reverted" {
return "execution reverted", nil
}
if errStr := err.Error(); strings.Contains(errStr, "revert") {
parts := strings.Split(errStr, "revert")
if len(parts) > 1 {
return strings.TrimSpace(parts[1]), nil
}
}
return "no revert data provided", nil
}
contractABI, err := abi.JSON(strings.NewReader(contract.ContractABI))
if err != nil {
return "", fmt.Errorf("failed to parse ABI: %v", err)
}
if len(revertData) < 4 {
return fmt.Sprintf("short revert data: %s", hex.EncodeToString(revertData)), nil
}
methodID := revertData[:4]
data := revertData[4:]
if hex.EncodeToString(methodID) == "08c379a0" {
reason, err := contractABI.Unpack("Error", data)
if err != nil {
return "", fmt.Errorf("failed to unpack revert reason: %v", err)
}
if len(reason) > 0 {
if str, ok := reason[0].(string); ok {
return str, nil
}
}
return "unknown revert reason", nil
}
for _, errDef := range contractABI.Errors {
errID := errDef.ID[:4]
if hex.EncodeToString(errID) == hex.EncodeToString(methodID) {
unpacked, err := errDef.Unpack(data)
if err != nil {
return "", fmt.Errorf("failed to unpack error %s: %v", errDef.Name, err)
}
return fmt.Sprintf("%s: %v", errDef.Name, unpacked), nil
}
}
return fmt.Sprintf("unknown revert reason (methodID: %s, data: %s)", hex.EncodeToString(methodID), hex.EncodeToString(data)), nil
}
В итоге получается куча кода который только и занимается тем что подготавливает запрос и потом обрабатывает ошибки. Это нормально если у меня было бы 2-3 функции которые я дергаю из контракта, но у меня их десятки и это уже пытка.
не понимаю почему abigen не делает это из коробки, чтобы можно было просто вызывать конкретную функцию смарт контракта и получать нормально ошибку без конструкций огромных.
Может я просто не умею гуглить или действительно нет какого то готового инструмента для работы со смарт контрактами?
Кто и как работает со смарт контрактами на golang?