Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module github.com/hyperledger/firefly-evmconnect
module github.com/davecrighton/firefly-evmconnect

go 1.23.0

require (
github.com/gorilla/mux v1.8.1
github.com/hashicorp/golang-lru v1.0.2
github.com/hyperledger/firefly-common v1.5.9
github.com/hyperledger/firefly-signer v1.1.21
github.com/hyperledger/firefly-signer v1.1.23-0.20260302140017-4d4b9e7da065
github.com/hyperledger/firefly-transaction-manager v1.4.4
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
Expand All @@ -24,7 +24,7 @@ require (
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/aidarkhanov/nanoid v1.0.8 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
Expand Down Expand Up @@ -99,3 +99,6 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/hyperledger/firefly-signer => github.com/davecrighton/firefly-signer v1.1.23-0.20260302140017-4d4b9e7da065
replace github.com/hyperlegder/evmconnect => github.com/davecrighton/firefly-evmconnect v1.4.3-0.20260302140737-2a995804706b
16 changes: 9 additions & 7 deletions internal/ethereum/estimate_gas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package ethereum

import (
"context"
"encoding/hex"
"encoding/json"
"testing"
Expand All @@ -32,6 +31,12 @@ import (
"github.com/stretchr/testify/mock"
)

var testDefaultError = &abi.Entry{
Type: abi.Error,
Name: "Error",
Inputs: abi.ParameterArray{{Type: "string"}},
}

const sampleGasEstimate = `{
"ffcapi": {
"version": "v1.0.0",
Expand Down Expand Up @@ -150,7 +155,7 @@ func TestGasEstimateFailRevertReasonInData(t *testing.T) {
ctx, c, mRPC, done := newTestConnector(t)
defer done()

errData, err := defaultError.EncodeCallDataValues([]string{"this reason"})
errData, err := testDefaultError.EncodeCallDataValues([]string{"this reason"})
assert.NoError(t, err)
mRPC.On("CallRPC", mock.Anything, mock.Anything, "eth_estimateGas",
mock.MatchedBy(func(tx *ethsigner.Transaction) bool {
Expand Down Expand Up @@ -205,7 +210,7 @@ func TestGasEstimateFailThenRevertDataFromCall(t *testing.T) {
ctx, c, mRPC, done := newTestConnector(t)
defer done()

errData, err := defaultError.EncodeCallDataValues([]string{"this reason"})
errData, err := testDefaultError.EncodeCallDataValues([]string{"this reason"})
assert.NoError(t, err)
mRPC.On("CallRPC", mock.Anything, mock.Anything, "eth_estimateGas",
mock.MatchedBy(func(tx *ethsigner.Transaction) bool {
Expand Down Expand Up @@ -261,7 +266,7 @@ func TestGasEstimateFailCustomErrorCannotParse(t *testing.T) {
ctx, c, mRPC, done := newTestConnector(t)
defer done()

errData, err := defaultError.EncodeCallDataValues([]string{"this reason"})
errData, err := testDefaultError.EncodeCallDataValues([]string{"this reason"})
assert.NoError(t, err)
mRPC.On("CallRPC", mock.Anything, mock.Anything, "eth_estimateGas",
mock.MatchedBy(func(tx *ethsigner.Transaction) bool {
Expand All @@ -281,6 +286,3 @@ func TestGasEstimateFailCustomErrorCannotParse(t *testing.T) {

}

func TestFormatErrorComponentBadCV(t *testing.T) {
assert.Equal(t, "?", formatErrorComponent(context.Background(), &abi.ComponentValue{}))
}
73 changes: 6 additions & 67 deletions internal/ethereum/exec_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
package ethereum

import (
"bytes"
"context"
"encoding/json"
"fmt"

"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-common/pkg/i18n"
Expand All @@ -34,21 +32,6 @@ import (
"github.com/hyperledger/firefly-transaction-manager/pkg/ffcapi"
)

var (
// See https://docs.soliditylang.org/en/v0.8.14/control-structures.html#revert
// There default error for `revert("some error")` is a function Error(string)
defaultError = &abi.Entry{
Type: abi.Error,
Name: "Error",
Inputs: abi.ParameterArray{
{
Type: "string",
},
},
}
defaultErrorID = defaultError.FunctionSelectorBytes()
)

func (c *ethConnector) QueryInvoke(ctx context.Context, req *ffcapi.QueryInvokeRequest) (*ffcapi.QueryInvokeResponse, ffcapi.ErrorReason, error) {
// Parse the input JSON data, to build the call data
callData, method, err := c.prepareCallData(ctx, &req.TransactionInput)
Expand Down Expand Up @@ -164,59 +147,15 @@ func processRevertReason(ctx context.Context, outputData ethtypes.HexBytes0xPref
// result in a multiple of 32 bytes) and has exactly 4 extra bytes for a function
// signature
if len(outputData)%32 == 4 {
signature := outputData[0:4]
if bytes.Equal(signature, defaultErrorID) {
errorInfo, err := defaultError.DecodeCallDataCtx(ctx, outputData)
if err == nil && len(errorInfo.Children) == 1 {
if strError, ok := errorInfo.Children[0].Value.(string); ok {
return strError
}
}
log.L(ctx).Warnf("Invalid revert data: %s", outputData)
} else if len(errorAbis) > 0 {
// check if the signature matches any of the declared custom error definitions
for _, e := range errorAbis {
idBytes := e.FunctionSelectorBytes()
if bytes.Equal(signature, idBytes) {
err := formatCustomError(ctx, e, outputData)
if err == "" {
log.L(ctx).Warnf("Invalid revert data: %s", outputData)
break
}
return err
}
}
var errors abi.ABI
for _, e := range errorAbis {
errors = append(errors, e)
}
if result, ok := errors.UnwrapErrorStringCtx(ctx, outputData); ok {
return result
}
// we call this "transient error" because it signals to the caller of the case
// that the raw revert data is returned, then it gets thrown away. so no need to translate
log.L(ctx).Debugf("Directly returning revert reason: %s", outputData)
return outputData.String()
}
return ""
}

func formatCustomError(ctx context.Context, e *abi.Entry, outputData ethtypes.HexBytes0xPrefix) string {
errorInfo, err := e.DecodeCallDataCtx(ctx, outputData)
if err == nil {
strError := fmt.Sprintf("%s(", e.Name)
for i, child := range errorInfo.Children {
strError += formatErrorComponent(ctx, child)
if i < len(errorInfo.Children)-1 {
strError += ", "
}
}
strError += ")"
return strError
}
return ""
}

func formatErrorComponent(ctx context.Context, child *abi.ComponentValue) string {
value, err := child.JSON()
if err != nil {
// if this part of the error structure failed to parse, simply append "?"
log.L(ctx).Warnf("Failed to parse component value in error: %+v", child)
return "?"
}
return string(value)
}
Loading
Loading