Задать вопрос

Как работать правильно со смарт контрактами в golang?

Начал работать впервые со смарт контрактами на 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?
  • Вопрос задан
  • 20 просмотров
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы