Merge pull request 'hex parser + test' (#141) from tron-148 into main
Some checks failed
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline failed
ci/woodpecker/push/frontend Pipeline failed

Reviewed-on: #141
This commit was merged in pull request #141.
This commit is contained in:
2025-12-24 02:53:38 +00:00
5 changed files with 73 additions and 28 deletions

View File

@@ -10,7 +10,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/shopspring/decimal"
"github.com/tech/sendico/gateway/chain/internal/service/gateway/rpcclient"
@@ -199,17 +198,11 @@ func erc20Decimals(ctx context.Context, client *rpc.Client, token common.Address
if err := client.CallContext(ctx, &hexResp, "eth_call", call, "latest"); err != nil {
return 0, merrors.Internal("decimals call failed: " + err.Error())
}
val, err := hexutil.DecodeBig(hexResp)
val, err := shared.DecodeHexUint8(hexResp)
if err != nil {
return 0, merrors.Internal("decimals decode failed: " + err.Error())
}
if val == nil {
return 0, merrors.Internal("decimals decode failed: empty response")
}
if val.BitLen() > 8 {
return 0, merrors.Internal("decimals decode failed: value out of range")
}
return uint8(val.Uint64()), nil
return val, nil
}
func toBaseUnits(amount string, decimals uint8) (*big.Int, error) {

View File

@@ -8,9 +8,9 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/shopspring/decimal"
"github.com/tech/sendico/gateway/chain/internal/service/gateway/shared"
"github.com/tech/sendico/gateway/chain/storage/model"
"github.com/tech/sendico/pkg/merrors"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
@@ -104,17 +104,11 @@ func readDecimals(ctx context.Context, client *rpc.Client, token string) (uint8,
if err := client.CallContext(ctx, &hexResp, "eth_call", call, "latest"); err != nil {
return 0, merrors.Internal("decimals call failed: " + err.Error())
}
val, err := hexutil.DecodeBig(hexResp)
val, err := shared.DecodeHexUint8(hexResp)
if err != nil {
return 0, merrors.Internal("decimals decode failed: " + err.Error())
}
if val == nil {
return 0, merrors.Internal("decimals decode failed: empty response")
}
if val.BitLen() > 8 {
return 0, merrors.Internal("decimals decode failed: value out of range")
}
return uint8(val.Uint64()), nil
return val, nil
}
func readBalanceOf(ctx context.Context, client *rpc.Client, token string, wallet string) (*big.Int, error) {
@@ -132,7 +126,7 @@ func readBalanceOf(ctx context.Context, client *rpc.Client, token string, wallet
if err := client.CallContext(ctx, &hexResp, "eth_call", call, "latest"); err != nil {
return nil, merrors.Internal("balanceOf call failed: " + err.Error())
}
bigVal, err := hexutil.DecodeBig(hexResp)
bigVal, err := shared.DecodeHexBig(hexResp)
if err != nil {
return nil, merrors.Internal("balanceOf decode failed: " + err.Error())
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/shopspring/decimal"
@@ -315,17 +314,11 @@ func erc20Decimals(ctx context.Context, client *rpc.Client, token common.Address
if err := client.CallContext(ctx, &hexResp, "eth_call", call, "latest"); err != nil {
return 0, executorInternal("decimals call failed", err)
}
val, err := hexutil.DecodeBig(hexResp)
val, err := shared.DecodeHexUint8(hexResp)
if err != nil {
return 0, executorInternal("decimals decode failed", err)
}
if val == nil {
return 0, executorInternal("decimals decode failed", errors.New("empty response"))
}
if val.BitLen() > 8 {
return 0, executorInternal("decimals decode failed", errors.New("value out of range"))
}
return uint8(val.Uint64()), nil
return val, nil
}
func toBaseUnits(amount string, decimals uint8) (*big.Int, error) {

View File

@@ -0,0 +1,49 @@
package shared
import (
"errors"
"math/big"
"strings"
)
var (
errHexEmpty = errors.New("hex value is empty")
errHexInvalid = errors.New("invalid hex number")
errHexOutOfRange = errors.New("hex number out of range")
)
// DecodeHexBig parses a hex string that may include leading zero digits.
func DecodeHexBig(input string) (*big.Int, error) {
trimmed := strings.TrimSpace(input)
if trimmed == "" {
return nil, errHexEmpty
}
noPrefix := strings.TrimPrefix(trimmed, "0x")
if noPrefix == "" {
return nil, errHexEmpty
}
value := strings.TrimLeft(noPrefix, "0")
if value == "" {
return big.NewInt(0), nil
}
val := new(big.Int)
if _, ok := val.SetString(value, 16); !ok {
return nil, errHexInvalid
}
return val, nil
}
// DecodeHexUint8 parses a hex string into uint8, allowing leading zeros.
func DecodeHexUint8(input string) (uint8, error) {
val, err := DecodeHexBig(input)
if err != nil {
return 0, err
}
if val == nil {
return 0, errHexInvalid
}
if val.BitLen() > 8 {
return 0, errHexOutOfRange
}
return uint8(val.Uint64()), nil
}

View File

@@ -0,0 +1,16 @@
package shared
import "testing"
func TestDecodeHexUint8_LeadingZeros(t *testing.T) {
t.Parallel()
const resp = "0x0000000000000000000000000000000000000000000000000000000000000006"
val, err := DecodeHexUint8(resp)
if err != nil {
t.Fatalf("DecodeHexUint8 error: %v", err)
}
if val != 6 {
t.Fatalf("DecodeHexUint8 value = %d, want 6", val)
}
}