From 78d359a411ad1224c13694d9c5f0803d68fe924c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 2 Jul 2026 10:29:04 -0500 Subject: [PATCH 1/8] codec: create codec module --- codec/bcs_converter.go | 241 ++++++ .../codec => codec}/bcs_converter_test.go | 0 codec/decoder.go | 632 ++++++++++++++ {relayer/codec => codec}/decoder_test.go | 0 codec/encoder.go | 368 +++++++++ codec/go.mod | 155 ++++ codec/go.sum | 539 ++++++++++++ .../loop/connection_load_test.go | 0 codec/loop/loop_reader.go | 191 +++++ .../loop/loop_reader_test.go | 0 codec/type_converters.go | 775 ++++++++++++++++++ .../codec => codec}/type_converters_test.go | 0 codec/types.go | 149 ++++ 13 files changed, 3050 insertions(+) create mode 100644 codec/bcs_converter.go rename {relayer/codec => codec}/bcs_converter_test.go (100%) create mode 100644 codec/decoder.go rename {relayer/codec => codec}/decoder_test.go (100%) create mode 100644 codec/encoder.go create mode 100644 codec/go.mod create mode 100644 codec/go.sum rename {relayer/chainreader => codec}/loop/connection_load_test.go (100%) create mode 100644 codec/loop/loop_reader.go rename {relayer/chainreader => codec}/loop/loop_reader_test.go (100%) create mode 100644 codec/type_converters.go rename {relayer/codec => codec}/type_converters_test.go (100%) create mode 100644 codec/types.go diff --git a/codec/bcs_converter.go b/codec/bcs_converter.go new file mode 100644 index 000000000..6566fcd39 --- /dev/null +++ b/codec/bcs_converter.go @@ -0,0 +1,241 @@ +package codec + +import ( + "fmt" + "strconv" + + aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" +) + +// BCSPrimitiveHandler defines a function that reads a primitive type from a BCS deserializer +type BCSPrimitiveHandler func(*aptosBCS.Deserializer) (any, error) + +// BCSVectorHandler defines a function that reads a vector type from a BCS deserializer +type BCSVectorHandler func(*aptosBCS.Deserializer, uint64) (any, error) + +// BCSTypeConverter provides a registry-based approach to converting BCS types to JSON-compatible values +type BCSTypeConverter struct { + primitiveHandlers map[string]BCSPrimitiveHandler + vectorHandlers map[string]BCSVectorHandler +} + +// NewBCSTypeConverter creates a new BCS type converter with all standard Sui types registered +func NewBCSTypeConverter() *BCSTypeConverter { + c := &BCSTypeConverter{ + primitiveHandlers: make(map[string]BCSPrimitiveHandler), + vectorHandlers: make(map[string]BCSVectorHandler), + } + + // Register primitive type handlers + c.registerPrimitiveHandlers() + c.registerVectorHandlers() + + return c +} + +// registerPrimitiveHandlers registers all standard Sui primitive type handlers +func (c *BCSTypeConverter) registerPrimitiveHandlers() { + // U8 - return as-is + c.RegisterPrimitive("U8", func(d *aptosBCS.Deserializer) (any, error) { + return d.U8(), nil + }) + c.RegisterPrimitive("u8", func(d *aptosBCS.Deserializer) (any, error) { + return d.U8(), nil + }) + + // U16 - return as-is + c.RegisterPrimitive("U16", func(d *aptosBCS.Deserializer) (any, error) { + return d.U16(), nil + }) + c.RegisterPrimitive("u16", func(d *aptosBCS.Deserializer) (any, error) { + return d.U16(), nil + }) + + // U32 - return as-is + c.RegisterPrimitive("U32", func(d *aptosBCS.Deserializer) (any, error) { + return d.U32(), nil + }) + c.RegisterPrimitive("u32", func(d *aptosBCS.Deserializer) (any, error) { + return d.U32(), nil + }) + + // U64 - return as string for JSON compatibility + c.RegisterPrimitive("U64", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U64() + return strconv.FormatUint(value, base10), nil + }) + c.RegisterPrimitive("u64", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U64() + return strconv.FormatUint(value, base10), nil + }) + + // U128 - return as string for JSON compatibility + c.RegisterPrimitive("U128", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U128() + return value.String(), nil + }) + c.RegisterPrimitive("u128", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U128() + return value.String(), nil + }) + + // U256 - return as string for JSON compatibility + c.RegisterPrimitive("U256", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U256() + return value.String(), nil + }) + c.RegisterPrimitive("u256", func(d *aptosBCS.Deserializer) (any, error) { + value := d.U256() + return value.String(), nil + }) + + // Bool - return as-is + c.RegisterPrimitive("Bool", func(d *aptosBCS.Deserializer) (any, error) { + return d.Bool(), nil + }) + c.RegisterPrimitive("bool", func(d *aptosBCS.Deserializer) (any, error) { + return d.Bool(), nil + }) + + // Address - return as byte array + c.RegisterPrimitive("Address", func(d *aptosBCS.Deserializer) (any, error) { + addressBytesLen := 32 + return d.ReadFixedBytes(addressBytesLen), nil + }) + c.RegisterPrimitive("address", func(d *aptosBCS.Deserializer) (any, error) { + addressBytesLen := 32 + return d.ReadFixedBytes(addressBytesLen), nil + }) +} + +// registerVectorHandlers registers standard vector type handlers +func (c *BCSTypeConverter) registerVectorHandlers() { + // Vector - read as byte array + c.RegisterVector("U8", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + bytes := make([]byte, length) + for i := range length { + bytes[i] = d.U8() + } + return bytes, nil + }) + c.RegisterVector("u8", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + bytes := make([]byte, length) + for i := range length { + bytes[i] = d.U8() + } + return bytes, nil + }) + + // Vector - read as uint64 array + c.RegisterVector("U64", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + uint64s := make([]uint64, length) + for i := range length { + uint64s[i] = d.U64() + } + return uint64s, nil + }) + c.RegisterVector("u64", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + uint64s := make([]uint64, length) + for i := range length { + uint64s[i] = d.U64() + } + return uint64s, nil + }) + + // Vector
- read as address array + c.RegisterVector("Address", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + addresses := make([]any, length) + for i := range length { + addressBytesLen := 32 + addresses[i] = d.ReadFixedBytes(addressBytesLen) + } + return addresses, nil + }) + c.RegisterVector("address", func(d *aptosBCS.Deserializer, length uint64) (any, error) { + addresses := make([]any, length) + for i := range length { + addressBytesLen := 32 + addresses[i] = d.ReadFixedBytes(addressBytesLen) + } + return addresses, nil + }) +} + +// RegisterPrimitive registers a handler for a primitive type +func (c *BCSTypeConverter) RegisterPrimitive(typeName string, handler BCSPrimitiveHandler) { + c.primitiveHandlers[typeName] = handler +} + +// RegisterVector registers a handler for a vector type +func (c *BCSTypeConverter) RegisterVector(elementType string, handler BCSVectorHandler) { + c.vectorHandlers[elementType] = handler +} + +// DecodePrimitive decodes a primitive type using the registered handler +func (c *BCSTypeConverter) DecodePrimitive(deserializer *aptosBCS.Deserializer, primitiveType string) (any, error) { + handler, ok := c.primitiveHandlers[primitiveType] + if !ok { + return nil, fmt.Errorf("unsupported primitive type: %s", primitiveType) + } + return handler(deserializer) +} + +// DecodeVector decodes a vector type using the registered handler +func (c *BCSTypeConverter) DecodeVector(deserializer *aptosBCS.Deserializer, elementType string) (any, error) { + length := deserializer.Uleb128() + + handler, ok := c.vectorHandlers[elementType] + if !ok { + // Fall back to generic primitive vector handling + primitiveHandler, primitiveOk := c.primitiveHandlers[elementType] + if !primitiveOk { + return nil, fmt.Errorf("unsupported vector element type: %s", elementType) + } + + // Generic vector of primitives + result := make([]any, length) + for i := range length { + value, err := primitiveHandler(deserializer) + if err != nil { + return nil, fmt.Errorf("failed to decode vector element at index %d: %w", i, err) + } + result[i] = value + } + return result, nil + } + + return handler(deserializer, uint64(length)) +} + +// HasPrimitiveHandler checks if a primitive type handler is registered +func (c *BCSTypeConverter) HasPrimitiveHandler(typeName string) bool { + _, ok := c.primitiveHandlers[typeName] + return ok +} + +// HasVectorHandler checks if a vector type handler is registered +func (c *BCSTypeConverter) HasVectorHandler(elementType string) bool { + _, ok := c.vectorHandlers[elementType] + return ok +} + +// Global BCS converter instance for package-wide use (lazy initialization) +var defaultBCSConverter *BCSTypeConverter + +// getDefaultBCSConverter returns the global BCS converter, initializing it if necessary +func getDefaultBCSConverter() *BCSTypeConverter { + if defaultBCSConverter == nil { + defaultBCSConverter = NewBCSTypeConverter() + } + return defaultBCSConverter +} + +// DecodeBCSPrimitive decodes a BCS primitive type using the default converter +func DecodeBCSPrimitive(deserializer *aptosBCS.Deserializer, primitiveType string) (any, error) { + return getDefaultBCSConverter().DecodePrimitive(deserializer, primitiveType) +} + +// DecodeBCSVector decodes a BCS vector type using the default converter +func DecodeBCSVector(deserializer *aptosBCS.Deserializer, elementType string) (any, error) { + return getDefaultBCSConverter().DecodeVector(deserializer, elementType) +} diff --git a/relayer/codec/bcs_converter_test.go b/codec/bcs_converter_test.go similarity index 100% rename from relayer/codec/bcs_converter_test.go rename to codec/bcs_converter_test.go diff --git a/codec/decoder.go b/codec/decoder.go new file mode 100644 index 000000000..10f6cd09e --- /dev/null +++ b/codec/decoder.go @@ -0,0 +1,632 @@ +package codec + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math" + "reflect" + "strconv" + "strings" + + aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" + "github.com/block-vision/sui-go-sdk/models" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" +) + +const ( + // Bit and byte constants + byteSize = 8 + uint8Bits = 8 + uint64Bits = 64 + bits128 = 128 + bits256 = 256 + + // Number bases + base10 = 10 + base16 = 16 + base2 = 2 + + // Response parsing constants + maxByteValue = 255 + + // proofWireBytes is the fixed on-wire size of each merkle proof element. + proofWireBytes = 32 + + // tokenTransferMinWireBytes is the minimum BCS size of Any2SuiTokenTransfer: + // uleb128(0) source pool + 32 dest token + 4 dest gas + uleb128(0) extra data + 32 amount. + tokenTransferMinWireBytes = 70 +) + +// DecodeSuiJsonValue decodes Sui JSON response data into the provided target. +func DecodeSuiJsonValue(data any, target any) error { + return bind.DecodeJSONReturn(data, target) +} + +// ConvertBase64StringsToHex walks arbitrary JSON-like structures and converts any +// base64-encoded strings into 0x-prefixed hex strings, preserving []byte slices. +func ConvertBase64StringsToHex(data any) any { + switch v := data.(type) { + case nil: + return nil + case string: + // check if the string is entirely numeric + if _, err := strconv.ParseUint(v, 10, 64); err == nil { + return v + } + + decoded, err := base64.StdEncoding.DecodeString(v) + if err == nil && len(decoded) > 0 { + return "0x" + hex.EncodeToString(decoded) + } + return v + case json.RawMessage: + var inner any + if err := json.Unmarshal(v, &inner); err != nil { + return v + } + return ConvertBase64StringsToHex(inner) + case map[string]any: + result := make(map[string]any, len(v)) + for key, value := range v { + result[key] = ConvertBase64StringsToHex(value) + } + return result + case []any: + result := make([]any, len(v)) + for i, value := range v { + result[i] = ConvertBase64StringsToHex(value) + } + return result + default: + rv := reflect.ValueOf(data) + if rv.Kind() == reflect.Slice { + // Preserve []byte and other byte slices as-is + if rv.Type().Elem().Kind() == reflect.Uint8 { + return data + } + + result := make([]any, rv.Len()) + for i := 0; i < rv.Len(); i++ { + result[i] = ConvertBase64StringsToHex(rv.Index(i).Interface()) + } + return result + } + + if rv.Kind() == reflect.Map && rv.Type().Key().Kind() == reflect.String { + result := make(map[string]any, rv.Len()) + iter := rv.MapRange() + for iter.Next() { + result[iter.Key().String()] = ConvertBase64StringsToHex(iter.Value().Interface()) + } + return result + } + } + + return data +} + +// DecodeSuiStructToJSON decodes a Sui struct into a JSON object +// using the normalized struct and the result +func DecodeSuiStructToJSON(normalizedStructs map[string]any, identifier string, bcsDecoder *aptosBCS.Deserializer) (map[string]any, error) { + jsonResult := make(map[string]any) + + normalizedStruct, ok := normalizedStructs[identifier].(map[string]any) + if !ok { + return nil, fmt.Errorf("struct with identifier '%s' not found in normalized structs", identifier) + } + + fields, ok := normalizedStruct["fields"].([]any) + if !ok { + return nil, fmt.Errorf("fields not found for struct '%s'", identifier) + } + + for _, field := range fields { + fieldMap, ok := field.(map[string]any) + if !ok { + continue + } + + fieldName, ok := fieldMap["name"].(string) + if !ok { + continue + } + + fieldType := fieldMap["type"] + + // Handle different field types based on the new format + switch v := fieldType.(type) { + case string: + // Primitive types like "U64", "Bool", "Address" + value, err := getDefaultBCSConverter().DecodePrimitive(bcsDecoder, v) + if err != nil { + return nil, fmt.Errorf("failed to decode primitive field %s: %w", fieldName, err) + } + jsonResult[fieldName] = value + + case map[string]any: + if vectorType, exists := v["Vector"]; exists { + // Vector type + decodedVector, err := decodeVectorField(bcsDecoder, vectorType, normalizedStructs) + if err != nil { + return nil, fmt.Errorf("failed to decode vector field %s: %w", fieldName, err) + } + jsonResult[fieldName] = decodedVector + } else if structType, exists := v["Struct"]; exists { + // Struct type + structMap, ok := structType.(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid struct type for field %s", fieldName) + } + structName, ok := structMap["name"].(string) + if !ok { + return nil, fmt.Errorf("struct name not found for field %s", fieldName) + } + + // Special case for String struct - it's a primitive type in Sui + if structName == "String" { + jsonResult[fieldName] = bcsDecoder.ReadString() + } else { + inner, err := DecodeSuiStructToJSON(normalizedStructs, structName, bcsDecoder) + if err != nil { + return nil, fmt.Errorf("failed to decode struct field %s: %w", fieldName, err) + } + jsonResult[fieldName] = inner + } + } + } + } + + return jsonResult, nil +} + +func decodeVectorField(bcsDecoder *aptosBCS.Deserializer, vectorType any, normalizedStructs map[string]any) (any, error) { + // Read the length of the vector first + vectorLength := bcsDecoder.Uleb128() + + switch v := vectorType.(type) { + case string: + // Try to use the BCS converter for registered vector types + if getDefaultBCSConverter().HasVectorHandler(v) { + // Use the registered vector handler which will handle the length internally + // We need to "rewind" by putting the length back since the handler expects to read it + // Actually, the handler receives the length as a parameter, so we're good + handler, _ := getDefaultBCSConverter().vectorHandlers[v] + return handler(bcsDecoder, uint64(vectorLength)) + } + + // Fall back to generic primitive vector handling + if getDefaultBCSConverter().HasPrimitiveHandler(v) { + primitiveVector := make([]any, vectorLength) + for i := range vectorLength { + value, err := getDefaultBCSConverter().DecodePrimitive(bcsDecoder, v) + if err != nil { + return nil, fmt.Errorf("failed to decode primitive vector element at index %d: %w", i, err) + } + primitiveVector[i] = value + } + return primitiveVector, nil + } + + return nil, fmt.Errorf("unsupported vector element type: %s", v) + + case map[string]any: + if innerVectorType, exists := v["Vector"]; exists { + // This is vector> - recursively decode each inner vector + outerVector := make([]any, vectorLength) + for i := range vectorLength { + innerResult, err := decodeVectorField(bcsDecoder, innerVectorType, normalizedStructs) + if err != nil { + return nil, fmt.Errorf("failed to decode inner vector at index %d: %w", i, err) + } + outerVector[i] = innerResult + } + + return outerVector, nil + } else if structType, exists := v["Struct"]; exists { + // This is vector - decode each struct + structVector := make([]any, vectorLength) + structMap, ok := structType.(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid struct type in vector") + } + structName, ok := structMap["name"].(string) + if !ok { + return nil, fmt.Errorf("struct name not found in vector element") + } + + // this is a special case where strings are defined as a struct in Sui normalized module structs definition + if structName == "String" { + vecOfStrings := make([]any, vectorLength) + for i := range vectorLength { + vecOfStrings[i] = bcsDecoder.ReadString() + } + + return vecOfStrings, nil + } + + for i := range vectorLength { + structResult, err := DecodeSuiStructToJSON(normalizedStructs, structName, bcsDecoder) + if err != nil { + return nil, fmt.Errorf("failed to decode struct at index %d: %w", i, err) + } + structVector[i] = structResult + } + + return structVector, nil + } + } + + return nil, fmt.Errorf("unsupported vector type: %v", vectorType) +} + +func DecodeSuiPrimative(bcsDecoder *aptosBCS.Deserializer, primativeType string) (any, error) { + // Try to decode as primitive using the BCS converter registry + converter := getDefaultBCSConverter() + if converter.HasPrimitiveHandler(primativeType) { + return converter.DecodePrimitive(bcsDecoder, primativeType) + } + + // Handle vector types + if strings.HasPrefix(primativeType, "vector<") && strings.HasSuffix(primativeType, ">") { + innerType := strings.TrimSuffix(strings.TrimPrefix(primativeType, "vector<"), ">") + + // Handle simple vector types + if converter.HasVectorHandler(innerType) || converter.HasPrimitiveHandler(innerType) { + return decodeVectorField(bcsDecoder, innerType, nil) + } + + // Handle nested vector types (e.g., vector>) + if innerType == "vector" || innerType == "vector" { + return decodeVectorField(bcsDecoder, map[string]any{"Vector": "U8"}, nil) + } + } + + return nil, fmt.Errorf("unsupported BCS primitive type: %s", primativeType) +} + +// DecodeVectorOfStructs decodes a vector of structs from BCS bytes +// vectorType should be in format "vector<0xpackage::module::StructName>" +func DecodeVectorOfStructs(bcsDecoder *aptosBCS.Deserializer, vectorType string, normalizedStructs map[string]any) (any, error) { + // Check if it's actually a vector type + if !strings.HasPrefix(vectorType, "vector<") || !strings.HasSuffix(vectorType, ">") { + return nil, fmt.Errorf("not a vector type: %s", vectorType) + } + + // Extract inner type + innerType := strings.TrimSuffix(strings.TrimPrefix(vectorType, "vector<"), ">") + + // Check if inner type is a struct (has 3 parts when split by ::) + structParts := strings.Split(innerType, "::") + if len(structParts) != 3 { + return nil, fmt.Errorf("inner type is not a struct: %s", innerType) + } + + structName := structParts[2] + + // Create vector type definition compatible with decodeVectorField + vectorTypedef := map[string]any{ + "Struct": map[string]any{ + "name": structName, + }, + } + + return decodeVectorField(bcsDecoder, vectorTypedef, normalizedStructs) +} + +// numericToBytes converts a number to byte slice (little-endian) +// Used by type_converters.go +func numericToBytes(num uint64) []byte { + bytes := make([]byte, uint64Bits/uint8Bits) + for i := range uint8Bits { + bytes[i] = byte(num >> (i * uint8Bits)) + } + // Remove trailing zeros + for len(bytes) > 1 && bytes[len(bytes)-1] == 0 { + bytes = bytes[:len(bytes)-1] + } + + return bytes +} + +// AnySliceToBytes converts slice of interface{} to byte slice +func AnySliceToBytes(src []any) ([]byte, error) { + dst := make([]byte, len(src)) + for i, v := range src { + //nolint:exhaustive + switch x := v.(type) { + case uint8: + dst[i] = x + case int: + if x < 0 || x > maxByteValue { + return nil, fmt.Errorf("element %d: int %d out of byte range", i, x) + } + dst[i] = byte(x) + case uint: + if x > maxByteValue { + return nil, fmt.Errorf("element %d: uint %d out of byte range", i, x) + } + dst[i] = byte(x) + case float64: + if x > maxByteValue { + return nil, fmt.Errorf("element %d: float64 %f out of byte range", i, x) + } + dst[i] = byte(x) + default: + return nil, fmt.Errorf("element %d: unsupported type %T", i, v) + } + } + + return dst, nil +} + +// handleSingleFieldStruct processes structs with single fields +// This is kept here for backward compatibility but the main implementation is in type_converters.go +func handleSingleFieldStruct(t reflect.Type, data any, decodeFn func(any, any) error) (any, error) { + field := t.Field(0) + newStructVal := reflect.New(t).Elem() + fieldPtr := newStructVal.Field(0).Addr().Interface() + + if err := decodeFn(data, fieldPtr); err != nil { + return nil, fmt.Errorf("failed decoding for single-field struct %v field %s (%v): %w", + t, field.Name, field.Type, err) + } + + return newStructVal.Interface(), nil +} + +// Overflow checking functions +func overflowFloat(t reflect.Type, x float64) bool { + //nolint:exhaustive + switch t.Kind() { + case reflect.Float32: + return overflowFloat32(x) + case reflect.Float64: + return false + default: + panic("reflect: OverflowFloat of non-float type " + t.String()) + } +} + +func overflowFloat32(x float64) bool { + if x < 0 { + x = -x + } + + return math.MaxFloat32 < x && x <= math.MaxFloat64 +} + +func overflowInt(t reflect.Type, x int64) bool { + //nolint:exhaustive + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + bitSize := t.Size() * uint8Bits + trunc := (x << (uint64Bits - bitSize)) >> (uint64Bits - bitSize) + + return x != trunc + default: + panic("reflect: OverflowInt of non-int type " + t.String()) + } +} + +func overflowUint(t reflect.Type, x uint64) bool { + //nolint:exhaustive + switch t.Kind() { + case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + bitSize := t.Size() * uint8Bits + trunc := (x << (uint64Bits - bitSize)) >> (uint64Bits - bitSize) + + return x != trunc + default: + panic("reflect: OverflowUint of non-uint type " + t.String()) + } +} + +func DeserializeExecutionReport(data []byte) (*ExecutionReport, error) { + deserializer := aptosBCS.NewDeserializer(data) + + // 1. Read source_chain_selector (u64) + sourceChainSelector := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sourceChainSelector: %w", err) + } + + // 2. Read message header + messageID := make([]byte, 32) + deserializer.ReadFixedBytesInto(messageID) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize messageID: %w", err) + } + + headerSourceChain := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize headerSourceChain: %w", err) + } + + destChainSelector := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destChainSelector: %w", err) + } + + sequenceNumber := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sequenceNumber: %w", err) + } + + nonce := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize nonce: %w", err) + } + + if sourceChainSelector != headerSourceChain { + return nil, fmt.Errorf("source chain selector mismatch: %d != %d", sourceChainSelector, headerSourceChain) + } + + header := RampMessageHeader{ + MessageID: messageID, + SourceChainSelector: headerSourceChain, + DestChainSelector: destChainSelector, + SequenceNumber: sequenceNumber, + Nonce: nonce, + } + + // 3. Read sender (vector) + sender := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sender: %w", err) + } + + // 4. Read data (vector) + msgData := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize data: %w", err) + } + + // 5. Read receiver (address) + receiver := deserializer.ReadFixedBytes(32) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize receiver: %w", err) + } + + // 6. Read gas_limit (u256) + gasLimit := deserializer.U256() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize gas_limit: %w", err) + } + + tokenReceiver := [32]byte{} + deserializer.ReadFixedBytesInto(tokenReceiver[:]) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize tokenReceiver: %w", err) + } + + // 7. Read token_amounts vector + tokenAmountsLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize token_amounts length: %w", err) + } + remaining := deserializer.Remaining() + if remaining < 0 || uint64(tokenAmountsLen)*tokenTransferMinWireBytes > uint64(remaining) { + return nil, fmt.Errorf("failed to deserialize execution report: token_amounts length %d exceeds remaining %d bytes", tokenAmountsLen, remaining) + } + tokenAmounts := make([]Any2SuiTokenTransfer, tokenAmountsLen) + + for i := range tokenAmountsLen { + sourcePoolAddr := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sourcePoolAddr: %w", err) + } + + destToken := deserializer.ReadFixedBytes(32) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destToken: %w", err) + } + + destGas := deserializer.U32() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destGas: %w", err) + } + extraData := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize extraData: %w", err) + } + amount := deserializer.U256() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize amount: %w", err) + } + + tokenAmounts[i] = Any2SuiTokenTransfer{ + SourcePoolAddress: sourcePoolAddr, + DestTokenAddress: models.SuiAddress(hex.EncodeToString(destToken)), + DestGasAmount: destGas, + ExtraData: extraData, + Amount: &amount, + } + } + + message := Any2SuiRampMessage{ + Header: header, + Sender: sender, + Data: msgData, + Receiver: models.SuiAddress(hex.EncodeToString(receiver)), + GasLimit: &gasLimit, + TokenReceiver: models.SuiAddressBytes(tokenReceiver), + TokenAmounts: tokenAmounts, + } + + // 8. Read offchain_token_data (vector>) + offchainDataLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize offchain_token_data length: %w", err) + } + if int(offchainDataLen) > deserializer.Remaining() { + return nil, fmt.Errorf("failed to deserialize execution report: offchain_token_data length %d exceeds remaining %d bytes", offchainDataLen, deserializer.Remaining()) + } + offchainData := make([][]byte, offchainDataLen) + + for i := range offchainDataLen { + offchainData[i] = deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize offchainData at index %d: %w", i, err) + } + } + + // 9. Read proofs (vector>) + proofsLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize proofs length: %w", err) + } + remaining = deserializer.Remaining() + if remaining < 0 || uint64(proofsLen)*proofWireBytes > uint64(remaining) { + return nil, fmt.Errorf("failed to deserialize execution report: proofs length %d exceeds remaining %d bytes", proofsLen, remaining) + } + proofs := make([][]byte, proofsLen) + + for i := range proofsLen { + proofs[i] = deserializer.ReadFixedBytes(proofWireBytes) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize proof at index %d: %w", i, err) + } + } + + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize execution report: %w", err) + } + + return &ExecutionReport{ + SourceChainSelector: sourceChainSelector, + Message: message, + OffchainTokenData: offchainData, + Proofs: proofs, + }, nil +} + +// UnwrapBCSPureBytes decodes a BCS-encoded pure input value stored on-chain. +// Pure vector arguments are stored with a ULEB128 length prefix. +func UnwrapBCSPureBytes(pure []byte) ([]byte, error) { + if len(pure) == 0 { + return nil, errors.New("pure bytes are empty") + } + + deserializer := aptosBCS.NewDeserializer(pure) + unwrapped := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to unwrap pure bytes: %w", err) + } + + return unwrapped, nil +} + +// DeserializeExecutionReportFromPure deserializes an execution report from a +// BCS-encoded pure input containing vector. +func DeserializeExecutionReportFromPure(pure []byte) (*ExecutionReport, error) { + reportBytes, err := UnwrapBCSPureBytes(pure) + if err != nil { + return nil, err + } + + return DeserializeExecutionReport(reportBytes) +} diff --git a/relayer/codec/decoder_test.go b/codec/decoder_test.go similarity index 100% rename from relayer/codec/decoder_test.go rename to codec/decoder_test.go diff --git a/codec/encoder.go b/codec/encoder.go new file mode 100644 index 000000000..cdae87709 --- /dev/null +++ b/codec/encoder.go @@ -0,0 +1,368 @@ +package codec + +import ( + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "reflect" + "strconv" + "strings" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" +) + +const ( + // Type bounds + maxUint8 = 255 + maxUint16 = 65535 + maxUint32 = 4294967295 +) + +// Safe conversion functions to avoid lint issues +func safeUint8(val uint64) (uint8, error) { + if val > maxUint8 { + return 0, fmt.Errorf("value %d exceeds uint8 maximum", val) + } + + return uint8(val), nil +} + +func safeUint16(val uint64) (uint16, error) { + if val > maxUint16 { + return 0, fmt.Errorf("value %d exceeds uint16 maximum", val) + } + + return uint16(val), nil +} + +func safeUint32(val uint64) (uint32, error) { + if val > maxUint32 { + return 0, fmt.Errorf("value %d exceeds uint32 maximum", val) + } + + return uint32(val), nil +} + +func EncodeToSuiValue(typeName string, value any) (any, error) { + // Normalize fully-qualified Sui string type to logical "string" + if strings.Contains(typeName, "::string::String") { + typeName = "string" + } + + switch typeName { + case "address": + return encodeAddress(value) + case "object_id": + return encodeObject(value) + case "u8", "u16", "u32", "u64", "u128", "u256": + return encodeUint(typeName, value) + case "bool": + return encodeBool(value) + case "string": + return encodeString(value) + default: + if strings.HasPrefix(typeName, "0x") && strings.Contains(typeName, "::") { + // TODO: need to use go-bsc to encode this. Reference https://github.com/fardream/go-bcs/blob/main/bcs/encode_test.go + return nil, errors.New("struct types are not supported") + } + if strings.HasPrefix(typeName, "vector<") && strings.HasSuffix(typeName, ">") { + return encodeVector(typeName, value) + } + + return nil, fmt.Errorf("unsupported type: %s", typeName) + } +} + +func encodeObject(value any) (bind.Object, error) { + switch v := value.(type) { + case bind.Object: + return bind.Object{Id: v.Id}, nil + default: + return bind.Object{}, fmt.Errorf("cannot convert %T to object", value) + } +} + +func encodeAddress(value any) (string, error) { + switch v := value.(type) { + case string: + // Ensure it's a valid Sui address format + if !strings.HasPrefix(v, "0x") { + v = "0x" + v + } + + normalizedAddress, err := bind.ToSuiAddress(v) + if err != nil { + return "", fmt.Errorf("failed to convert address to Sui address: %w", err) + } + + return normalizedAddress, nil + case []byte: + stringAddr := "0x" + hex.EncodeToString(v) + + normalizedAddress, err := bind.ToSuiAddress(stringAddr) + if err != nil { + return "", fmt.Errorf("failed to convert address to Sui address: %w", err) + } + + return normalizedAddress, nil + default: + return "", fmt.Errorf("cannot convert %T to address", value) + } +} + +func encodeUint(typeName string, value any) (any, error) { + // First convert to a common intermediate type + var baseValue uint64 + var bigIntValue *big.Int + + switch v := value.(type) { + case int: + if v < 0 { + return nil, fmt.Errorf("cannot convert negative int %d to %s", v, typeName) + } + baseValue = uint64(v) + case int64: + if v < 0 { + return nil, fmt.Errorf("cannot convert negative int %d to %s", v, typeName) + } + baseValue = uint64(v) + case uint: + baseValue = uint64(v) + case uint64: + baseValue = v + case uint32: + baseValue = uint64(v) + case uint16: + baseValue = uint64(v) + case uint8: + baseValue = uint64(v) + case float64: + if v < 0 { + return nil, fmt.Errorf("cannot convert negative float %f to %s", v, typeName) + } + baseValue = uint64(v) + case json.Number: + // Handle JSON numbers properly + numStr := v.String() + if typeName == "u128" || typeName == "u256" { + bigIntValue = new(big.Int) + if _, ok := bigIntValue.SetString(numStr, base10); !ok { + return nil, fmt.Errorf("cannot convert json.Number %s to %s", v, typeName) + } + } else if strings.Contains(numStr, ".") { + f, err := v.Float64() + if err != nil { + return nil, fmt.Errorf("cannot convert json.Number %s to %s: %w", v, typeName, err) + } + baseValue = uint64(f) + } else { + // json parsing int64 for selector was going out of range + i, err := strconv.ParseUint(numStr, base10, 64) + if err != nil { + return nil, fmt.Errorf("cannot convert json.Number %s to %s: %w", v, typeName, err) + } + //nolint:gosec + // we assume safe conversion without negative numbers + baseValue = uint64(i) + } + case string: + // Handle big numbers that might come as strings + if typeName == "u128" || typeName == "u256" { + bigIntValue = new(big.Int) + _, ok := bigIntValue.SetString(v, base10) + if !ok { + return nil, fmt.Errorf("cannot convert string %s to %s", v, typeName) + } + } else { + i, err := strconv.ParseUint(v, base10, 64) + if err != nil { + return nil, fmt.Errorf("cannot convert string %s to %s: %w", v, typeName, err) + } + baseValue = i + } + case *big.Int: + bigIntValue = v + default: + return nil, fmt.Errorf("cannot convert %T to %s", value, typeName) + } + + // Now convert to the appropriate type based on typeName + switch typeName { + case "u8": + if bigIntValue != nil { + if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint8 { + return nil, fmt.Errorf("value %s too large for u8", bigIntValue.String()) + } + // Safe conversion after bounds check + return safeUint8(bigIntValue.Uint64()) + } + if baseValue > maxUint8 { + return nil, fmt.Errorf("value %d too large for u8", baseValue) + } + + return safeUint8(baseValue) + case "u16": + if bigIntValue != nil { + if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint16 { + return nil, fmt.Errorf("value %s too large for u16", bigIntValue.String()) + } + // Safe conversion after bounds check + return safeUint16(bigIntValue.Uint64()) + } + if baseValue > maxUint16 { + return nil, fmt.Errorf("value %d too large for u16", baseValue) + } + + return safeUint16(baseValue) + case "u32": + if bigIntValue != nil { + if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint32 { + return nil, fmt.Errorf("value %s too large for u32", bigIntValue.String()) + } + // Safe conversion after bounds check + return safeUint32(bigIntValue.Uint64()) + } + if baseValue > maxUint32 { + return nil, fmt.Errorf("value %d too large for u32", baseValue) + } + + return safeUint32(baseValue) + case "u64": + if bigIntValue != nil { + if !bigIntValue.IsUint64() { + return nil, fmt.Errorf("value %s too large for u64", bigIntValue.String()) + } + + return bigIntValue.Uint64(), nil + } + + return baseValue, nil + case "u128": + if bigIntValue != nil { + // Validate it fits in u128 (2^128 - 1) + maxVal := new(big.Int) + maxVal.Exp(big.NewInt(base2), big.NewInt(bits128), nil) + maxVal.Sub(maxVal, big.NewInt(1)) + if bigIntValue.Cmp(maxVal) > 0 { + return nil, fmt.Errorf("value %s too large for u128", bigIntValue.String()) + } + + return bigIntValue.String(), nil + } + + return strconv.FormatUint(baseValue, base10), nil + case "u256": + if bigIntValue != nil { + // Validate it fits in u256 (2^256 - 1) + maxVal := new(big.Int) + maxVal.Exp(big.NewInt(base2), big.NewInt(bits256), nil) + maxVal.Sub(maxVal, big.NewInt(1)) + if bigIntValue.Cmp(maxVal) > 0 { + return nil, fmt.Errorf("value %s too large for u256", bigIntValue.String()) + } + + return bigIntValue.String(), nil + } + + return strconv.FormatUint(baseValue, base10), nil + default: + return nil, fmt.Errorf("unsupported uint type: %s", typeName) + } +} + +func encodeBool(value any) (bool, error) { + switch v := value.(type) { + case bool: + return v, nil + case string: + b, err := strconv.ParseBool(v) + if err != nil { + return false, fmt.Errorf("cannot convert string %s to bool: %w", v, err) + } + + return b, nil + case int: + return v != 0, nil + case float64: + return v != 0, nil + default: + return false, fmt.Errorf("cannot convert %T to bool", value) + } +} + +func encodeString(value any) (string, error) { + switch v := value.(type) { + case string: + return v, nil + case []byte: + return string(v), nil + default: + return "", fmt.Errorf("cannot convert %T to string", value) + } +} + +func encodeVector(typeName string, value any) ([]any, error) { + // Extract the inner type, e.g., "vector" -> "string" + if !strings.HasPrefix(typeName, "vector<") || !strings.HasSuffix(typeName, ">") { + return nil, fmt.Errorf("invalid vector type: %s", typeName) + } + innerType := typeName[len("vector<") : len(typeName)-1] + + if value == nil { + return nil, fmt.Errorf("nil value cannot be encoded in encodeVector") + } + rv := reflect.ValueOf(value) + + if !rv.IsValid() { + return nil, fmt.Errorf("invalid reflect value for vector type %s in encodeVector", typeName) + } + kind := rv.Kind() + + // Use reflection to ensure 'value' is a slice or array + if kind != reflect.Slice && kind != reflect.Array { + return nil, fmt.Errorf("expected a slice/array for vector type %s, got %T", typeName, value) + } + + // Special handling for vector (byte arrays) + if innerType == "u8" { + // Handle []any from JSON unmarshaling + if interfaceSlice, ok := value.([]any); ok { + bytes := make([]byte, len(interfaceSlice)) + for i, item := range interfaceSlice { + if num, ok := item.(float64); ok { + if num < 0 || num > maxUint8 { + return nil, fmt.Errorf("invalid byte value at index %d: %f", i, num) + } + bytes[i] = byte(num) + } else if num, ok := item.(int); ok { + if num < 0 || num > maxUint8 { + return nil, fmt.Errorf("invalid byte value at index %d: %d", i, num) + } + bytes[i] = byte(num) + } else { + return nil, fmt.Errorf("invalid byte value at index %d: %T", i, item) + } + } + + return []any{bytes}, nil + } + // Handle []byte directly + if bytes, ok := value.([]byte); ok { + return []any{bytes}, nil + } + } + + encodedElements := make([]any, 0, rv.Len()) + for i := range rv.Len() { + elem := rv.Index(i).Interface() + encodedElem, err := EncodeToSuiValue(innerType, elem) + if err != nil { + return nil, fmt.Errorf("failed to encode element at index %d: %w", i, err) + } + encodedElements = append(encodedElements, encodedElem) + } + + return encodedElements, nil +} diff --git a/codec/go.mod b/codec/go.mod new file mode 100644 index 000000000..f55a2b73f --- /dev/null +++ b/codec/go.mod @@ -0,0 +1,155 @@ +module github.com/smartcontractkit/chainlink-sui/codec + +go 1.26.2 + +require ( + github.com/aptos-labs/aptos-go-sdk v1.13.0 + github.com/block-vision/sui-go-sdk v1.2.1 + github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 + github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 + github.com/stretchr/testify v1.11.1 + google.golang.org/grpc v1.81.1 +) + +require ( + filippo.io/edwards25519 v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/XSAM/otelsql v0.37.0 // indirect + github.com/apache/arrow-go/v18 v18.3.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect + github.com/buger/jsonparser v1.1.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2 // indirect + github.com/cloudevents/sdk-go/v2 v2.16.2 // indirect + github.com/coder/websocket v1.8.14 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/ethereum/go-ethereum v1.17.3 // indirect + github.com/fatih/color v1.19.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.3 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grafana/otel-profiling-go v0.5.1 // indirect + github.com/grafana/pyroscope-go v1.2.8 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-plugin v1.8.0 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/hasura/go-graphql-client v0.15.1 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/invopop/jsonschema v0.13.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/copier v0.4.0 // indirect + github.com/jmoiron/sqlx v1.4.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.5 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lib/pq v1.12.3 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/marcboeker/go-duckdb v1.8.5 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oklog/run v1.2.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.3.0 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v1.20.99 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/samber/lo v1.53.0 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect + github.com/scylladb/go-reflectx v1.0.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/smartcontractkit/chain-selectors v1.0.102 // indirect + github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect + github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect + github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect + github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect + github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e // indirect + github.com/test-go/testify v1.1.4 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.uber.org/goleak v1.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.28.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.45.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/codec/go.sum b/codec/go.sum new file mode 100644 index 000000000..af7737f81 --- /dev/null +++ b/codec/go.sum @@ -0,0 +1,539 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= +filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/XSAM/otelsql v0.37.0 h1:ya5RNw028JW0eJW8Ma4AmoKxAYsJSGuNVbC7F1J457A= +github.com/XSAM/otelsql v0.37.0/go.mod h1:LHbCu49iU8p255nCn1oi04oX2UjSoRcUMiKEHo2a5qM= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4Dj3ixk= +github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= +github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= +github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= +github.com/aptos-labs/aptos-go-sdk v1.13.0 h1:epv7K/tIbAEO2RfogwGacICBig8rrigJj24fDsy6KTg= +github.com/aptos-labs/aptos-go-sdk v1.13.0/go.mod h1:FTgKp0RLfEefllCdkCj0jPU14xWk11yA7SFVfCDLUj8= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/block-vision/sui-go-sdk v1.2.1 h1:uwvGbzfcrS4SsIaakclYxy0qgEF1XWIUtTYWXB4PoAw= +github.com/block-vision/sui-go-sdk v1.2.1/go.mod h1:t8mWASwfyv+EyqHGO9ZrcDiCJWGOFEXqq50TMJ8GQco= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= +github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2 h1:ydUjnKn4RoCeN8rge3F/deT52w2WJMmIC5mHNUq+Ut8= +github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2/go.mod h1:Bny999RuVUtNjzTGa9HCHpXjrLGMipJVq5kqVpudBl0= +github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK1D+NnQM= +github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI= +github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q5LDd4Q= +github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY= +github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= +github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= +github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M= +github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.3.5 h1:b3taDMxCBCBVgyRrS1AZVHO14ubMYZB++QpNhBg+Nyo= +github.com/hashicorp/go-memdb v1.3.5/go.mod h1:8IVKKBkVe+fxFgdFOYxzQQNjz+sWCyHCdIC/+5+Vy1Y= +github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs= +github.com/hashicorp/go-plugin v1.8.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/hasura/go-graphql-client v0.15.1 h1:mCb5I+8Bk3FU3GKWvf/zDXkTh7FbGlqJmP3oisBdnN8= +github.com/hasura/go-graphql-client v0.15.1/go.mod h1:jfSZtBER3or+88Q9vFhWHiFMPppfYILRyl+0zsgPIIw= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= +github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= +github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= +github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v1.20.99 h1:vZEybF3CT0t6L0UjsOtHRML7vuIglHocmvJMMH/se4M= +github.com/prometheus/common v1.20.99/go.mod h1:VX44Tebe4qpuTK+MQWg25h4fJGKBqzObSdxuB7y8K/Y= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= +github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= +github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/smartcontractkit/chain-selectors v1.0.102 h1:qYP4+72HfvogCHR5ymwRFee36WH77514ZBj299SVCBA= +github.com/smartcontractkit/chain-selectors v1.0.102/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb h1:uSXBvj/idCBg7hMLmek8YYNa0JKggqlTkHYdtIooeNM= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb/go.mod h1:2xVZJ3o9udYFeJhwyHXAMlNhptJ99uoiGjzfOicYNX8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 h1:5bxDnwI0wuPoC0H5H3H2n9CnQPb5iakR6UmAY4j8KUg= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= +github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 h1:L2Y1W2j+GcgGJ8Af6PkXznAUGOMwfOatk9nooE57Gx4= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5/go.mod h1:YYlFG2/G5/Q+BzByqcsvIZxS3vW8dgnxz6j+VTwEYj4= +github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= +github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= +github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= +github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= +github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= +github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= diff --git a/relayer/chainreader/loop/connection_load_test.go b/codec/loop/connection_load_test.go similarity index 100% rename from relayer/chainreader/loop/connection_load_test.go rename to codec/loop/connection_load_test.go diff --git a/codec/loop/loop_reader.go b/codec/loop/loop_reader.go new file mode 100644 index 000000000..97eedc9dd --- /dev/null +++ b/codec/loop/loop_reader.go @@ -0,0 +1,191 @@ +package loop + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types/query" + "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + + "github.com/smartcontractkit/chainlink-aptos/relayer/chainreader/loop" + + "github.com/smartcontractkit/chainlink-sui/relayer/codec" +) + +const ( + READ_COMPONENTS_COUNT = 3 +) + +func NewLoopChainReader(log logger.Logger, reader types.ContractReader) types.ContractReader { + lrLogger := logger.Named(log, "LoopChainReader") + return &loopChainReader{logger: lrLogger, reader: reader, moduleAddresses: map[string]string{}} +} + +type loopChainReader struct { + services.Service + types.UnimplementedContractReader + logger logger.Logger + reader types.ContractReader + moduleAddresses map[string]string +} + +func (s *loopChainReader) Name() string { + return s.reader.Name() +} + +func (s *loopChainReader) Ready() error { + return s.reader.Ready() +} + +func (s *loopChainReader) HealthReport() map[string]error { + return s.reader.HealthReport() +} + +func (s *loopChainReader) Start(ctx context.Context) error { + return s.reader.Start(ctx) +} + +func (s *loopChainReader) Close() error { + return s.reader.Close() +} + +func (s *loopChainReader) GetLatestValue(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error { + convertedResult := []byte{} + + s.logger.Debugw("GetLatestValue params before serialization over LOOP", "params", params) + + jsonParamBytes, err := json.Marshal(params) + if err != nil { + return fmt.Errorf("failed to marshal params: %+w", err) + } + + err = s.reader.GetLatestValue(ctx, readIdentifier, confidenceLevel, &jsonParamBytes, &convertedResult) + if err != nil { + return fmt.Errorf("failed to call GetLatestValue over LOOP: %w", err) + } + + err = s.decodeGLVReturnValue(readIdentifier, convertedResult, returnVal) + if err != nil { + return fmt.Errorf("failed to decode GetLatestValue return value: %w", err) + } + + return nil +} + +func (s *loopChainReader) BatchGetLatestValues(ctx context.Context, request types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) { + convertedRequest := types.BatchGetLatestValuesRequest{} + for contract, requestBatch := range request { + convertedBatch := []types.BatchRead{} + for _, read := range requestBatch { + jsonParamBytes, err := json.Marshal(read.Params) + if err != nil { + return nil, fmt.Errorf("failed to marshal params: %+w", err) + } + convertedBatch = append(convertedBatch, types.BatchRead{ + ReadName: read.ReadName, + Params: jsonParamBytes, + ReturnVal: &[]byte{}, + }) + } + convertedRequest[contract] = convertedBatch + } + + result, err := s.reader.BatchGetLatestValues(ctx, convertedRequest) + if err != nil { + return nil, err + } + + convertedResult := types.BatchGetLatestValuesResult{} + for contract, resultBatch := range result { + requestBatch := request[contract] + convertedBatch := []types.BatchReadResult{} + for i, result := range resultBatch { + read := requestBatch[i] + resultValue, resultError := result.GetResult() + convertedResult := types.BatchReadResult{ReadName: result.ReadName} + if resultError == nil { + resultPointer := resultValue.(*[]byte) + err := s.decodeGLVReturnValue(result.ReadName, *resultPointer, read.ReturnVal) + if err != nil { + resultError = fmt.Errorf("failed to decode BatchGetLatestValue return value: %w", err) + } + } + convertedResult.SetResult(read.ReturnVal, resultError) + convertedBatch = append(convertedBatch, convertedResult) + } + convertedResult[contract] = convertedBatch + } + + s.logger.Debugw("BatchGetLatestValues result", "result", convertedResult) + + return convertedResult, nil +} + +func (s *loopChainReader) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) { + convertedExpressions, err := loop.SerializeExpressions(filter.Expressions) + if err != nil { + return nil, fmt.Errorf("failed to serialize QueryKey expressions: %w", err) + } + + s.logger.Debugw("QueryKey expressions after serialization over LOOP", "expressions", convertedExpressions) + + convertedFilter := query.KeyFilter{ + Key: filter.Key, + Expressions: convertedExpressions, + } + + sequences, err := s.reader.QueryKey(ctx, contract, convertedFilter, limitAndSort, &[]byte{}) + if err != nil { + return nil, fmt.Errorf("failed to call QueryKey over LOOP: %w", err) + } + + for i, sequence := range sequences { + jsonBytes := sequence.Data.(*[]byte) + jsonData := map[string]any{} + err := json.Unmarshal(*jsonBytes, &jsonData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal LOOP sourced JSON event data (`%s`): %w", string(*jsonBytes), err) + } + + eventData := reflect.New(reflect.TypeOf(sequenceDataType).Elem()).Interface() + err = codec.DecodeSuiJsonValue(jsonData, eventData) + if err != nil { + return nil, fmt.Errorf("failed to decode LOOP sourced event data (`%s`) into a Sui value: %+w", string(*jsonBytes), err) + } + + sequences[i].Data = eventData + } + + return sequences, nil +} + +func (s *loopChainReader) Bind(ctx context.Context, bindings []types.BoundContract) error { + return s.reader.Bind(ctx, bindings) +} + +func (s *loopChainReader) Unbind(ctx context.Context, bindings []types.BoundContract) error { + // we ignore unbind errors, because if the LOOP plugin restarted, the binding would not exist. + _ = s.reader.Unbind(ctx, bindings) + + return nil +} + +func (s *loopChainReader) decodeGLVReturnValue(label string, jsonBytes []byte, returnVal any) error { + var result any + err := json.Unmarshal(jsonBytes, &result) + if err != nil { + return fmt.Errorf("failed to unmarshal %s GetLatestValue result (`%s`): %w", label, string(jsonBytes), err) + } + + err = codec.DecodeSuiJsonValue(result, returnVal) + if err != nil { + return fmt.Errorf("failed to decode %s GetLatestValue JSON value (`%s`) to %T: %w", label, string(jsonBytes), returnVal, err) + } + + return nil +} diff --git a/relayer/chainreader/loop/loop_reader_test.go b/codec/loop/loop_reader_test.go similarity index 100% rename from relayer/chainreader/loop/loop_reader_test.go rename to codec/loop/loop_reader_test.go diff --git a/codec/type_converters.go b/codec/type_converters.go new file mode 100644 index 000000000..c330c21d5 --- /dev/null +++ b/codec/type_converters.go @@ -0,0 +1,775 @@ +package codec + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "reflect" + "strconv" + "strings" +) + +// TypeConversionFunc defines a function that converts data from one type to another +type TypeConversionFunc func(from reflect.Type, to reflect.Type, data any) (any, error) + +// TypeConverter provides a registry approach to type conversions for mapstructure +type TypeConverter struct { + converters map[string]TypeConversionFunc +} + +// NewTypeConverter creates a new type converter with all standard conversions registered +func NewTypeConverter() *TypeConverter { + tc := &TypeConverter{ + converters: make(map[string]TypeConversionFunc), + } + + tc.registerStandardConverters() + return tc +} + +// registerStandardConverters registers all standard type conversion handlers +func (tc *TypeConverter) registerStandardConverters() { + // Hex string conversions + tc.RegisterConverter("hex_string_to_string", tc.hexToString) + tc.RegisterConverter("hex_string_to_bytes", tc.hexToBytes) + tc.RegisterConverter("hex_string_to_uint", tc.hexToUint) + tc.RegisterConverter("hex_string_to_int", tc.hexToInt) + tc.RegisterConverter("hex_string_to_bigint", tc.hexToBigInt) + tc.RegisterConverter("hex_string_to_array", tc.hexToArray) + + // Base64 string conversions + tc.RegisterConverter("base64_string_to_bytes", tc.base64ToBytes) + + // Numeric string conversions + tc.RegisterConverter("string_to_int", tc.stringToInt) + tc.RegisterConverter("string_to_uint", tc.stringToUint) + tc.RegisterConverter("string_to_float", tc.stringToFloat) + tc.RegisterConverter("string_to_bytes", tc.stringToBytes) + tc.RegisterConverter("string_to_bigint", tc.stringToBigInt) + + // Boolean conversions + tc.RegisterConverter("bool_to_int", tc.boolToInt) + tc.RegisterConverter("bool_to_uint", tc.boolToUint) + tc.RegisterConverter("bool_to_bigint", tc.boolToBigInt) + + // Array/Slice conversions + tc.RegisterConverter("slice_to_slice", tc.sliceToSlice) + tc.RegisterConverter("slice_to_hex_string", tc.sliceToHexString) + + // Float64 conversions (JSON unmarshals numbers as float64) + tc.RegisterConverter("float64_to_uint", tc.float64ToUint) + tc.RegisterConverter("float64_to_int", tc.float64ToInt) + + // json.Number conversions + tc.RegisterConverter("json_number_to_uint", tc.jsonNumberToUint) + tc.RegisterConverter("json_number_to_int", tc.jsonNumberToInt) + + // Bytes to numeric conversions (for BCS decoding) + tc.RegisterConverter("bytes_to_uint", tc.bytesToUint) + tc.RegisterConverter("slice_any_to_uint", tc.sliceAnyToUint) +} + +// RegisterConverter registers a conversion function with a unique key +func (tc *TypeConverter) RegisterConverter(key string, fn TypeConversionFunc) { + tc.converters[key] = fn +} + +// Convert attempts to convert data using registered converters +func (tc *TypeConverter) Convert(from reflect.Type, to reflect.Type, data any) (any, error) { + // Try float64 conversions (JSON unmarshals numbers as float64) + if from.Kind() == reflect.Float64 { + result, err, handled := tc.handleFloat64(data.(float64), to) + if handled { + return result, err + } + } + + // Try json.Number conversions + if _, ok := data.(json.Number); ok { + result, err, handled := tc.handleJSONNumber(data.(json.Number), to) + if handled { + return result, err + } + } + + // Try hex string conversions + if from.Kind() == reflect.String { + if str, ok := data.(string); ok && strings.HasPrefix(str, "0x") { + result, err, handled := tc.handleHexString(str, to, data) + if handled { + return result, err + } + } + } + + // Try numeric string conversions (before base64, as numeric strings can be valid base64) + if from.Kind() == reflect.String { + result, err, handled := tc.handleNumericString(data.(string), to) + if handled { + return result, err + } + } + + // Try base64 conversions (fallback for non-numeric strings to []byte) + if from.Kind() == reflect.String && (to.Kind() == reflect.Slice || to.Kind() == reflect.Array) && to.Elem().Kind() == reflect.Uint8 { + result, err, handled := tc.handleBase64String(data.(string), to) + if handled { + return result, err + } + } + + // Try boolean conversions + if from.Kind() == reflect.Bool { + result, err, handled := tc.handleBoolean(data.(bool), to) + if handled { + return result, err + } + } + + // Try bytes/slice to numeric conversions + if from.Kind() == reflect.Slice || from.Kind() == reflect.Array { + // Handle []byte or []any to numeric + if isNumericTarget(to) { + result, err, handled := tc.handleBytesToNumeric(from, to, data) + if handled { + return result, err + } + } + + // Handle slice to slice conversions + if to.Kind() == reflect.Slice { + result, err, handled := tc.handleSlice(from, to, data) + if handled { + return result, err + } + } + + // Handle slice to hex string conversions + if to.Kind() == reflect.String { + result, err := tc.sliceToHexString(from, to, data) + if err == nil { + return result, err + } + } + } + + // No conversion found, return data as-is + return data, nil +} + +// handleHexString handles hex string conversions +func (tc *TypeConverter) handleHexString(str string, to reflect.Type, data any) (result any, err error, handled bool) { + hexStr := strings.TrimPrefix(str, "0x") + + switch to.Kind() { + case reflect.String: + if fn, ok := tc.converters["hex_string_to_string"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + case reflect.Slice: + if to.Elem().Kind() == reflect.Uint8 { + if fn, ok := tc.converters["hex_string_to_bytes"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + } + case reflect.Array: + if to.Elem().Kind() == reflect.Uint8 { + if fn, ok := tc.converters["hex_string_to_array"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if fn, ok := tc.converters["hex_string_to_uint"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if fn, ok := tc.converters["hex_string_to_int"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + case reflect.Ptr: + if to == reflect.TypeOf((*big.Int)(nil)) { + if fn, ok := tc.converters["hex_string_to_bigint"]; ok { + result, err = fn(reflect.TypeOf(str), to, hexStr) + return result, err, true + } + } + case reflect.Interface: + return "0x" + hexStr, nil, true + } + + return nil, nil, false +} + +// handleBase64String handles base64 string conversions +func (tc *TypeConverter) handleBase64String(str string, to reflect.Type) (result any, err error, handled bool) { + if fn, ok := tc.converters["base64_string_to_bytes"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + // If base64ToBytes returns the original string unchanged, it means + // base64 decoding failed, so we should let default handling try + if resultStr, isStr := result.(string); isStr && resultStr == str { + return nil, nil, false + } + return result, err, true + } + return nil, nil, false +} + +// handleNumericString handles numeric string conversions +func (tc *TypeConverter) handleNumericString(str string, to reflect.Type) (result any, err error, handled bool) { + switch to.Kind() { + case reflect.String: + return str, nil, true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if fn, ok := tc.converters["string_to_int"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + return result, err, true + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if fn, ok := tc.converters["string_to_uint"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + return result, err, true + } + case reflect.Float32, reflect.Float64: + if fn, ok := tc.converters["string_to_float"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + return result, err, true + } + case reflect.Slice: + if to.Elem().Kind() == reflect.Uint8 { + if fn, ok := tc.converters["string_to_bytes"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + // If stringToBytes returns the original string unchanged, it means + // it's not a numeric string, so we should let other handlers try + if resultStr, isStr := result.(string); isStr && resultStr == str { + return nil, nil, false + } + return result, err, true + } + } + case reflect.Ptr: + if to == reflect.TypeOf((*big.Int)(nil)) { + if fn, ok := tc.converters["string_to_bigint"]; ok { + result, err = fn(reflect.TypeOf(str), to, str) + return result, err, true + } + } + } + + return nil, nil, false +} + +// handleBoolean handles boolean conversions +func (tc *TypeConverter) handleBoolean(boolValue bool, to reflect.Type) (result any, err error, handled bool) { + switch to.Kind() { + case reflect.Bool: + return boolValue, nil, true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if fn, ok := tc.converters["bool_to_int"]; ok { + result, err = fn(reflect.TypeOf(boolValue), to, boolValue) + return result, err, true + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if fn, ok := tc.converters["bool_to_uint"]; ok { + result, err = fn(reflect.TypeOf(boolValue), to, boolValue) + return result, err, true + } + case reflect.Ptr: + if to == reflect.TypeOf((*big.Int)(nil)) { + if fn, ok := tc.converters["bool_to_bigint"]; ok { + result, err = fn(reflect.TypeOf(boolValue), to, boolValue) + return result, err, true + } + } + } + + return nil, nil, false +} + +// handleSlice handles array/slice conversions +func (tc *TypeConverter) handleSlice(from, to reflect.Type, data any) (result any, err error, handled bool) { + if fn, ok := tc.converters["slice_to_slice"]; ok { + result, err = fn(from, to, data) + return result, err, true + } + return nil, nil, false +} + +// Conversion function implementations + +func (tc *TypeConverter) hexToString(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + return hexStr, nil +} + +func (tc *TypeConverter) hexToBytes(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + + if hexStr == "" { + return []uint8{}, nil + } + + if len(hexStr)%2 == 1 { + hexStr = "0" + hexStr + } + + return hex.DecodeString(hexStr) +} + +func (tc *TypeConverter) hexToUint(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + + return strconv.ParseUint(hexStr, base16, uint64Bits) +} + +func (tc *TypeConverter) hexToInt(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + + val, err := strconv.ParseInt(hexStr, base16, uint64Bits) + if err != nil { + return nil, fmt.Errorf("failed to parse hex to int: %w", err) + } + + return reflect.ValueOf(val).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) hexToBigInt(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + + bi := new(big.Int) + bi.SetString(hexStr, base16) + return bi, nil +} + +func (tc *TypeConverter) hexToArray(from, to reflect.Type, data any) (any, error) { + hexStr, ok := data.(string) + if !ok { + return data, nil + } + + bytes, err := tc.hexToBytes(from, reflect.SliceOf(to.Elem()), hexStr) + if err != nil { + return nil, fmt.Errorf("failed to decode hex string %q: %w", hexStr, err) + } + + byteSlice := bytes.([]byte) + out := make([]uint8, to.Len()) + + // Re-enable this check once we can guarantee that responses are always the same length as the target array length. + // Disabling for now to avoid breaking changes to core. + // if len(byteSlice) != to.Len() { + // return nil, fmt.Errorf("hex to array: byte slice length %d is not equal to output array length %d", len(byteSlice), to.Len()) + // } + + copy(out, byteSlice) + + return out, nil +} + +func (tc *TypeConverter) sliceToHexString(from, to reflect.Type, data any) (any, error) { + bytes, err := AnySliceToBytes(data.([]any)) + if err != nil { + return nil, err + } + + return "0x" + hex.EncodeToString(bytes), nil +} + +func (tc *TypeConverter) base64ToBytes(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + bytes, err := base64.StdEncoding.DecodeString(str) + if err == nil { + return bytes, nil + } + + // Base64 decoding failed - return string unchanged so handler knows to pass to default + return str, nil +} + +func (tc *TypeConverter) stringToInt(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + val, err := strconv.ParseInt(str, base10, uint64Bits) + if err != nil { + return nil, fmt.Errorf("failed to parse string to int: %w", err) + } + + if overflowInt(to, val) { + return nil, fmt.Errorf("value %d overflows %v", val, to) + } + + return reflect.ValueOf(val).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) stringToUint(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + val, err := strconv.ParseUint(str, base10, uint64Bits) + if err != nil { + return nil, fmt.Errorf("failed to parse string to uint: %w", err) + } + + if overflowUint(to, val) { + return nil, fmt.Errorf("value %d overflows %v", val, to) + } + + return reflect.ValueOf(val).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) stringToFloat(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + val, err := strconv.ParseFloat(str, uint64Bits) + if err != nil { + return nil, fmt.Errorf("failed to parse string to float: %w", err) + } + + if overflowFloat(to, val) { + return nil, fmt.Errorf("value %f overflows %v", val, to) + } + + return reflect.ValueOf(val).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) stringToBytes(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + // Try numeric string first (convert to little-endian bytes) + if num, err := strconv.ParseUint(str, base10, uint64Bits); err == nil { + return numericToBytes(num), nil + } + + // Not a numeric string - return unchanged so other handlers (base64) can try + return str, nil +} + +func (tc *TypeConverter) stringToBigInt(from, to reflect.Type, data any) (any, error) { + str, ok := data.(string) + if !ok { + return data, nil + } + + result := new(big.Int) + _, success := result.SetString(str, base10) + if !success { + return nil, fmt.Errorf("cannot parse string %s as big.Int", str) + } + + return result, nil +} + +func (tc *TypeConverter) boolToInt(from, to reflect.Type, data any) (any, error) { + boolValue, ok := data.(bool) + if !ok { + return data, nil + } + + if boolValue { + return reflect.ValueOf(1).Convert(to).Interface(), nil + } + + return reflect.ValueOf(0).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) boolToUint(from, to reflect.Type, data any) (any, error) { + boolValue, ok := data.(bool) + if !ok { + return data, nil + } + + if boolValue { + return reflect.ValueOf(1).Convert(to).Interface(), nil + } + + return reflect.ValueOf(0).Convert(to).Interface(), nil +} + +func (tc *TypeConverter) boolToBigInt(from, to reflect.Type, data any) (any, error) { + boolValue, ok := data.(bool) + if !ok { + return data, nil + } + + if boolValue { + return big.NewInt(1), nil + } + + return big.NewInt(0), nil +} + +func (tc *TypeConverter) sliceToSlice(from, to reflect.Type, data any) (any, error) { + sourceSlice := reflect.ValueOf(data) + targetSlice := reflect.MakeSlice(to, sourceSlice.Len(), sourceSlice.Cap()) + + for i := range sourceSlice.Len() { + sourceElem := sourceSlice.Index(i).Interface() + targetElem := reflect.New(to.Elem()).Interface() + + if err := DecodeSuiJsonValue(sourceElem, targetElem); err != nil { + return nil, fmt.Errorf("failed to decode array element at index %d: %w", i, err) + } + + targetSlice.Index(i).Set(reflect.ValueOf(targetElem).Elem()) + } + + return targetSlice.Interface(), nil +} + +// float64ToUint converts float64 to uint types (JSON unmarshals numbers as float64) +func (tc *TypeConverter) float64ToUint(from, to reflect.Type, data any) (any, error) { + floatVal, ok := data.(float64) + if !ok { + return data, nil + } + + uintVal := uint64(floatVal) + if overflowUint(to, uintVal) { + return nil, fmt.Errorf("value %d overflows %v", uintVal, to) + } + + return reflect.ValueOf(uintVal).Convert(to).Interface(), nil +} + +// float64ToInt converts float64 to int types +func (tc *TypeConverter) float64ToInt(from, to reflect.Type, data any) (any, error) { + floatVal, ok := data.(float64) + if !ok { + return data, nil + } + + intVal := int64(floatVal) + if overflowInt(to, intVal) { + return nil, fmt.Errorf("value %d overflows %v", intVal, to) + } + + return reflect.ValueOf(intVal).Convert(to).Interface(), nil +} + +// jsonNumberToUint converts json.Number to uint types +func (tc *TypeConverter) jsonNumberToUint(from, to reflect.Type, data any) (any, error) { + jsonNum, ok := data.(json.Number) + if !ok { + return data, nil + } + + intVal, err := jsonNum.Int64() + if err != nil { + return nil, fmt.Errorf("failed to parse JSON number: %w", err) + } + + if intVal < 0 { + return nil, fmt.Errorf("cannot convert negative value %d to uint", intVal) + } + + uintVal := uint64(intVal) + if overflowUint(to, uintVal) { + return nil, fmt.Errorf("value %d overflows %v", uintVal, to) + } + + return reflect.ValueOf(uintVal).Convert(to).Interface(), nil +} + +// jsonNumberToInt converts json.Number to int types +func (tc *TypeConverter) jsonNumberToInt(from, to reflect.Type, data any) (any, error) { + jsonNum, ok := data.(json.Number) + if !ok { + return data, nil + } + + intVal, err := jsonNum.Int64() + if err != nil { + return nil, fmt.Errorf("failed to parse JSON number: %w", err) + } + + if overflowInt(to, intVal) { + return nil, fmt.Errorf("value %d overflows %v", intVal, to) + } + + return reflect.ValueOf(intVal).Convert(to).Interface(), nil +} + +// bytesToUint converts []byte to uint types (little-endian) +func (tc *TypeConverter) bytesToUint(from, to reflect.Type, data any) (any, error) { + bytes, ok := data.([]byte) + if !ok { + return data, nil + } + + if len(bytes) == 0 { + return nil, fmt.Errorf("empty byte array cannot be converted to numeric value") + } + + var result uint64 + // Process bytes in little-endian order + for i := 0; i < len(bytes) && i < 8; i++ { + result |= uint64(bytes[i]) << (8 * i) + } + + if overflowUint(to, result) { + return nil, fmt.Errorf("value %d overflows %v", result, to) + } + + return reflect.ValueOf(result).Convert(to).Interface(), nil +} + +// sliceAnyToUint converts []any to uint types (converts to bytes first) +func (tc *TypeConverter) sliceAnyToUint(from, to reflect.Type, data any) (any, error) { + anySlice, ok := data.([]any) + if !ok { + return data, nil + } + + bytes, err := AnySliceToBytes(anySlice) + if err != nil { + return nil, fmt.Errorf("failed to convert slice to bytes: %w", err) + } + + return tc.bytesToUint(reflect.TypeOf(bytes), to, bytes) +} + +// handleFloat64 handles float64 conversions +func (tc *TypeConverter) handleFloat64(floatVal float64, to reflect.Type) (result any, err error, handled bool) { + switch to.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if fn, ok := tc.converters["float64_to_uint"]; ok { + result, err = fn(reflect.TypeOf(floatVal), to, floatVal) + return result, err, true + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if fn, ok := tc.converters["float64_to_int"]; ok { + result, err = fn(reflect.TypeOf(floatVal), to, floatVal) + return result, err, true + } + } + + return nil, nil, false +} + +// handleJSONNumber handles json.Number conversions +func (tc *TypeConverter) handleJSONNumber(jsonNum json.Number, to reflect.Type) (result any, err error, handled bool) { + switch to.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if fn, ok := tc.converters["json_number_to_uint"]; ok { + result, err = fn(reflect.TypeOf(jsonNum), to, jsonNum) + return result, err, true + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if fn, ok := tc.converters["json_number_to_int"]; ok { + result, err = fn(reflect.TypeOf(jsonNum), to, jsonNum) + return result, err, true + } + } + + return nil, nil, false +} + +// handleBytesToNumeric handles conversions from []byte or []any to numeric types +func (tc *TypeConverter) handleBytesToNumeric(from, to reflect.Type, data any) (result any, err error, handled bool) { + if from.Kind() == reflect.Slice && from.Elem().Kind() == reflect.Uint8 { + // []byte to numeric + if fn, ok := tc.converters["bytes_to_uint"]; ok { + result, err = fn(from, to, data) + return result, err, true + } + } + + if from.Kind() == reflect.Slice && from.Elem().Kind() == reflect.Interface { + // []any to numeric + if fn, ok := tc.converters["slice_any_to_uint"]; ok { + result, err = fn(from, to, data) + return result, err, true + } + } + + return nil, nil, false +} + +// isNumericTarget checks if the target type is a numeric type +func isNumericTarget(to reflect.Type) bool { + switch to.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + } + return false +} + +// Global type converter instance for package-wide use (lazy initialization) +var defaultTypeConverter *TypeConverter + +// getDefaultTypeConverter returns the global type converter, initializing it if necessary +func getDefaultTypeConverter() *TypeConverter { + if defaultTypeConverter == nil { + defaultTypeConverter = NewTypeConverter() + } + return defaultTypeConverter +} + +// UnifiedTypeConverterHook is a mapstructure hook that uses the type converter registry +func UnifiedTypeConverterHook(from, to reflect.Type, data any) (any, error) { + // Skip if types are the same + if from == to { + return data, nil + } + + // Handle single-field struct case only for primitive source types + // (string, bool, numeric types) to avoid interfering with map-to-struct decoding + if to.Kind() == reflect.Struct && to.NumField() == 1 { + switch from.Kind() { + case reflect.String, reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, + reflect.Slice, reflect.Array: + return handleSingleFieldStruct(to, data, DecodeSuiJsonValue) + } + } + + // Use the global converter + return getDefaultTypeConverter().Convert(from, to, data) +} + +func BytesToAnySlice(b []byte) []any { + result := make([]any, len(b)) + for i, v := range b { + result[i] = v + } + return result +} diff --git a/relayer/codec/type_converters_test.go b/codec/type_converters_test.go similarity index 100% rename from relayer/codec/type_converters_test.go rename to codec/type_converters_test.go diff --git a/codec/types.go b/codec/types.go new file mode 100644 index 000000000..22d8d5601 --- /dev/null +++ b/codec/types.go @@ -0,0 +1,149 @@ +package codec + +import ( + "errors" + "math/big" + + "github.com/block-vision/sui-go-sdk/models" +) + +var AccountZero = make([]byte, 32) + +type PTBCommandDependency struct { + CommandIndex uint16 + ResultIndex *uint16 +} + +// PointerTag defines the structured format for pointer tags used in chain reader. +// Pointer tags specify how to derive object IDs from pointer objects stored on-chain. +type PointerTag struct { + // Module name containing the pointer object (e.g. "state_object", "offramp", "counter") + Module string `json:"module"` + // PointerName is the object type to search for (e.g. "CCIPObjectRefPointer", "OffRampStatePointer") + PointerName string `json:"pointerName"` + // FieldName is OPTIONAL and NOT USED by the implementation. The parent field name is automatically + // looked up from the global common.PointerConfigs registry based on the PointerName. + // This field exists for backward compatibility or future implementations to override static code but is currently ignored. + FieldName string `json:"fieldName,omitempty"` + // DerivationKey is the key used to derive the child object ID from the parent object ID (e.g. "CCIPObjectRef", "CCIP_OWNABLE") + DerivationKey string `json:"derivationKey"` + // PackageID is the package ID for the Pointer object if it differs from the calling contract's package ID + // This is used for cross-package pointer dependencies (e.g. offramp package depending on CCIP package CCIPObjectRef) + // If empty, the calling contract's package ID is used + PackageID string `json:"packageId,omitempty"` +} + +func (p PointerTag) Validate() error { + if p.Module == "" { + return errors.New("PointerTag.Module is required") + } + if p.PointerName == "" { + return errors.New("PointerTag.Pointer is required") + } + // FieldName is optional - it's looked up from common.PointerConfigs + if p.DerivationKey == "" { + return errors.New("PointerTag.DerivationKey is required") + } + return nil +} + +// SuiFunctionParam defines a parameter for a Sui function call +type SuiFunctionParam struct { + // Name of the parameter + Name string + // PointerTag (optional) specify how to derive object IDs from pointer objects stored on-chain. + PointerTag *PointerTag + // Type of the parameter (e.g., "u64", "String", "vector", "ptb_dependency") + Type string + // IsMutable specifies if the object is mutable or not (optional - defaults to true) + IsMutable *bool + // IsGeneric specifies if the parameter is a generic argument + GenericType *string + // Whether the parameter is required + Required bool + // Default value to use if not provided + DefaultValue any + // Result from a previous PTB Command (optional). It is used for expressive construction of PTB commands + PTBDependency *PTBCommandDependency + // GenericDependency maps to internal helpers for fetching an unknown generic type required by the parameter + GenericDependency *string +} + +type SuiPTBCommandType string + +const ( + SuiPTBCommandMoveCall SuiPTBCommandType = "move_call" + SuiPTBCommandPublish SuiPTBCommandType = "publish" + SuiPTBCommandTransfer SuiPTBCommandType = "transfer" +) + +// OCRConfigSet event data +type ConfigSet struct { + OcrPluginType byte + ConfigDigest []byte + Signers [][]byte + // this is a list of addresses, we can treat them as strings + Transmitters []string + BigF byte +} + +// SourceChainConfigSet event data +type SourceChainConfigSet struct { + SourceChainSelector uint64 + SourceChainConfig SourceChainConfig +} + +// SourceChainConfig event data +type SourceChainConfig struct { + Router string + IsEnabled bool + MinSeqNr uint64 + IsRMNVerificationDisabled bool + OnRamp []byte +} + +// ExecutionReport event data +type ExecutionReport struct { + SourceChainSelector uint64 + Message Any2SuiRampMessage + OffchainTokenData [][]byte + Proofs [][]byte +} + +// RampMessageHeader event data +type RampMessageHeader struct { + MessageID []byte + SourceChainSelector uint64 + DestChainSelector uint64 + SequenceNumber uint64 + Nonce uint64 +} + +// Any2SuiTokenTransfer event data +type Any2SuiTokenTransfer struct { + SourcePoolAddress []byte + DestTokenAddress models.SuiAddress + DestGasAmount uint32 + ExtraData []byte + Amount *big.Int +} + +// Any2SuiRampMessage event data +type Any2SuiRampMessage struct { + Header RampMessageHeader + Sender []byte + Data []byte + Receiver models.SuiAddress + GasLimit *big.Int + TokenReceiver models.SuiAddressBytes + TokenAmounts []Any2SuiTokenTransfer +} + +// ExecutionStateChanged event data +type ExecutionStateChanged struct { + SourceChainSelector uint64 `json:"source_chain_selector"` + SequenceNumber uint64 `json:"sequence_number"` + MessageId []byte `json:"message_id"` + MessageHash []byte `json:"message_hash"` + State byte `json:"state"` +} From 8c8d6f976d1542e47043d428f9a2a55acc4634dc Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 2 Jul 2026 10:31:31 -0500 Subject: [PATCH 2/8] deprecate relayer/codec & relayer/chainreader/loop --- deployment/go.mod | 1 + deployment/go.sum | 2 + go.mod | 1 + go.sum | 2 + integration-tests/go.mod | 1 + integration-tests/go.sum | 2 + relayer/chainreader/loop/loop_reader.go | 187 +----- relayer/codec/bcs_converter.go | 250 +------- relayer/codec/decoder.go | 635 ++----------------- relayer/codec/encoder.go | 368 +---------- relayer/codec/type_converters.go | 781 +----------------------- relayer/codec/types.go | 161 +---- scripts/go.sum | 2 + 13 files changed, 138 insertions(+), 2255 deletions(-) diff --git a/deployment/go.mod b/deployment/go.mod index a8c1840cf..a70ebb5e1 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -139,6 +139,7 @@ require ( github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect + github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad // indirect github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect diff --git a/deployment/go.sum b/deployment/go.sum index fe3bae7b7..48e74c14b 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -623,6 +623,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= diff --git a/go.mod b/go.mod index 3c38b9bf3..53b5f5d21 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 + github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad github.com/stretchr/testify v1.11.1 github.com/test-go/testify v1.1.4 go.opentelemetry.io/otel v1.44.0 diff --git a/go.sum b/go.sum index 2225de6a4..2a17f280a 100644 --- a/go.sum +++ b/go.sum @@ -308,6 +308,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 27c59c671..127365d4f 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -141,6 +141,7 @@ require ( github.com/smartcontractkit/chainlink-protos/job-distributor v0.19.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect + github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad // indirect github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index a74375a07..1a7d6704f 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -625,6 +625,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5 h1:EiQx0LCPzxlfO9piSPeMCVSZAnp/BxAsPIGh/jBal18= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= diff --git a/relayer/chainreader/loop/loop_reader.go b/relayer/chainreader/loop/loop_reader.go index 97eedc9dd..94d285f29 100644 --- a/relayer/chainreader/loop/loop_reader.go +++ b/relayer/chainreader/loop/loop_reader.go @@ -1,191 +1,18 @@ package loop import ( - "context" - "encoding/json" - "fmt" - "reflect" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-common/pkg/types/query" - "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - - "github.com/smartcontractkit/chainlink-aptos/relayer/chainreader/loop" - "github.com/smartcontractkit/chainlink-sui/relayer/codec" + "github.com/smartcontractkit/chainlink-sui/codec/loop" ) -const ( - READ_COMPONENTS_COUNT = 3 -) +//go:fix inline +const READ_COMPONENTS_COUNT = loop.READ_COMPONENTS_COUNT +//Deprecated: use loop.NewLoopChainReader +// +//go:fix inline func NewLoopChainReader(log logger.Logger, reader types.ContractReader) types.ContractReader { - lrLogger := logger.Named(log, "LoopChainReader") - return &loopChainReader{logger: lrLogger, reader: reader, moduleAddresses: map[string]string{}} -} - -type loopChainReader struct { - services.Service - types.UnimplementedContractReader - logger logger.Logger - reader types.ContractReader - moduleAddresses map[string]string -} - -func (s *loopChainReader) Name() string { - return s.reader.Name() -} - -func (s *loopChainReader) Ready() error { - return s.reader.Ready() -} - -func (s *loopChainReader) HealthReport() map[string]error { - return s.reader.HealthReport() -} - -func (s *loopChainReader) Start(ctx context.Context) error { - return s.reader.Start(ctx) -} - -func (s *loopChainReader) Close() error { - return s.reader.Close() -} - -func (s *loopChainReader) GetLatestValue(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error { - convertedResult := []byte{} - - s.logger.Debugw("GetLatestValue params before serialization over LOOP", "params", params) - - jsonParamBytes, err := json.Marshal(params) - if err != nil { - return fmt.Errorf("failed to marshal params: %+w", err) - } - - err = s.reader.GetLatestValue(ctx, readIdentifier, confidenceLevel, &jsonParamBytes, &convertedResult) - if err != nil { - return fmt.Errorf("failed to call GetLatestValue over LOOP: %w", err) - } - - err = s.decodeGLVReturnValue(readIdentifier, convertedResult, returnVal) - if err != nil { - return fmt.Errorf("failed to decode GetLatestValue return value: %w", err) - } - - return nil -} - -func (s *loopChainReader) BatchGetLatestValues(ctx context.Context, request types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) { - convertedRequest := types.BatchGetLatestValuesRequest{} - for contract, requestBatch := range request { - convertedBatch := []types.BatchRead{} - for _, read := range requestBatch { - jsonParamBytes, err := json.Marshal(read.Params) - if err != nil { - return nil, fmt.Errorf("failed to marshal params: %+w", err) - } - convertedBatch = append(convertedBatch, types.BatchRead{ - ReadName: read.ReadName, - Params: jsonParamBytes, - ReturnVal: &[]byte{}, - }) - } - convertedRequest[contract] = convertedBatch - } - - result, err := s.reader.BatchGetLatestValues(ctx, convertedRequest) - if err != nil { - return nil, err - } - - convertedResult := types.BatchGetLatestValuesResult{} - for contract, resultBatch := range result { - requestBatch := request[contract] - convertedBatch := []types.BatchReadResult{} - for i, result := range resultBatch { - read := requestBatch[i] - resultValue, resultError := result.GetResult() - convertedResult := types.BatchReadResult{ReadName: result.ReadName} - if resultError == nil { - resultPointer := resultValue.(*[]byte) - err := s.decodeGLVReturnValue(result.ReadName, *resultPointer, read.ReturnVal) - if err != nil { - resultError = fmt.Errorf("failed to decode BatchGetLatestValue return value: %w", err) - } - } - convertedResult.SetResult(read.ReturnVal, resultError) - convertedBatch = append(convertedBatch, convertedResult) - } - convertedResult[contract] = convertedBatch - } - - s.logger.Debugw("BatchGetLatestValues result", "result", convertedResult) - - return convertedResult, nil -} - -func (s *loopChainReader) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) { - convertedExpressions, err := loop.SerializeExpressions(filter.Expressions) - if err != nil { - return nil, fmt.Errorf("failed to serialize QueryKey expressions: %w", err) - } - - s.logger.Debugw("QueryKey expressions after serialization over LOOP", "expressions", convertedExpressions) - - convertedFilter := query.KeyFilter{ - Key: filter.Key, - Expressions: convertedExpressions, - } - - sequences, err := s.reader.QueryKey(ctx, contract, convertedFilter, limitAndSort, &[]byte{}) - if err != nil { - return nil, fmt.Errorf("failed to call QueryKey over LOOP: %w", err) - } - - for i, sequence := range sequences { - jsonBytes := sequence.Data.(*[]byte) - jsonData := map[string]any{} - err := json.Unmarshal(*jsonBytes, &jsonData) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal LOOP sourced JSON event data (`%s`): %w", string(*jsonBytes), err) - } - - eventData := reflect.New(reflect.TypeOf(sequenceDataType).Elem()).Interface() - err = codec.DecodeSuiJsonValue(jsonData, eventData) - if err != nil { - return nil, fmt.Errorf("failed to decode LOOP sourced event data (`%s`) into a Sui value: %+w", string(*jsonBytes), err) - } - - sequences[i].Data = eventData - } - - return sequences, nil -} - -func (s *loopChainReader) Bind(ctx context.Context, bindings []types.BoundContract) error { - return s.reader.Bind(ctx, bindings) -} - -func (s *loopChainReader) Unbind(ctx context.Context, bindings []types.BoundContract) error { - // we ignore unbind errors, because if the LOOP plugin restarted, the binding would not exist. - _ = s.reader.Unbind(ctx, bindings) - - return nil -} - -func (s *loopChainReader) decodeGLVReturnValue(label string, jsonBytes []byte, returnVal any) error { - var result any - err := json.Unmarshal(jsonBytes, &result) - if err != nil { - return fmt.Errorf("failed to unmarshal %s GetLatestValue result (`%s`): %w", label, string(jsonBytes), err) - } - - err = codec.DecodeSuiJsonValue(result, returnVal) - if err != nil { - return fmt.Errorf("failed to decode %s GetLatestValue JSON value (`%s`) to %T: %w", label, string(jsonBytes), returnVal, err) - } - - return nil + return loop.NewLoopChainReader(log, reader) } diff --git a/relayer/codec/bcs_converter.go b/relayer/codec/bcs_converter.go index 6566fcd39..de01aeca0 100644 --- a/relayer/codec/bcs_converter.go +++ b/relayer/codec/bcs_converter.go @@ -1,241 +1,43 @@ package codec import ( - "fmt" - "strconv" - aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" + + "github.com/smartcontractkit/chainlink-sui/codec" ) -// BCSPrimitiveHandler defines a function that reads a primitive type from a BCS deserializer -type BCSPrimitiveHandler func(*aptosBCS.Deserializer) (any, error) +// Deprecated: use codec.BCSPrimitiveHandler +// +//go:fix inline +type BCSPrimitiveHandler = codec.BCSPrimitiveHandler -// BCSVectorHandler defines a function that reads a vector type from a BCS deserializer -type BCSVectorHandler func(*aptosBCS.Deserializer, uint64) (any, error) +// Deprecated: use codec.BCSVectorHandler +// +//go:fix inline +type BCSVectorHandler = codec.BCSVectorHandler -// BCSTypeConverter provides a registry-based approach to converting BCS types to JSON-compatible values -type BCSTypeConverter struct { - primitiveHandlers map[string]BCSPrimitiveHandler - vectorHandlers map[string]BCSVectorHandler -} +// Deprecated: use codec.BCSTypeConverter +// +//go:fix inline +type BCSTypeConverter = codec.BCSTypeConverter -// NewBCSTypeConverter creates a new BCS type converter with all standard Sui types registered +// Deprecated: use codec.NewBCSTypeConverter +// +//go:fix inline func NewBCSTypeConverter() *BCSTypeConverter { - c := &BCSTypeConverter{ - primitiveHandlers: make(map[string]BCSPrimitiveHandler), - vectorHandlers: make(map[string]BCSVectorHandler), - } - - // Register primitive type handlers - c.registerPrimitiveHandlers() - c.registerVectorHandlers() - - return c -} - -// registerPrimitiveHandlers registers all standard Sui primitive type handlers -func (c *BCSTypeConverter) registerPrimitiveHandlers() { - // U8 - return as-is - c.RegisterPrimitive("U8", func(d *aptosBCS.Deserializer) (any, error) { - return d.U8(), nil - }) - c.RegisterPrimitive("u8", func(d *aptosBCS.Deserializer) (any, error) { - return d.U8(), nil - }) - - // U16 - return as-is - c.RegisterPrimitive("U16", func(d *aptosBCS.Deserializer) (any, error) { - return d.U16(), nil - }) - c.RegisterPrimitive("u16", func(d *aptosBCS.Deserializer) (any, error) { - return d.U16(), nil - }) - - // U32 - return as-is - c.RegisterPrimitive("U32", func(d *aptosBCS.Deserializer) (any, error) { - return d.U32(), nil - }) - c.RegisterPrimitive("u32", func(d *aptosBCS.Deserializer) (any, error) { - return d.U32(), nil - }) - - // U64 - return as string for JSON compatibility - c.RegisterPrimitive("U64", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U64() - return strconv.FormatUint(value, base10), nil - }) - c.RegisterPrimitive("u64", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U64() - return strconv.FormatUint(value, base10), nil - }) - - // U128 - return as string for JSON compatibility - c.RegisterPrimitive("U128", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U128() - return value.String(), nil - }) - c.RegisterPrimitive("u128", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U128() - return value.String(), nil - }) - - // U256 - return as string for JSON compatibility - c.RegisterPrimitive("U256", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U256() - return value.String(), nil - }) - c.RegisterPrimitive("u256", func(d *aptosBCS.Deserializer) (any, error) { - value := d.U256() - return value.String(), nil - }) - - // Bool - return as-is - c.RegisterPrimitive("Bool", func(d *aptosBCS.Deserializer) (any, error) { - return d.Bool(), nil - }) - c.RegisterPrimitive("bool", func(d *aptosBCS.Deserializer) (any, error) { - return d.Bool(), nil - }) - - // Address - return as byte array - c.RegisterPrimitive("Address", func(d *aptosBCS.Deserializer) (any, error) { - addressBytesLen := 32 - return d.ReadFixedBytes(addressBytesLen), nil - }) - c.RegisterPrimitive("address", func(d *aptosBCS.Deserializer) (any, error) { - addressBytesLen := 32 - return d.ReadFixedBytes(addressBytesLen), nil - }) -} - -// registerVectorHandlers registers standard vector type handlers -func (c *BCSTypeConverter) registerVectorHandlers() { - // Vector - read as byte array - c.RegisterVector("U8", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - bytes := make([]byte, length) - for i := range length { - bytes[i] = d.U8() - } - return bytes, nil - }) - c.RegisterVector("u8", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - bytes := make([]byte, length) - for i := range length { - bytes[i] = d.U8() - } - return bytes, nil - }) - - // Vector - read as uint64 array - c.RegisterVector("U64", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - uint64s := make([]uint64, length) - for i := range length { - uint64s[i] = d.U64() - } - return uint64s, nil - }) - c.RegisterVector("u64", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - uint64s := make([]uint64, length) - for i := range length { - uint64s[i] = d.U64() - } - return uint64s, nil - }) - - // Vector
- read as address array - c.RegisterVector("Address", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - addresses := make([]any, length) - for i := range length { - addressBytesLen := 32 - addresses[i] = d.ReadFixedBytes(addressBytesLen) - } - return addresses, nil - }) - c.RegisterVector("address", func(d *aptosBCS.Deserializer, length uint64) (any, error) { - addresses := make([]any, length) - for i := range length { - addressBytesLen := 32 - addresses[i] = d.ReadFixedBytes(addressBytesLen) - } - return addresses, nil - }) -} - -// RegisterPrimitive registers a handler for a primitive type -func (c *BCSTypeConverter) RegisterPrimitive(typeName string, handler BCSPrimitiveHandler) { - c.primitiveHandlers[typeName] = handler -} - -// RegisterVector registers a handler for a vector type -func (c *BCSTypeConverter) RegisterVector(elementType string, handler BCSVectorHandler) { - c.vectorHandlers[elementType] = handler -} - -// DecodePrimitive decodes a primitive type using the registered handler -func (c *BCSTypeConverter) DecodePrimitive(deserializer *aptosBCS.Deserializer, primitiveType string) (any, error) { - handler, ok := c.primitiveHandlers[primitiveType] - if !ok { - return nil, fmt.Errorf("unsupported primitive type: %s", primitiveType) - } - return handler(deserializer) -} - -// DecodeVector decodes a vector type using the registered handler -func (c *BCSTypeConverter) DecodeVector(deserializer *aptosBCS.Deserializer, elementType string) (any, error) { - length := deserializer.Uleb128() - - handler, ok := c.vectorHandlers[elementType] - if !ok { - // Fall back to generic primitive vector handling - primitiveHandler, primitiveOk := c.primitiveHandlers[elementType] - if !primitiveOk { - return nil, fmt.Errorf("unsupported vector element type: %s", elementType) - } - - // Generic vector of primitives - result := make([]any, length) - for i := range length { - value, err := primitiveHandler(deserializer) - if err != nil { - return nil, fmt.Errorf("failed to decode vector element at index %d: %w", i, err) - } - result[i] = value - } - return result, nil - } - - return handler(deserializer, uint64(length)) -} - -// HasPrimitiveHandler checks if a primitive type handler is registered -func (c *BCSTypeConverter) HasPrimitiveHandler(typeName string) bool { - _, ok := c.primitiveHandlers[typeName] - return ok -} - -// HasVectorHandler checks if a vector type handler is registered -func (c *BCSTypeConverter) HasVectorHandler(elementType string) bool { - _, ok := c.vectorHandlers[elementType] - return ok -} - -// Global BCS converter instance for package-wide use (lazy initialization) -var defaultBCSConverter *BCSTypeConverter - -// getDefaultBCSConverter returns the global BCS converter, initializing it if necessary -func getDefaultBCSConverter() *BCSTypeConverter { - if defaultBCSConverter == nil { - defaultBCSConverter = NewBCSTypeConverter() - } - return defaultBCSConverter + return codec.NewBCSTypeConverter() } -// DecodeBCSPrimitive decodes a BCS primitive type using the default converter +// Deprecated: codec.DecodeBCSPrimitive +// +//go:fix inline func DecodeBCSPrimitive(deserializer *aptosBCS.Deserializer, primitiveType string) (any, error) { - return getDefaultBCSConverter().DecodePrimitive(deserializer, primitiveType) + return codec.DecodeBCSPrimitive(deserializer, primitiveType) } -// DecodeBCSVector decodes a BCS vector type using the default converter +// Deprecated: codec.DecodeBCSVector +// +//go:fix inline func DecodeBCSVector(deserializer *aptosBCS.Deserializer, elementType string) (any, error) { - return getDefaultBCSConverter().DecodeVector(deserializer, elementType) + return codec.DecodeBCSVector(deserializer, elementType) } diff --git a/relayer/codec/decoder.go b/relayer/codec/decoder.go index 10f6cd09e..686533602 100644 --- a/relayer/codec/decoder.go +++ b/relayer/codec/decoder.go @@ -1,632 +1,71 @@ package codec import ( - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "math" - "reflect" - "strconv" - "strings" - aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" - "github.com/block-vision/sui-go-sdk/models" - "github.com/smartcontractkit/chainlink-sui/bindings/bind" + "github.com/smartcontractkit/chainlink-sui/codec" ) -const ( - // Bit and byte constants - byteSize = 8 - uint8Bits = 8 - uint64Bits = 64 - bits128 = 128 - bits256 = 256 - - // Number bases - base10 = 10 - base16 = 16 - base2 = 2 - - // Response parsing constants - maxByteValue = 255 - - // proofWireBytes is the fixed on-wire size of each merkle proof element. - proofWireBytes = 32 - - // tokenTransferMinWireBytes is the minimum BCS size of Any2SuiTokenTransfer: - // uleb128(0) source pool + 32 dest token + 4 dest gas + uleb128(0) extra data + 32 amount. - tokenTransferMinWireBytes = 70 -) -// DecodeSuiJsonValue decodes Sui JSON response data into the provided target. +// Deprecated: use codec.DecodeSuiJsonValue +// +//go:fix inline func DecodeSuiJsonValue(data any, target any) error { - return bind.DecodeJSONReturn(data, target) + return codec.DecodeSuiJsonValue(data, target) } -// ConvertBase64StringsToHex walks arbitrary JSON-like structures and converts any -// base64-encoded strings into 0x-prefixed hex strings, preserving []byte slices. +// Deprecated: use codec.ConvertBase64StringsToHex +// +//go:fix inline func ConvertBase64StringsToHex(data any) any { - switch v := data.(type) { - case nil: - return nil - case string: - // check if the string is entirely numeric - if _, err := strconv.ParseUint(v, 10, 64); err == nil { - return v - } - - decoded, err := base64.StdEncoding.DecodeString(v) - if err == nil && len(decoded) > 0 { - return "0x" + hex.EncodeToString(decoded) - } - return v - case json.RawMessage: - var inner any - if err := json.Unmarshal(v, &inner); err != nil { - return v - } - return ConvertBase64StringsToHex(inner) - case map[string]any: - result := make(map[string]any, len(v)) - for key, value := range v { - result[key] = ConvertBase64StringsToHex(value) - } - return result - case []any: - result := make([]any, len(v)) - for i, value := range v { - result[i] = ConvertBase64StringsToHex(value) - } - return result - default: - rv := reflect.ValueOf(data) - if rv.Kind() == reflect.Slice { - // Preserve []byte and other byte slices as-is - if rv.Type().Elem().Kind() == reflect.Uint8 { - return data - } - - result := make([]any, rv.Len()) - for i := 0; i < rv.Len(); i++ { - result[i] = ConvertBase64StringsToHex(rv.Index(i).Interface()) - } - return result - } - - if rv.Kind() == reflect.Map && rv.Type().Key().Kind() == reflect.String { - result := make(map[string]any, rv.Len()) - iter := rv.MapRange() - for iter.Next() { - result[iter.Key().String()] = ConvertBase64StringsToHex(iter.Value().Interface()) - } - return result - } - } - - return data + return codec.ConvertBase64StringsToHex(data) } -// DecodeSuiStructToJSON decodes a Sui struct into a JSON object -// using the normalized struct and the result +// Deprecated: use codec.DecodeSuiStructToJSON +// +//go:fix inline func DecodeSuiStructToJSON(normalizedStructs map[string]any, identifier string, bcsDecoder *aptosBCS.Deserializer) (map[string]any, error) { - jsonResult := make(map[string]any) - - normalizedStruct, ok := normalizedStructs[identifier].(map[string]any) - if !ok { - return nil, fmt.Errorf("struct with identifier '%s' not found in normalized structs", identifier) - } - - fields, ok := normalizedStruct["fields"].([]any) - if !ok { - return nil, fmt.Errorf("fields not found for struct '%s'", identifier) - } - - for _, field := range fields { - fieldMap, ok := field.(map[string]any) - if !ok { - continue - } - - fieldName, ok := fieldMap["name"].(string) - if !ok { - continue - } - - fieldType := fieldMap["type"] - - // Handle different field types based on the new format - switch v := fieldType.(type) { - case string: - // Primitive types like "U64", "Bool", "Address" - value, err := getDefaultBCSConverter().DecodePrimitive(bcsDecoder, v) - if err != nil { - return nil, fmt.Errorf("failed to decode primitive field %s: %w", fieldName, err) - } - jsonResult[fieldName] = value - - case map[string]any: - if vectorType, exists := v["Vector"]; exists { - // Vector type - decodedVector, err := decodeVectorField(bcsDecoder, vectorType, normalizedStructs) - if err != nil { - return nil, fmt.Errorf("failed to decode vector field %s: %w", fieldName, err) - } - jsonResult[fieldName] = decodedVector - } else if structType, exists := v["Struct"]; exists { - // Struct type - structMap, ok := structType.(map[string]any) - if !ok { - return nil, fmt.Errorf("invalid struct type for field %s", fieldName) - } - structName, ok := structMap["name"].(string) - if !ok { - return nil, fmt.Errorf("struct name not found for field %s", fieldName) - } - - // Special case for String struct - it's a primitive type in Sui - if structName == "String" { - jsonResult[fieldName] = bcsDecoder.ReadString() - } else { - inner, err := DecodeSuiStructToJSON(normalizedStructs, structName, bcsDecoder) - if err != nil { - return nil, fmt.Errorf("failed to decode struct field %s: %w", fieldName, err) - } - jsonResult[fieldName] = inner - } - } - } - } - - return jsonResult, nil -} - -func decodeVectorField(bcsDecoder *aptosBCS.Deserializer, vectorType any, normalizedStructs map[string]any) (any, error) { - // Read the length of the vector first - vectorLength := bcsDecoder.Uleb128() - - switch v := vectorType.(type) { - case string: - // Try to use the BCS converter for registered vector types - if getDefaultBCSConverter().HasVectorHandler(v) { - // Use the registered vector handler which will handle the length internally - // We need to "rewind" by putting the length back since the handler expects to read it - // Actually, the handler receives the length as a parameter, so we're good - handler, _ := getDefaultBCSConverter().vectorHandlers[v] - return handler(bcsDecoder, uint64(vectorLength)) - } - - // Fall back to generic primitive vector handling - if getDefaultBCSConverter().HasPrimitiveHandler(v) { - primitiveVector := make([]any, vectorLength) - for i := range vectorLength { - value, err := getDefaultBCSConverter().DecodePrimitive(bcsDecoder, v) - if err != nil { - return nil, fmt.Errorf("failed to decode primitive vector element at index %d: %w", i, err) - } - primitiveVector[i] = value - } - return primitiveVector, nil - } - - return nil, fmt.Errorf("unsupported vector element type: %s", v) - - case map[string]any: - if innerVectorType, exists := v["Vector"]; exists { - // This is vector> - recursively decode each inner vector - outerVector := make([]any, vectorLength) - for i := range vectorLength { - innerResult, err := decodeVectorField(bcsDecoder, innerVectorType, normalizedStructs) - if err != nil { - return nil, fmt.Errorf("failed to decode inner vector at index %d: %w", i, err) - } - outerVector[i] = innerResult - } - - return outerVector, nil - } else if structType, exists := v["Struct"]; exists { - // This is vector - decode each struct - structVector := make([]any, vectorLength) - structMap, ok := structType.(map[string]any) - if !ok { - return nil, fmt.Errorf("invalid struct type in vector") - } - structName, ok := structMap["name"].(string) - if !ok { - return nil, fmt.Errorf("struct name not found in vector element") - } - - // this is a special case where strings are defined as a struct in Sui normalized module structs definition - if structName == "String" { - vecOfStrings := make([]any, vectorLength) - for i := range vectorLength { - vecOfStrings[i] = bcsDecoder.ReadString() - } - - return vecOfStrings, nil - } - - for i := range vectorLength { - structResult, err := DecodeSuiStructToJSON(normalizedStructs, structName, bcsDecoder) - if err != nil { - return nil, fmt.Errorf("failed to decode struct at index %d: %w", i, err) - } - structVector[i] = structResult - } - - return structVector, nil - } - } - - return nil, fmt.Errorf("unsupported vector type: %v", vectorType) + return codec.DecodeSuiStructToJSON(normalizedStructs, identifier, bcsDecoder) } +// Deprecated: use codec.DecodeSuiPrimative +// +//go:fix inline func DecodeSuiPrimative(bcsDecoder *aptosBCS.Deserializer, primativeType string) (any, error) { - // Try to decode as primitive using the BCS converter registry - converter := getDefaultBCSConverter() - if converter.HasPrimitiveHandler(primativeType) { - return converter.DecodePrimitive(bcsDecoder, primativeType) - } - - // Handle vector types - if strings.HasPrefix(primativeType, "vector<") && strings.HasSuffix(primativeType, ">") { - innerType := strings.TrimSuffix(strings.TrimPrefix(primativeType, "vector<"), ">") - - // Handle simple vector types - if converter.HasVectorHandler(innerType) || converter.HasPrimitiveHandler(innerType) { - return decodeVectorField(bcsDecoder, innerType, nil) - } - - // Handle nested vector types (e.g., vector>) - if innerType == "vector" || innerType == "vector" { - return decodeVectorField(bcsDecoder, map[string]any{"Vector": "U8"}, nil) - } - } - - return nil, fmt.Errorf("unsupported BCS primitive type: %s", primativeType) + return codec.DecodeSuiPrimative(bcsDecoder, primativeType) } -// DecodeVectorOfStructs decodes a vector of structs from BCS bytes -// vectorType should be in format "vector<0xpackage::module::StructName>" +// Deprecated: use codec.DecodeVectorOfStructs +// +//go:fix inline func DecodeVectorOfStructs(bcsDecoder *aptosBCS.Deserializer, vectorType string, normalizedStructs map[string]any) (any, error) { - // Check if it's actually a vector type - if !strings.HasPrefix(vectorType, "vector<") || !strings.HasSuffix(vectorType, ">") { - return nil, fmt.Errorf("not a vector type: %s", vectorType) - } - - // Extract inner type - innerType := strings.TrimSuffix(strings.TrimPrefix(vectorType, "vector<"), ">") - - // Check if inner type is a struct (has 3 parts when split by ::) - structParts := strings.Split(innerType, "::") - if len(structParts) != 3 { - return nil, fmt.Errorf("inner type is not a struct: %s", innerType) - } - - structName := structParts[2] - - // Create vector type definition compatible with decodeVectorField - vectorTypedef := map[string]any{ - "Struct": map[string]any{ - "name": structName, - }, - } - - return decodeVectorField(bcsDecoder, vectorTypedef, normalizedStructs) + return codec.DecodeVectorOfStructs(bcsDecoder, vectorType, normalizedStructs) } -// numericToBytes converts a number to byte slice (little-endian) -// Used by type_converters.go -func numericToBytes(num uint64) []byte { - bytes := make([]byte, uint64Bits/uint8Bits) - for i := range uint8Bits { - bytes[i] = byte(num >> (i * uint8Bits)) - } - // Remove trailing zeros - for len(bytes) > 1 && bytes[len(bytes)-1] == 0 { - bytes = bytes[:len(bytes)-1] - } - - return bytes -} - -// AnySliceToBytes converts slice of interface{} to byte slice +// Deprecated: use codec.AnySliceToBytes +// +//go:fix inline func AnySliceToBytes(src []any) ([]byte, error) { - dst := make([]byte, len(src)) - for i, v := range src { - //nolint:exhaustive - switch x := v.(type) { - case uint8: - dst[i] = x - case int: - if x < 0 || x > maxByteValue { - return nil, fmt.Errorf("element %d: int %d out of byte range", i, x) - } - dst[i] = byte(x) - case uint: - if x > maxByteValue { - return nil, fmt.Errorf("element %d: uint %d out of byte range", i, x) - } - dst[i] = byte(x) - case float64: - if x > maxByteValue { - return nil, fmt.Errorf("element %d: float64 %f out of byte range", i, x) - } - dst[i] = byte(x) - default: - return nil, fmt.Errorf("element %d: unsupported type %T", i, v) - } - } - - return dst, nil -} - -// handleSingleFieldStruct processes structs with single fields -// This is kept here for backward compatibility but the main implementation is in type_converters.go -func handleSingleFieldStruct(t reflect.Type, data any, decodeFn func(any, any) error) (any, error) { - field := t.Field(0) - newStructVal := reflect.New(t).Elem() - fieldPtr := newStructVal.Field(0).Addr().Interface() - - if err := decodeFn(data, fieldPtr); err != nil { - return nil, fmt.Errorf("failed decoding for single-field struct %v field %s (%v): %w", - t, field.Name, field.Type, err) - } - - return newStructVal.Interface(), nil -} - -// Overflow checking functions -func overflowFloat(t reflect.Type, x float64) bool { - //nolint:exhaustive - switch t.Kind() { - case reflect.Float32: - return overflowFloat32(x) - case reflect.Float64: - return false - default: - panic("reflect: OverflowFloat of non-float type " + t.String()) - } -} - -func overflowFloat32(x float64) bool { - if x < 0 { - x = -x - } - - return math.MaxFloat32 < x && x <= math.MaxFloat64 -} - -func overflowInt(t reflect.Type, x int64) bool { - //nolint:exhaustive - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bitSize := t.Size() * uint8Bits - trunc := (x << (uint64Bits - bitSize)) >> (uint64Bits - bitSize) - - return x != trunc - default: - panic("reflect: OverflowInt of non-int type " + t.String()) - } -} - -func overflowUint(t reflect.Type, x uint64) bool { - //nolint:exhaustive - switch t.Kind() { - case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bitSize := t.Size() * uint8Bits - trunc := (x << (uint64Bits - bitSize)) >> (uint64Bits - bitSize) - - return x != trunc - default: - panic("reflect: OverflowUint of non-uint type " + t.String()) - } + return codec.AnySliceToBytes(src) } +// Deprecated: use codec.DeserializeExecutionReport +// +//go:fix inline func DeserializeExecutionReport(data []byte) (*ExecutionReport, error) { - deserializer := aptosBCS.NewDeserializer(data) - - // 1. Read source_chain_selector (u64) - sourceChainSelector := deserializer.U64() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize sourceChainSelector: %w", err) - } - - // 2. Read message header - messageID := make([]byte, 32) - deserializer.ReadFixedBytesInto(messageID) - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize messageID: %w", err) - } - - headerSourceChain := deserializer.U64() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize headerSourceChain: %w", err) - } - - destChainSelector := deserializer.U64() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize destChainSelector: %w", err) - } - - sequenceNumber := deserializer.U64() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize sequenceNumber: %w", err) - } - - nonce := deserializer.U64() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize nonce: %w", err) - } - - if sourceChainSelector != headerSourceChain { - return nil, fmt.Errorf("source chain selector mismatch: %d != %d", sourceChainSelector, headerSourceChain) - } - - header := RampMessageHeader{ - MessageID: messageID, - SourceChainSelector: headerSourceChain, - DestChainSelector: destChainSelector, - SequenceNumber: sequenceNumber, - Nonce: nonce, - } - - // 3. Read sender (vector) - sender := deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize sender: %w", err) - } - - // 4. Read data (vector) - msgData := deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize data: %w", err) - } - - // 5. Read receiver (address) - receiver := deserializer.ReadFixedBytes(32) - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize receiver: %w", err) - } - - // 6. Read gas_limit (u256) - gasLimit := deserializer.U256() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize gas_limit: %w", err) - } - - tokenReceiver := [32]byte{} - deserializer.ReadFixedBytesInto(tokenReceiver[:]) - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize tokenReceiver: %w", err) - } - - // 7. Read token_amounts vector - tokenAmountsLen := deserializer.Uleb128() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize token_amounts length: %w", err) - } - remaining := deserializer.Remaining() - if remaining < 0 || uint64(tokenAmountsLen)*tokenTransferMinWireBytes > uint64(remaining) { - return nil, fmt.Errorf("failed to deserialize execution report: token_amounts length %d exceeds remaining %d bytes", tokenAmountsLen, remaining) - } - tokenAmounts := make([]Any2SuiTokenTransfer, tokenAmountsLen) - - for i := range tokenAmountsLen { - sourcePoolAddr := deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize sourcePoolAddr: %w", err) - } - - destToken := deserializer.ReadFixedBytes(32) - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize destToken: %w", err) - } - - destGas := deserializer.U32() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize destGas: %w", err) - } - extraData := deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize extraData: %w", err) - } - amount := deserializer.U256() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize amount: %w", err) - } - - tokenAmounts[i] = Any2SuiTokenTransfer{ - SourcePoolAddress: sourcePoolAddr, - DestTokenAddress: models.SuiAddress(hex.EncodeToString(destToken)), - DestGasAmount: destGas, - ExtraData: extraData, - Amount: &amount, - } - } - - message := Any2SuiRampMessage{ - Header: header, - Sender: sender, - Data: msgData, - Receiver: models.SuiAddress(hex.EncodeToString(receiver)), - GasLimit: &gasLimit, - TokenReceiver: models.SuiAddressBytes(tokenReceiver), - TokenAmounts: tokenAmounts, - } - - // 8. Read offchain_token_data (vector>) - offchainDataLen := deserializer.Uleb128() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize offchain_token_data length: %w", err) - } - if int(offchainDataLen) > deserializer.Remaining() { - return nil, fmt.Errorf("failed to deserialize execution report: offchain_token_data length %d exceeds remaining %d bytes", offchainDataLen, deserializer.Remaining()) - } - offchainData := make([][]byte, offchainDataLen) - - for i := range offchainDataLen { - offchainData[i] = deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize offchainData at index %d: %w", i, err) - } - } - - // 9. Read proofs (vector>) - proofsLen := deserializer.Uleb128() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize proofs length: %w", err) - } - remaining = deserializer.Remaining() - if remaining < 0 || uint64(proofsLen)*proofWireBytes > uint64(remaining) { - return nil, fmt.Errorf("failed to deserialize execution report: proofs length %d exceeds remaining %d bytes", proofsLen, remaining) - } - proofs := make([][]byte, proofsLen) - - for i := range proofsLen { - proofs[i] = deserializer.ReadFixedBytes(proofWireBytes) - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize proof at index %d: %w", i, err) - } - } - - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to deserialize execution report: %w", err) - } - - return &ExecutionReport{ - SourceChainSelector: sourceChainSelector, - Message: message, - OffchainTokenData: offchainData, - Proofs: proofs, - }, nil + return codec.DeserializeExecutionReport(data) } -// UnwrapBCSPureBytes decodes a BCS-encoded pure input value stored on-chain. -// Pure vector arguments are stored with a ULEB128 length prefix. +// Deprecated: use codec.UnwrapBCSPureBytes +// +//go:fix inline func UnwrapBCSPureBytes(pure []byte) ([]byte, error) { - if len(pure) == 0 { - return nil, errors.New("pure bytes are empty") - } - - deserializer := aptosBCS.NewDeserializer(pure) - unwrapped := deserializer.ReadBytes() - if err := deserializer.Error(); err != nil { - return nil, fmt.Errorf("failed to unwrap pure bytes: %w", err) - } - - return unwrapped, nil + return codec.UnwrapBCSPureBytes(pure) } -// DeserializeExecutionReportFromPure deserializes an execution report from a -// BCS-encoded pure input containing vector. +// Deprecated: use DeserializeExecutionReportFromPure +// +//go:fix inline func DeserializeExecutionReportFromPure(pure []byte) (*ExecutionReport, error) { - reportBytes, err := UnwrapBCSPureBytes(pure) - if err != nil { - return nil, err - } - - return DeserializeExecutionReport(reportBytes) + return codec.DeserializeExecutionReportFromPure(pure) } diff --git a/relayer/codec/encoder.go b/relayer/codec/encoder.go index cdae87709..86940d743 100644 --- a/relayer/codec/encoder.go +++ b/relayer/codec/encoder.go @@ -1,368 +1,10 @@ package codec -import ( - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "math/big" - "reflect" - "strconv" - "strings" - - "github.com/smartcontractkit/chainlink-sui/bindings/bind" -) - -const ( - // Type bounds - maxUint8 = 255 - maxUint16 = 65535 - maxUint32 = 4294967295 -) - -// Safe conversion functions to avoid lint issues -func safeUint8(val uint64) (uint8, error) { - if val > maxUint8 { - return 0, fmt.Errorf("value %d exceeds uint8 maximum", val) - } - - return uint8(val), nil -} - -func safeUint16(val uint64) (uint16, error) { - if val > maxUint16 { - return 0, fmt.Errorf("value %d exceeds uint16 maximum", val) - } - - return uint16(val), nil -} - -func safeUint32(val uint64) (uint32, error) { - if val > maxUint32 { - return 0, fmt.Errorf("value %d exceeds uint32 maximum", val) - } - - return uint32(val), nil -} +import "github.com/smartcontractkit/chainlink-sui/codec" +// Deprecated: use codec.EncodeToSuiValue +// +//go:fix inline func EncodeToSuiValue(typeName string, value any) (any, error) { - // Normalize fully-qualified Sui string type to logical "string" - if strings.Contains(typeName, "::string::String") { - typeName = "string" - } - - switch typeName { - case "address": - return encodeAddress(value) - case "object_id": - return encodeObject(value) - case "u8", "u16", "u32", "u64", "u128", "u256": - return encodeUint(typeName, value) - case "bool": - return encodeBool(value) - case "string": - return encodeString(value) - default: - if strings.HasPrefix(typeName, "0x") && strings.Contains(typeName, "::") { - // TODO: need to use go-bsc to encode this. Reference https://github.com/fardream/go-bcs/blob/main/bcs/encode_test.go - return nil, errors.New("struct types are not supported") - } - if strings.HasPrefix(typeName, "vector<") && strings.HasSuffix(typeName, ">") { - return encodeVector(typeName, value) - } - - return nil, fmt.Errorf("unsupported type: %s", typeName) - } -} - -func encodeObject(value any) (bind.Object, error) { - switch v := value.(type) { - case bind.Object: - return bind.Object{Id: v.Id}, nil - default: - return bind.Object{}, fmt.Errorf("cannot convert %T to object", value) - } -} - -func encodeAddress(value any) (string, error) { - switch v := value.(type) { - case string: - // Ensure it's a valid Sui address format - if !strings.HasPrefix(v, "0x") { - v = "0x" + v - } - - normalizedAddress, err := bind.ToSuiAddress(v) - if err != nil { - return "", fmt.Errorf("failed to convert address to Sui address: %w", err) - } - - return normalizedAddress, nil - case []byte: - stringAddr := "0x" + hex.EncodeToString(v) - - normalizedAddress, err := bind.ToSuiAddress(stringAddr) - if err != nil { - return "", fmt.Errorf("failed to convert address to Sui address: %w", err) - } - - return normalizedAddress, nil - default: - return "", fmt.Errorf("cannot convert %T to address", value) - } -} - -func encodeUint(typeName string, value any) (any, error) { - // First convert to a common intermediate type - var baseValue uint64 - var bigIntValue *big.Int - - switch v := value.(type) { - case int: - if v < 0 { - return nil, fmt.Errorf("cannot convert negative int %d to %s", v, typeName) - } - baseValue = uint64(v) - case int64: - if v < 0 { - return nil, fmt.Errorf("cannot convert negative int %d to %s", v, typeName) - } - baseValue = uint64(v) - case uint: - baseValue = uint64(v) - case uint64: - baseValue = v - case uint32: - baseValue = uint64(v) - case uint16: - baseValue = uint64(v) - case uint8: - baseValue = uint64(v) - case float64: - if v < 0 { - return nil, fmt.Errorf("cannot convert negative float %f to %s", v, typeName) - } - baseValue = uint64(v) - case json.Number: - // Handle JSON numbers properly - numStr := v.String() - if typeName == "u128" || typeName == "u256" { - bigIntValue = new(big.Int) - if _, ok := bigIntValue.SetString(numStr, base10); !ok { - return nil, fmt.Errorf("cannot convert json.Number %s to %s", v, typeName) - } - } else if strings.Contains(numStr, ".") { - f, err := v.Float64() - if err != nil { - return nil, fmt.Errorf("cannot convert json.Number %s to %s: %w", v, typeName, err) - } - baseValue = uint64(f) - } else { - // json parsing int64 for selector was going out of range - i, err := strconv.ParseUint(numStr, base10, 64) - if err != nil { - return nil, fmt.Errorf("cannot convert json.Number %s to %s: %w", v, typeName, err) - } - //nolint:gosec - // we assume safe conversion without negative numbers - baseValue = uint64(i) - } - case string: - // Handle big numbers that might come as strings - if typeName == "u128" || typeName == "u256" { - bigIntValue = new(big.Int) - _, ok := bigIntValue.SetString(v, base10) - if !ok { - return nil, fmt.Errorf("cannot convert string %s to %s", v, typeName) - } - } else { - i, err := strconv.ParseUint(v, base10, 64) - if err != nil { - return nil, fmt.Errorf("cannot convert string %s to %s: %w", v, typeName, err) - } - baseValue = i - } - case *big.Int: - bigIntValue = v - default: - return nil, fmt.Errorf("cannot convert %T to %s", value, typeName) - } - - // Now convert to the appropriate type based on typeName - switch typeName { - case "u8": - if bigIntValue != nil { - if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint8 { - return nil, fmt.Errorf("value %s too large for u8", bigIntValue.String()) - } - // Safe conversion after bounds check - return safeUint8(bigIntValue.Uint64()) - } - if baseValue > maxUint8 { - return nil, fmt.Errorf("value %d too large for u8", baseValue) - } - - return safeUint8(baseValue) - case "u16": - if bigIntValue != nil { - if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint16 { - return nil, fmt.Errorf("value %s too large for u16", bigIntValue.String()) - } - // Safe conversion after bounds check - return safeUint16(bigIntValue.Uint64()) - } - if baseValue > maxUint16 { - return nil, fmt.Errorf("value %d too large for u16", baseValue) - } - - return safeUint16(baseValue) - case "u32": - if bigIntValue != nil { - if !bigIntValue.IsUint64() || bigIntValue.Uint64() > maxUint32 { - return nil, fmt.Errorf("value %s too large for u32", bigIntValue.String()) - } - // Safe conversion after bounds check - return safeUint32(bigIntValue.Uint64()) - } - if baseValue > maxUint32 { - return nil, fmt.Errorf("value %d too large for u32", baseValue) - } - - return safeUint32(baseValue) - case "u64": - if bigIntValue != nil { - if !bigIntValue.IsUint64() { - return nil, fmt.Errorf("value %s too large for u64", bigIntValue.String()) - } - - return bigIntValue.Uint64(), nil - } - - return baseValue, nil - case "u128": - if bigIntValue != nil { - // Validate it fits in u128 (2^128 - 1) - maxVal := new(big.Int) - maxVal.Exp(big.NewInt(base2), big.NewInt(bits128), nil) - maxVal.Sub(maxVal, big.NewInt(1)) - if bigIntValue.Cmp(maxVal) > 0 { - return nil, fmt.Errorf("value %s too large for u128", bigIntValue.String()) - } - - return bigIntValue.String(), nil - } - - return strconv.FormatUint(baseValue, base10), nil - case "u256": - if bigIntValue != nil { - // Validate it fits in u256 (2^256 - 1) - maxVal := new(big.Int) - maxVal.Exp(big.NewInt(base2), big.NewInt(bits256), nil) - maxVal.Sub(maxVal, big.NewInt(1)) - if bigIntValue.Cmp(maxVal) > 0 { - return nil, fmt.Errorf("value %s too large for u256", bigIntValue.String()) - } - - return bigIntValue.String(), nil - } - - return strconv.FormatUint(baseValue, base10), nil - default: - return nil, fmt.Errorf("unsupported uint type: %s", typeName) - } -} - -func encodeBool(value any) (bool, error) { - switch v := value.(type) { - case bool: - return v, nil - case string: - b, err := strconv.ParseBool(v) - if err != nil { - return false, fmt.Errorf("cannot convert string %s to bool: %w", v, err) - } - - return b, nil - case int: - return v != 0, nil - case float64: - return v != 0, nil - default: - return false, fmt.Errorf("cannot convert %T to bool", value) - } -} - -func encodeString(value any) (string, error) { - switch v := value.(type) { - case string: - return v, nil - case []byte: - return string(v), nil - default: - return "", fmt.Errorf("cannot convert %T to string", value) - } -} - -func encodeVector(typeName string, value any) ([]any, error) { - // Extract the inner type, e.g., "vector" -> "string" - if !strings.HasPrefix(typeName, "vector<") || !strings.HasSuffix(typeName, ">") { - return nil, fmt.Errorf("invalid vector type: %s", typeName) - } - innerType := typeName[len("vector<") : len(typeName)-1] - - if value == nil { - return nil, fmt.Errorf("nil value cannot be encoded in encodeVector") - } - rv := reflect.ValueOf(value) - - if !rv.IsValid() { - return nil, fmt.Errorf("invalid reflect value for vector type %s in encodeVector", typeName) - } - kind := rv.Kind() - - // Use reflection to ensure 'value' is a slice or array - if kind != reflect.Slice && kind != reflect.Array { - return nil, fmt.Errorf("expected a slice/array for vector type %s, got %T", typeName, value) - } - - // Special handling for vector (byte arrays) - if innerType == "u8" { - // Handle []any from JSON unmarshaling - if interfaceSlice, ok := value.([]any); ok { - bytes := make([]byte, len(interfaceSlice)) - for i, item := range interfaceSlice { - if num, ok := item.(float64); ok { - if num < 0 || num > maxUint8 { - return nil, fmt.Errorf("invalid byte value at index %d: %f", i, num) - } - bytes[i] = byte(num) - } else if num, ok := item.(int); ok { - if num < 0 || num > maxUint8 { - return nil, fmt.Errorf("invalid byte value at index %d: %d", i, num) - } - bytes[i] = byte(num) - } else { - return nil, fmt.Errorf("invalid byte value at index %d: %T", i, item) - } - } - - return []any{bytes}, nil - } - // Handle []byte directly - if bytes, ok := value.([]byte); ok { - return []any{bytes}, nil - } - } - - encodedElements := make([]any, 0, rv.Len()) - for i := range rv.Len() { - elem := rv.Index(i).Interface() - encodedElem, err := EncodeToSuiValue(innerType, elem) - if err != nil { - return nil, fmt.Errorf("failed to encode element at index %d: %w", i, err) - } - encodedElements = append(encodedElements, encodedElem) - } - - return encodedElements, nil + return codec.EncodeToSuiValue(typeName, value) } diff --git a/relayer/codec/type_converters.go b/relayer/codec/type_converters.go index c330c21d5..b9c9d3190 100644 --- a/relayer/codec/type_converters.go +++ b/relayer/codec/type_converters.go @@ -1,775 +1,38 @@ package codec import ( - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "math/big" "reflect" - "strconv" - "strings" + + "github.com/smartcontractkit/chainlink-sui/codec" ) -// TypeConversionFunc defines a function that converts data from one type to another -type TypeConversionFunc func(from reflect.Type, to reflect.Type, data any) (any, error) +// Deprecated: use codec.TypeConversionFunc +// +//go:fix inline +type TypeConversionFunc = codec.TypeConversionFunc -// TypeConverter provides a registry approach to type conversions for mapstructure -type TypeConverter struct { - converters map[string]TypeConversionFunc -} +// Deprecated: use codec.TypeConverter +// +//go:fix inline +type TypeConverter = codec.TypeConverter -// NewTypeConverter creates a new type converter with all standard conversions registered +// Deprecated: use codec.NewTypeConverter +// +//go:fix inline func NewTypeConverter() *TypeConverter { - tc := &TypeConverter{ - converters: make(map[string]TypeConversionFunc), - } - - tc.registerStandardConverters() - return tc -} - -// registerStandardConverters registers all standard type conversion handlers -func (tc *TypeConverter) registerStandardConverters() { - // Hex string conversions - tc.RegisterConverter("hex_string_to_string", tc.hexToString) - tc.RegisterConverter("hex_string_to_bytes", tc.hexToBytes) - tc.RegisterConverter("hex_string_to_uint", tc.hexToUint) - tc.RegisterConverter("hex_string_to_int", tc.hexToInt) - tc.RegisterConverter("hex_string_to_bigint", tc.hexToBigInt) - tc.RegisterConverter("hex_string_to_array", tc.hexToArray) - - // Base64 string conversions - tc.RegisterConverter("base64_string_to_bytes", tc.base64ToBytes) - - // Numeric string conversions - tc.RegisterConverter("string_to_int", tc.stringToInt) - tc.RegisterConverter("string_to_uint", tc.stringToUint) - tc.RegisterConverter("string_to_float", tc.stringToFloat) - tc.RegisterConverter("string_to_bytes", tc.stringToBytes) - tc.RegisterConverter("string_to_bigint", tc.stringToBigInt) - - // Boolean conversions - tc.RegisterConverter("bool_to_int", tc.boolToInt) - tc.RegisterConverter("bool_to_uint", tc.boolToUint) - tc.RegisterConverter("bool_to_bigint", tc.boolToBigInt) - - // Array/Slice conversions - tc.RegisterConverter("slice_to_slice", tc.sliceToSlice) - tc.RegisterConverter("slice_to_hex_string", tc.sliceToHexString) - - // Float64 conversions (JSON unmarshals numbers as float64) - tc.RegisterConverter("float64_to_uint", tc.float64ToUint) - tc.RegisterConverter("float64_to_int", tc.float64ToInt) - - // json.Number conversions - tc.RegisterConverter("json_number_to_uint", tc.jsonNumberToUint) - tc.RegisterConverter("json_number_to_int", tc.jsonNumberToInt) - - // Bytes to numeric conversions (for BCS decoding) - tc.RegisterConverter("bytes_to_uint", tc.bytesToUint) - tc.RegisterConverter("slice_any_to_uint", tc.sliceAnyToUint) -} - -// RegisterConverter registers a conversion function with a unique key -func (tc *TypeConverter) RegisterConverter(key string, fn TypeConversionFunc) { - tc.converters[key] = fn -} - -// Convert attempts to convert data using registered converters -func (tc *TypeConverter) Convert(from reflect.Type, to reflect.Type, data any) (any, error) { - // Try float64 conversions (JSON unmarshals numbers as float64) - if from.Kind() == reflect.Float64 { - result, err, handled := tc.handleFloat64(data.(float64), to) - if handled { - return result, err - } - } - - // Try json.Number conversions - if _, ok := data.(json.Number); ok { - result, err, handled := tc.handleJSONNumber(data.(json.Number), to) - if handled { - return result, err - } - } - - // Try hex string conversions - if from.Kind() == reflect.String { - if str, ok := data.(string); ok && strings.HasPrefix(str, "0x") { - result, err, handled := tc.handleHexString(str, to, data) - if handled { - return result, err - } - } - } - - // Try numeric string conversions (before base64, as numeric strings can be valid base64) - if from.Kind() == reflect.String { - result, err, handled := tc.handleNumericString(data.(string), to) - if handled { - return result, err - } - } - - // Try base64 conversions (fallback for non-numeric strings to []byte) - if from.Kind() == reflect.String && (to.Kind() == reflect.Slice || to.Kind() == reflect.Array) && to.Elem().Kind() == reflect.Uint8 { - result, err, handled := tc.handleBase64String(data.(string), to) - if handled { - return result, err - } - } - - // Try boolean conversions - if from.Kind() == reflect.Bool { - result, err, handled := tc.handleBoolean(data.(bool), to) - if handled { - return result, err - } - } - - // Try bytes/slice to numeric conversions - if from.Kind() == reflect.Slice || from.Kind() == reflect.Array { - // Handle []byte or []any to numeric - if isNumericTarget(to) { - result, err, handled := tc.handleBytesToNumeric(from, to, data) - if handled { - return result, err - } - } - - // Handle slice to slice conversions - if to.Kind() == reflect.Slice { - result, err, handled := tc.handleSlice(from, to, data) - if handled { - return result, err - } - } - - // Handle slice to hex string conversions - if to.Kind() == reflect.String { - result, err := tc.sliceToHexString(from, to, data) - if err == nil { - return result, err - } - } - } - - // No conversion found, return data as-is - return data, nil -} - -// handleHexString handles hex string conversions -func (tc *TypeConverter) handleHexString(str string, to reflect.Type, data any) (result any, err error, handled bool) { - hexStr := strings.TrimPrefix(str, "0x") - - switch to.Kind() { - case reflect.String: - if fn, ok := tc.converters["hex_string_to_string"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - case reflect.Slice: - if to.Elem().Kind() == reflect.Uint8 { - if fn, ok := tc.converters["hex_string_to_bytes"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - } - case reflect.Array: - if to.Elem().Kind() == reflect.Uint8 { - if fn, ok := tc.converters["hex_string_to_array"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if fn, ok := tc.converters["hex_string_to_uint"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if fn, ok := tc.converters["hex_string_to_int"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - case reflect.Ptr: - if to == reflect.TypeOf((*big.Int)(nil)) { - if fn, ok := tc.converters["hex_string_to_bigint"]; ok { - result, err = fn(reflect.TypeOf(str), to, hexStr) - return result, err, true - } - } - case reflect.Interface: - return "0x" + hexStr, nil, true - } - - return nil, nil, false -} - -// handleBase64String handles base64 string conversions -func (tc *TypeConverter) handleBase64String(str string, to reflect.Type) (result any, err error, handled bool) { - if fn, ok := tc.converters["base64_string_to_bytes"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - // If base64ToBytes returns the original string unchanged, it means - // base64 decoding failed, so we should let default handling try - if resultStr, isStr := result.(string); isStr && resultStr == str { - return nil, nil, false - } - return result, err, true - } - return nil, nil, false -} - -// handleNumericString handles numeric string conversions -func (tc *TypeConverter) handleNumericString(str string, to reflect.Type) (result any, err error, handled bool) { - switch to.Kind() { - case reflect.String: - return str, nil, true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if fn, ok := tc.converters["string_to_int"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - return result, err, true - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if fn, ok := tc.converters["string_to_uint"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - return result, err, true - } - case reflect.Float32, reflect.Float64: - if fn, ok := tc.converters["string_to_float"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - return result, err, true - } - case reflect.Slice: - if to.Elem().Kind() == reflect.Uint8 { - if fn, ok := tc.converters["string_to_bytes"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - // If stringToBytes returns the original string unchanged, it means - // it's not a numeric string, so we should let other handlers try - if resultStr, isStr := result.(string); isStr && resultStr == str { - return nil, nil, false - } - return result, err, true - } - } - case reflect.Ptr: - if to == reflect.TypeOf((*big.Int)(nil)) { - if fn, ok := tc.converters["string_to_bigint"]; ok { - result, err = fn(reflect.TypeOf(str), to, str) - return result, err, true - } - } - } - - return nil, nil, false -} - -// handleBoolean handles boolean conversions -func (tc *TypeConverter) handleBoolean(boolValue bool, to reflect.Type) (result any, err error, handled bool) { - switch to.Kind() { - case reflect.Bool: - return boolValue, nil, true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if fn, ok := tc.converters["bool_to_int"]; ok { - result, err = fn(reflect.TypeOf(boolValue), to, boolValue) - return result, err, true - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if fn, ok := tc.converters["bool_to_uint"]; ok { - result, err = fn(reflect.TypeOf(boolValue), to, boolValue) - return result, err, true - } - case reflect.Ptr: - if to == reflect.TypeOf((*big.Int)(nil)) { - if fn, ok := tc.converters["bool_to_bigint"]; ok { - result, err = fn(reflect.TypeOf(boolValue), to, boolValue) - return result, err, true - } - } - } - - return nil, nil, false -} - -// handleSlice handles array/slice conversions -func (tc *TypeConverter) handleSlice(from, to reflect.Type, data any) (result any, err error, handled bool) { - if fn, ok := tc.converters["slice_to_slice"]; ok { - result, err = fn(from, to, data) - return result, err, true - } - return nil, nil, false -} - -// Conversion function implementations - -func (tc *TypeConverter) hexToString(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - return hexStr, nil -} - -func (tc *TypeConverter) hexToBytes(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - - if hexStr == "" { - return []uint8{}, nil - } - - if len(hexStr)%2 == 1 { - hexStr = "0" + hexStr - } - - return hex.DecodeString(hexStr) -} - -func (tc *TypeConverter) hexToUint(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - - return strconv.ParseUint(hexStr, base16, uint64Bits) -} - -func (tc *TypeConverter) hexToInt(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - - val, err := strconv.ParseInt(hexStr, base16, uint64Bits) - if err != nil { - return nil, fmt.Errorf("failed to parse hex to int: %w", err) - } - - return reflect.ValueOf(val).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) hexToBigInt(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - - bi := new(big.Int) - bi.SetString(hexStr, base16) - return bi, nil -} - -func (tc *TypeConverter) hexToArray(from, to reflect.Type, data any) (any, error) { - hexStr, ok := data.(string) - if !ok { - return data, nil - } - - bytes, err := tc.hexToBytes(from, reflect.SliceOf(to.Elem()), hexStr) - if err != nil { - return nil, fmt.Errorf("failed to decode hex string %q: %w", hexStr, err) - } - - byteSlice := bytes.([]byte) - out := make([]uint8, to.Len()) - - // Re-enable this check once we can guarantee that responses are always the same length as the target array length. - // Disabling for now to avoid breaking changes to core. - // if len(byteSlice) != to.Len() { - // return nil, fmt.Errorf("hex to array: byte slice length %d is not equal to output array length %d", len(byteSlice), to.Len()) - // } - - copy(out, byteSlice) - - return out, nil -} - -func (tc *TypeConverter) sliceToHexString(from, to reflect.Type, data any) (any, error) { - bytes, err := AnySliceToBytes(data.([]any)) - if err != nil { - return nil, err - } - - return "0x" + hex.EncodeToString(bytes), nil -} - -func (tc *TypeConverter) base64ToBytes(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - bytes, err := base64.StdEncoding.DecodeString(str) - if err == nil { - return bytes, nil - } - - // Base64 decoding failed - return string unchanged so handler knows to pass to default - return str, nil -} - -func (tc *TypeConverter) stringToInt(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - val, err := strconv.ParseInt(str, base10, uint64Bits) - if err != nil { - return nil, fmt.Errorf("failed to parse string to int: %w", err) - } - - if overflowInt(to, val) { - return nil, fmt.Errorf("value %d overflows %v", val, to) - } - - return reflect.ValueOf(val).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) stringToUint(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - val, err := strconv.ParseUint(str, base10, uint64Bits) - if err != nil { - return nil, fmt.Errorf("failed to parse string to uint: %w", err) - } - - if overflowUint(to, val) { - return nil, fmt.Errorf("value %d overflows %v", val, to) - } - - return reflect.ValueOf(val).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) stringToFloat(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - val, err := strconv.ParseFloat(str, uint64Bits) - if err != nil { - return nil, fmt.Errorf("failed to parse string to float: %w", err) - } - - if overflowFloat(to, val) { - return nil, fmt.Errorf("value %f overflows %v", val, to) - } - - return reflect.ValueOf(val).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) stringToBytes(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - // Try numeric string first (convert to little-endian bytes) - if num, err := strconv.ParseUint(str, base10, uint64Bits); err == nil { - return numericToBytes(num), nil - } - - // Not a numeric string - return unchanged so other handlers (base64) can try - return str, nil -} - -func (tc *TypeConverter) stringToBigInt(from, to reflect.Type, data any) (any, error) { - str, ok := data.(string) - if !ok { - return data, nil - } - - result := new(big.Int) - _, success := result.SetString(str, base10) - if !success { - return nil, fmt.Errorf("cannot parse string %s as big.Int", str) - } - - return result, nil -} - -func (tc *TypeConverter) boolToInt(from, to reflect.Type, data any) (any, error) { - boolValue, ok := data.(bool) - if !ok { - return data, nil - } - - if boolValue { - return reflect.ValueOf(1).Convert(to).Interface(), nil - } - - return reflect.ValueOf(0).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) boolToUint(from, to reflect.Type, data any) (any, error) { - boolValue, ok := data.(bool) - if !ok { - return data, nil - } - - if boolValue { - return reflect.ValueOf(1).Convert(to).Interface(), nil - } - - return reflect.ValueOf(0).Convert(to).Interface(), nil -} - -func (tc *TypeConverter) boolToBigInt(from, to reflect.Type, data any) (any, error) { - boolValue, ok := data.(bool) - if !ok { - return data, nil - } - - if boolValue { - return big.NewInt(1), nil - } - - return big.NewInt(0), nil -} - -func (tc *TypeConverter) sliceToSlice(from, to reflect.Type, data any) (any, error) { - sourceSlice := reflect.ValueOf(data) - targetSlice := reflect.MakeSlice(to, sourceSlice.Len(), sourceSlice.Cap()) - - for i := range sourceSlice.Len() { - sourceElem := sourceSlice.Index(i).Interface() - targetElem := reflect.New(to.Elem()).Interface() - - if err := DecodeSuiJsonValue(sourceElem, targetElem); err != nil { - return nil, fmt.Errorf("failed to decode array element at index %d: %w", i, err) - } - - targetSlice.Index(i).Set(reflect.ValueOf(targetElem).Elem()) - } - - return targetSlice.Interface(), nil -} - -// float64ToUint converts float64 to uint types (JSON unmarshals numbers as float64) -func (tc *TypeConverter) float64ToUint(from, to reflect.Type, data any) (any, error) { - floatVal, ok := data.(float64) - if !ok { - return data, nil - } - - uintVal := uint64(floatVal) - if overflowUint(to, uintVal) { - return nil, fmt.Errorf("value %d overflows %v", uintVal, to) - } - - return reflect.ValueOf(uintVal).Convert(to).Interface(), nil + return codec.NewTypeConverter() } -// float64ToInt converts float64 to int types -func (tc *TypeConverter) float64ToInt(from, to reflect.Type, data any) (any, error) { - floatVal, ok := data.(float64) - if !ok { - return data, nil - } - - intVal := int64(floatVal) - if overflowInt(to, intVal) { - return nil, fmt.Errorf("value %d overflows %v", intVal, to) - } - - return reflect.ValueOf(intVal).Convert(to).Interface(), nil -} - -// jsonNumberToUint converts json.Number to uint types -func (tc *TypeConverter) jsonNumberToUint(from, to reflect.Type, data any) (any, error) { - jsonNum, ok := data.(json.Number) - if !ok { - return data, nil - } - - intVal, err := jsonNum.Int64() - if err != nil { - return nil, fmt.Errorf("failed to parse JSON number: %w", err) - } - - if intVal < 0 { - return nil, fmt.Errorf("cannot convert negative value %d to uint", intVal) - } - - uintVal := uint64(intVal) - if overflowUint(to, uintVal) { - return nil, fmt.Errorf("value %d overflows %v", uintVal, to) - } - - return reflect.ValueOf(uintVal).Convert(to).Interface(), nil -} - -// jsonNumberToInt converts json.Number to int types -func (tc *TypeConverter) jsonNumberToInt(from, to reflect.Type, data any) (any, error) { - jsonNum, ok := data.(json.Number) - if !ok { - return data, nil - } - - intVal, err := jsonNum.Int64() - if err != nil { - return nil, fmt.Errorf("failed to parse JSON number: %w", err) - } - - if overflowInt(to, intVal) { - return nil, fmt.Errorf("value %d overflows %v", intVal, to) - } - - return reflect.ValueOf(intVal).Convert(to).Interface(), nil -} - -// bytesToUint converts []byte to uint types (little-endian) -func (tc *TypeConverter) bytesToUint(from, to reflect.Type, data any) (any, error) { - bytes, ok := data.([]byte) - if !ok { - return data, nil - } - - if len(bytes) == 0 { - return nil, fmt.Errorf("empty byte array cannot be converted to numeric value") - } - - var result uint64 - // Process bytes in little-endian order - for i := 0; i < len(bytes) && i < 8; i++ { - result |= uint64(bytes[i]) << (8 * i) - } - - if overflowUint(to, result) { - return nil, fmt.Errorf("value %d overflows %v", result, to) - } - - return reflect.ValueOf(result).Convert(to).Interface(), nil -} - -// sliceAnyToUint converts []any to uint types (converts to bytes first) -func (tc *TypeConverter) sliceAnyToUint(from, to reflect.Type, data any) (any, error) { - anySlice, ok := data.([]any) - if !ok { - return data, nil - } - - bytes, err := AnySliceToBytes(anySlice) - if err != nil { - return nil, fmt.Errorf("failed to convert slice to bytes: %w", err) - } - - return tc.bytesToUint(reflect.TypeOf(bytes), to, bytes) -} - -// handleFloat64 handles float64 conversions -func (tc *TypeConverter) handleFloat64(floatVal float64, to reflect.Type) (result any, err error, handled bool) { - switch to.Kind() { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if fn, ok := tc.converters["float64_to_uint"]; ok { - result, err = fn(reflect.TypeOf(floatVal), to, floatVal) - return result, err, true - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if fn, ok := tc.converters["float64_to_int"]; ok { - result, err = fn(reflect.TypeOf(floatVal), to, floatVal) - return result, err, true - } - } - - return nil, nil, false -} - -// handleJSONNumber handles json.Number conversions -func (tc *TypeConverter) handleJSONNumber(jsonNum json.Number, to reflect.Type) (result any, err error, handled bool) { - switch to.Kind() { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if fn, ok := tc.converters["json_number_to_uint"]; ok { - result, err = fn(reflect.TypeOf(jsonNum), to, jsonNum) - return result, err, true - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if fn, ok := tc.converters["json_number_to_int"]; ok { - result, err = fn(reflect.TypeOf(jsonNum), to, jsonNum) - return result, err, true - } - } - - return nil, nil, false -} - -// handleBytesToNumeric handles conversions from []byte or []any to numeric types -func (tc *TypeConverter) handleBytesToNumeric(from, to reflect.Type, data any) (result any, err error, handled bool) { - if from.Kind() == reflect.Slice && from.Elem().Kind() == reflect.Uint8 { - // []byte to numeric - if fn, ok := tc.converters["bytes_to_uint"]; ok { - result, err = fn(from, to, data) - return result, err, true - } - } - - if from.Kind() == reflect.Slice && from.Elem().Kind() == reflect.Interface { - // []any to numeric - if fn, ok := tc.converters["slice_any_to_uint"]; ok { - result, err = fn(from, to, data) - return result, err, true - } - } - - return nil, nil, false -} - -// isNumericTarget checks if the target type is a numeric type -func isNumericTarget(to reflect.Type) bool { - switch to.Kind() { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - } - return false -} - -// Global type converter instance for package-wide use (lazy initialization) -var defaultTypeConverter *TypeConverter - -// getDefaultTypeConverter returns the global type converter, initializing it if necessary -func getDefaultTypeConverter() *TypeConverter { - if defaultTypeConverter == nil { - defaultTypeConverter = NewTypeConverter() - } - return defaultTypeConverter -} - -// UnifiedTypeConverterHook is a mapstructure hook that uses the type converter registry +// Deprecated: use codec.UnifiedTypeConverterHook +// +//go:fix inline func UnifiedTypeConverterHook(from, to reflect.Type, data any) (any, error) { - // Skip if types are the same - if from == to { - return data, nil - } - - // Handle single-field struct case only for primitive source types - // (string, bool, numeric types) to avoid interfering with map-to-struct decoding - if to.Kind() == reflect.Struct && to.NumField() == 1 { - switch from.Kind() { - case reflect.String, reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64, - reflect.Slice, reflect.Array: - return handleSingleFieldStruct(to, data, DecodeSuiJsonValue) - } - } - - // Use the global converter - return getDefaultTypeConverter().Convert(from, to, data) + return codec.UnifiedTypeConverterHook(from, to, data) } +// Deprecated: use codec.BytesToAnySlice +// +//go:fix inline func BytesToAnySlice(b []byte) []any { - result := make([]any, len(b)) - for i, v := range b { - result[i] = v - } - return result + return codec.BytesToAnySlice(b) } diff --git a/relayer/codec/types.go b/relayer/codec/types.go index 22d8d5601..8a47ecff3 100644 --- a/relayer/codec/types.go +++ b/relayer/codec/types.go @@ -1,149 +1,48 @@ package codec -import ( - "errors" - "math/big" +import "github.com/smartcontractkit/chainlink-sui/codec" - "github.com/block-vision/sui-go-sdk/models" -) - -var AccountZero = make([]byte, 32) - -type PTBCommandDependency struct { - CommandIndex uint16 - ResultIndex *uint16 -} +//go:fix inline +var AccountZero = codec.AccountZero -// PointerTag defines the structured format for pointer tags used in chain reader. -// Pointer tags specify how to derive object IDs from pointer objects stored on-chain. -type PointerTag struct { - // Module name containing the pointer object (e.g. "state_object", "offramp", "counter") - Module string `json:"module"` - // PointerName is the object type to search for (e.g. "CCIPObjectRefPointer", "OffRampStatePointer") - PointerName string `json:"pointerName"` - // FieldName is OPTIONAL and NOT USED by the implementation. The parent field name is automatically - // looked up from the global common.PointerConfigs registry based on the PointerName. - // This field exists for backward compatibility or future implementations to override static code but is currently ignored. - FieldName string `json:"fieldName,omitempty"` - // DerivationKey is the key used to derive the child object ID from the parent object ID (e.g. "CCIPObjectRef", "CCIP_OWNABLE") - DerivationKey string `json:"derivationKey"` - // PackageID is the package ID for the Pointer object if it differs from the calling contract's package ID - // This is used for cross-package pointer dependencies (e.g. offramp package depending on CCIP package CCIPObjectRef) - // If empty, the calling contract's package ID is used - PackageID string `json:"packageId,omitempty"` -} +//go:fix inline +type PTBCommandDependency = codec.PTBCommandDependency -func (p PointerTag) Validate() error { - if p.Module == "" { - return errors.New("PointerTag.Module is required") - } - if p.PointerName == "" { - return errors.New("PointerTag.Pointer is required") - } - // FieldName is optional - it's looked up from common.PointerConfigs - if p.DerivationKey == "" { - return errors.New("PointerTag.DerivationKey is required") - } - return nil -} +//go:fix inline +type PointerTag = codec.PointerTag -// SuiFunctionParam defines a parameter for a Sui function call -type SuiFunctionParam struct { - // Name of the parameter - Name string - // PointerTag (optional) specify how to derive object IDs from pointer objects stored on-chain. - PointerTag *PointerTag - // Type of the parameter (e.g., "u64", "String", "vector", "ptb_dependency") - Type string - // IsMutable specifies if the object is mutable or not (optional - defaults to true) - IsMutable *bool - // IsGeneric specifies if the parameter is a generic argument - GenericType *string - // Whether the parameter is required - Required bool - // Default value to use if not provided - DefaultValue any - // Result from a previous PTB Command (optional). It is used for expressive construction of PTB commands - PTBDependency *PTBCommandDependency - // GenericDependency maps to internal helpers for fetching an unknown generic type required by the parameter - GenericDependency *string -} +//go:fix inline +type SuiFunctionParam = codec.SuiFunctionParam -type SuiPTBCommandType string +//go:fix inline +type SuiPTBCommandType = codec.SuiPTBCommandType const ( - SuiPTBCommandMoveCall SuiPTBCommandType = "move_call" - SuiPTBCommandPublish SuiPTBCommandType = "publish" - SuiPTBCommandTransfer SuiPTBCommandType = "transfer" + SuiPTBCommandMoveCall = codec.SuiPTBCommandMoveCall + SuiPTBCommandPublish = codec.SuiPTBCommandPublish + SuiPTBCommandTransfer = codec.SuiPTBCommandTransfer ) -// OCRConfigSet event data -type ConfigSet struct { - OcrPluginType byte - ConfigDigest []byte - Signers [][]byte - // this is a list of addresses, we can treat them as strings - Transmitters []string - BigF byte -} +//go:fix inline +type ConfigSet = codec.ConfigSet -// SourceChainConfigSet event data -type SourceChainConfigSet struct { - SourceChainSelector uint64 - SourceChainConfig SourceChainConfig -} +//go:fix inline +type SourceChainConfigSet = codec.SourceChainConfigSet -// SourceChainConfig event data -type SourceChainConfig struct { - Router string - IsEnabled bool - MinSeqNr uint64 - IsRMNVerificationDisabled bool - OnRamp []byte -} +//go:fix inline +type SourceChainConfig = codec.SourceChainConfig -// ExecutionReport event data -type ExecutionReport struct { - SourceChainSelector uint64 - Message Any2SuiRampMessage - OffchainTokenData [][]byte - Proofs [][]byte -} +//go:fix inline +type ExecutionReport = codec.ExecutionReport -// RampMessageHeader event data -type RampMessageHeader struct { - MessageID []byte - SourceChainSelector uint64 - DestChainSelector uint64 - SequenceNumber uint64 - Nonce uint64 -} +//go:fix inline +type RampMessageHeader = codec.RampMessageHeader -// Any2SuiTokenTransfer event data -type Any2SuiTokenTransfer struct { - SourcePoolAddress []byte - DestTokenAddress models.SuiAddress - DestGasAmount uint32 - ExtraData []byte - Amount *big.Int -} +//go:fix inline +type Any2SuiTokenTransfer = codec.Any2SuiTokenTransfer -// Any2SuiRampMessage event data -type Any2SuiRampMessage struct { - Header RampMessageHeader - Sender []byte - Data []byte - Receiver models.SuiAddress - GasLimit *big.Int - TokenReceiver models.SuiAddressBytes - TokenAmounts []Any2SuiTokenTransfer -} +//go:fix inline +type Any2SuiRampMessage = codec.Any2SuiRampMessage -// ExecutionStateChanged event data -type ExecutionStateChanged struct { - SourceChainSelector uint64 `json:"source_chain_selector"` - SequenceNumber uint64 `json:"sequence_number"` - MessageId []byte `json:"message_id"` - MessageHash []byte `json:"message_hash"` - State byte `json:"state"` -} +//go:fix inline +type ExecutionStateChanged = codec.ExecutionStateChanged diff --git a/scripts/go.sum b/scripts/go.sum index a51b4bd72..4d7e3f8d7 100644 --- a/scripts/go.sum +++ b/scripts/go.sum @@ -625,6 +625,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= +github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5 h1:EiQx0LCPzxlfO9piSPeMCVSZAnp/BxAsPIGh/jBal18= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= From a1cdd6cb45d1b638a4f6c78bbb054e98ebcf0b07 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Thu, 2 Jul 2026 15:02:03 -0400 Subject: [PATCH 3/8] further refactoring --- bindings/bind/common.go | 8 +- bindings/bind/json_decoder.go | 35 +- bindings/bind/utils.go | 49 +- codec/decoder.go | 3 +- codec/encoder.go | 13 +- codec/go.mod | 2 +- codec/go.sum | 2 - codec/json_decoder.go | 94 ++ codec/loop/loop_reader.go | 2 +- codec/loop/loop_reader_test.go | 918 +++++++----------- codec/types.go | 80 ++ go.md | 23 +- go.mod | 2 + integration-tests/go.mod | 2 + integration-tests/go.sum | 2 - .../chainreader}/loop/connection_load_test.go | 0 relayer/chainreader/loop/loop_reader_test.go | 625 ++++++++++++ 17 files changed, 1202 insertions(+), 658 deletions(-) create mode 100644 codec/json_decoder.go rename {codec => relayer/chainreader}/loop/connection_load_test.go (100%) create mode 100644 relayer/chainreader/loop/loop_reader_test.go diff --git a/bindings/bind/common.go b/bindings/bind/common.go index 3efabec12..5f63fc80f 100644 --- a/bindings/bind/common.go +++ b/bindings/bind/common.go @@ -11,6 +11,7 @@ import ( "github.com/block-vision/sui-go-sdk/signer" "github.com/block-vision/sui-go-sdk/transaction" + "github.com/smartcontractkit/chainlink-sui/codec" bindutils "github.com/smartcontractkit/chainlink-sui/bindings/utils" "github.com/smartcontractkit/chainlink-sui/relayer/client" ) @@ -32,10 +33,9 @@ type transactionObjectChangesClient interface { GetTransactionChangedObjects(ctx context.Context, digest string) ([]*suirpcv2.ChangedObject, error) } -type Object struct { - Id string - InitialSharedVersion *uint64 -} +// Object is an alias for codec.Object for backward compatibility. +// Deprecated: use codec.Object directly. +type Object = codec.Object type EmptyMoveStructWitness struct{} diff --git a/bindings/bind/json_decoder.go b/bindings/bind/json_decoder.go index 5edf4558c..c37f5ca74 100644 --- a/bindings/bind/json_decoder.go +++ b/bindings/bind/json_decoder.go @@ -1,47 +1,23 @@ package bind import ( - "encoding/json" - "errors" "fmt" "math/big" "reflect" "strings" "github.com/mitchellh/mapstructure" + + "github.com/smartcontractkit/chainlink-sui/codec" ) // DecodeJSONReturn decodes gRPC/JSON Move return values into the provided target. +// Deprecated: use codec.DecodeJSONReturn directly. func DecodeJSONReturn(data any, target any) error { - if target == nil { - return errors.New("target cannot be nil") - } - - if raw, ok := data.(json.RawMessage); ok { - var intermediate any - if err := json.Unmarshal(raw, &intermediate); err != nil { - return fmt.Errorf("json unmarshal failed: %w", err) - } - - return DecodeJSONReturn(intermediate, target) - } - - if reflect.TypeOf(data) == reflect.TypeOf(target).Elem() { - reflect.ValueOf(target).Elem().Set(reflect.ValueOf(data)) - return nil - } - - targetType := reflect.TypeOf(target).Elem() - - bigPtrT := reflect.TypeOf((*big.Int)(nil)) - bigValT := bigPtrT.Elem() - if targetType == bigValT || targetType == bigPtrT { - return decodeBigInt(data, target) - } - - return decodeWithMapstructure(data, target) + return codec.DecodeJSONReturn(data, target) } +// decodeBigInt decodes a string into a big.Int target. func decodeBigInt(data any, target any) error { str, ok := data.(string) if !ok { @@ -67,6 +43,7 @@ func decodeBigInt(data any, target any) error { return nil } +// decodeWithMapstructure decodes using mapstructure with custom hooks. func decodeWithMapstructure(data any, target any) error { config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( diff --git a/bindings/bind/utils.go b/bindings/bind/utils.go index f2aa7143c..4d03049e6 100644 --- a/bindings/bind/utils.go +++ b/bindings/bind/utils.go @@ -1,59 +1,24 @@ package bind import ( - "encoding/hex" "fmt" - "strings" "unicode" "github.com/block-vision/sui-go-sdk/models" - "github.com/block-vision/sui-go-sdk/utils" + + "github.com/smartcontractkit/chainlink-sui/codec" ) // IsSuiAddress returns true if addr is a valid Sui address/ObjectID. -// It is an improvement over the sui-go-sdk's IsValidSuiAddress function. +// Deprecated: use codec.IsSuiAddress directly. func IsSuiAddress(addr string) bool { - if !(strings.HasPrefix(addr, "0x") || strings.HasPrefix(addr, "0X")) { - return false - } - - h := addr[2:] - - // 1..64 hex chars - if len(h) == 0 || len(h) > 64 { - return false - } - - // hex only - for _, r := range h { - if !isHexRune(r) { - return false - } - } - - // hex.DecodeString requires even length; allow odd by left-padding one '0' - if len(h)%2 == 1 { - h = "0" + h - } - - b, err := hex.DecodeString(h) - - if err != nil { - return false - } - - // must be ≤32 bytes (32 bytes == 64 hex chars) - return len(b) <= 32 + return codec.IsSuiAddress(addr) } -// ToSuiAddress normalizes and validates a Sui address +// ToSuiAddress normalizes and validates a Sui address. +// Deprecated: use codec.ToSuiAddress directly. func ToSuiAddress(address string) (string, error) { - normalized := utils.NormalizeSuiAddress(address) - if !IsSuiAddress(string(normalized)) { - return "", fmt.Errorf("invalid sui address: %s", address) - } - - return string(normalized), nil + return codec.ToSuiAddress(address) } func GetFailedTxError(tx *models.SuiTransactionBlockResponse) error { diff --git a/codec/decoder.go b/codec/decoder.go index 10f6cd09e..d3c191677 100644 --- a/codec/decoder.go +++ b/codec/decoder.go @@ -14,7 +14,6 @@ import ( aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" "github.com/block-vision/sui-go-sdk/models" - "github.com/smartcontractkit/chainlink-sui/bindings/bind" ) const ( @@ -43,7 +42,7 @@ const ( // DecodeSuiJsonValue decodes Sui JSON response data into the provided target. func DecodeSuiJsonValue(data any, target any) error { - return bind.DecodeJSONReturn(data, target) + return DecodeJSONReturn(data, target) } // ConvertBase64StringsToHex walks arbitrary JSON-like structures and converts any diff --git a/codec/encoder.go b/codec/encoder.go index cdae87709..ac520d469 100644 --- a/codec/encoder.go +++ b/codec/encoder.go @@ -10,7 +10,6 @@ import ( "strconv" "strings" - "github.com/smartcontractkit/chainlink-sui/bindings/bind" ) const ( @@ -75,12 +74,12 @@ func EncodeToSuiValue(typeName string, value any) (any, error) { } } -func encodeObject(value any) (bind.Object, error) { +func encodeObject(value any) (Object, error) { switch v := value.(type) { - case bind.Object: - return bind.Object{Id: v.Id}, nil + case Object: + return Object{Id: v.Id}, nil default: - return bind.Object{}, fmt.Errorf("cannot convert %T to object", value) + return Object{}, fmt.Errorf("cannot convert %T to object", value) } } @@ -92,7 +91,7 @@ func encodeAddress(value any) (string, error) { v = "0x" + v } - normalizedAddress, err := bind.ToSuiAddress(v) + normalizedAddress, err := ToSuiAddress(v) if err != nil { return "", fmt.Errorf("failed to convert address to Sui address: %w", err) } @@ -101,7 +100,7 @@ func encodeAddress(value any) (string, error) { case []byte: stringAddr := "0x" + hex.EncodeToString(v) - normalizedAddress, err := bind.ToSuiAddress(stringAddr) + normalizedAddress, err := ToSuiAddress(stringAddr) if err != nil { return "", fmt.Errorf("failed to convert address to Sui address: %w", err) } diff --git a/codec/go.mod b/codec/go.mod index f55a2b73f..a8a40105c 100644 --- a/codec/go.mod +++ b/codec/go.mod @@ -5,9 +5,9 @@ go 1.26.2 require ( github.com/aptos-labs/aptos-go-sdk v1.13.0 github.com/block-vision/sui-go-sdk v1.2.1 + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 - github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 github.com/stretchr/testify v1.11.1 google.golang.org/grpc v1.81.1 ) diff --git a/codec/go.sum b/codec/go.sum index af7737f81..d42f62f93 100644 --- a/codec/go.sum +++ b/codec/go.sum @@ -300,8 +300,6 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 h1:L2Y1W2j+GcgGJ8Af6PkXznAUGOMwfOatk9nooE57Gx4= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5/go.mod h1:YYlFG2/G5/Q+BzByqcsvIZxS3vW8dgnxz6j+VTwEYj4= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/codec/json_decoder.go b/codec/json_decoder.go new file mode 100644 index 000000000..b6312d051 --- /dev/null +++ b/codec/json_decoder.go @@ -0,0 +1,94 @@ +package codec + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "reflect" + "strings" + + "github.com/mitchellh/mapstructure" +) + +// DecodeJSONReturn decodes gRPC/JSON Move return values into the provided target. +func DecodeJSONReturn(data any, target any) error { + if target == nil { + return errors.New("target cannot be nil") + } + + if raw, ok := data.(json.RawMessage); ok { + var intermediate any + if err := json.Unmarshal(raw, &intermediate); err != nil { + return fmt.Errorf("json unmarshal failed: %w", err) + } + + return DecodeJSONReturn(intermediate, target) + } + + if reflect.TypeOf(data) == reflect.TypeOf(target).Elem() { + reflect.ValueOf(target).Elem().Set(reflect.ValueOf(data)) + return nil + } + + targetType := reflect.TypeOf(target).Elem() + + bigPtrT := reflect.TypeOf((*big.Int)(nil)) + bigValT := bigPtrT.Elem() + if targetType == bigValT || targetType == bigPtrT { + return decodeBigInt(data, target) + } + + return decodeWithMapstructure(data, target) +} + +func decodeBigInt(data any, target any) error { + str, ok := data.(string) + if !ok { + return fmt.Errorf("big.Int decode: expected string, got %T", data) + } + + bi, success := new(big.Int).SetString(str, 10) + if !success { + return fmt.Errorf("big.Int decode: invalid number %q", str) + } + + targetValue := reflect.ValueOf(target).Elem() + targetType := targetValue.Type() + bigPtrT := reflect.TypeOf((*big.Int)(nil)) + bigValT := bigPtrT.Elem() + + if targetType == bigValT { + targetValue.Set(reflect.ValueOf(*bi)) + } else { + targetValue.Set(reflect.ValueOf(bi)) + } + + return nil +} + +func decodeWithMapstructure(data any, target any) error { + config := &mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc( + UnifiedTypeConverterHook, + mapstructure.StringToTimeDurationHookFunc(), + ), + Result: target, + WeaklyTypedInput: true, + TagName: "json", + MatchName: fuzzyFieldMatcher, + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return fmt.Errorf("failed to create decoder: %w", err) + } + + return decoder.Decode(data) +} + +func fuzzyFieldMatcher(mapKey, fieldName string) bool { + mk := strings.ReplaceAll(mapKey, "_", "") + fn := strings.ReplaceAll(fieldName, "_", "") + return strings.EqualFold(mk, fn) +} diff --git a/codec/loop/loop_reader.go b/codec/loop/loop_reader.go index 97eedc9dd..d925d5607 100644 --- a/codec/loop/loop_reader.go +++ b/codec/loop/loop_reader.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-aptos/relayer/chainreader/loop" - "github.com/smartcontractkit/chainlink-sui/relayer/codec" + "github.com/smartcontractkit/chainlink-sui/codec" ) const ( diff --git a/codec/loop/loop_reader_test.go b/codec/loop/loop_reader_test.go index d6152e32e..fa6e92176 100644 --- a/codec/loop/loop_reader_test.go +++ b/codec/loop/loop_reader_test.go @@ -5,619 +5,411 @@ package loop import ( "context" "encoding/json" + "errors" "fmt" - "math/big" - "os" - "strings" "testing" - "time" - - "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/reader" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/sqlutil/sqltest" "github.com/smartcontractkit/chainlink-common/pkg/types" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - - aptosCRConfig "github.com/smartcontractkit/chainlink-aptos/relayer/chainreader/config" - - "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" - chainreaderConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" - "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/indexer" - "github.com/smartcontractkit/chainlink-sui/relayer/client" - "github.com/smartcontractkit/chainlink-sui/relayer/codec" - "github.com/smartcontractkit/chainlink-sui/relayer/testutils" ) -//nolint:paralleltest -func TestLoopChainReaderLocal(t *testing.T) { - log := logger.Test(t) +// mockContractReader is a mock implementation of types.ContractReader for testing. +// It simulates the LOOP boundary behavior where params and return values are []byte. +type mockContractReader struct { + mock.Mock +} - cmd, err := testutils.StartSuiNode(testutils.CLI) - require.NoError(t, err) +func (m *mockContractReader) Name() string { + args := m.Called() + return args.String(0) +} - testutils.CleanupTestContracts() +func (m *mockContractReader) GetLatestValue(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error { + args := m.Called(ctx, readIdentifier, confidenceLevel, params, returnVal) + + // Simulate the LOOP boundary: copy the mock return value to the returnVal pointer + if ret := args.Get(0); ret != nil { + // returnVal is expected to be *[]byte + if rv, ok := returnVal.(*[]byte); ok { + *rv = ret.([]byte) + } + } + + return args.Error(1) +} - // Ensure the process is killed when the test completes. - t.Cleanup(func() { - testutils.CleanupTestContracts() +func (m *mockContractReader) BatchGetLatestValues(ctx context.Context, request types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) { + args := m.Called(ctx, request) + return args.Get(0).(types.BatchGetLatestValuesResult), args.Error(1) +} - if cmd.Process != nil { - perr := cmd.Process.Kill() - if perr != nil { - t.Logf("Failed to kill process: %v", perr) - } - } - }) +func (m *mockContractReader) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) { + args := m.Called(ctx, contract, filter, limitAndSort, sequenceDataType) + + // Simulate LOOP boundary: sequenceDataType is *[]byte, we return mock data + if ret := args.Get(0); ret != nil { + return ret.([]types.Sequence), nil + } + + return nil, args.Error(1) +} - log.Debugw("Started Sui node") +func (m *mockContractReader) Bind(ctx context.Context, bindings []types.BoundContract) error { + args := m.Called(ctx, bindings) + return args.Error(0) +} - runLoopChainReaderEchoTest(t, log, testutils.LocalGrpcURL) +func (m *mockContractReader) Unbind(ctx context.Context, bindings []types.BoundContract) error { + args := m.Called(ctx, bindings) + return args.Error(0) } -func runLoopChainReaderEchoTest(t *testing.T, log logger.Logger, rpcUrl string) { - t.Helper() - ctx := context.Background() +func (m *mockContractReader) Start(ctx context.Context) error { + args := m.Called(ctx) + return args.Error(0) +} - keystoreInstance := testutils.NewTestKeystore(t) - accountAddress, publicKeyBytes := testutils.GetAccountAndKeyFromSui(keystoreInstance) +func (m *mockContractReader) Close() error { + args := m.Called() + return args.Error(0) +} - relayerClient, clientErr := client.NewPTBClient(log, client.PTBClientConfig{ - GrpcTarget: rpcUrl, - GrpcToken: "test", - TransactionTimeout: 10 * time.Second, - MaxConcurrentRequests: 5, - KeystoreService: keystoreInstance, - DefaultRequestType: client.TransactionRequestType("WaitForLocalExecution"), - }) - require.NoError(t, clientErr) +func (m *mockContractReader) Ready() error { + args := m.Called() + return args.Error(0) +} - faucetFundErr := testutils.FundWithFaucet(log, testutils.SuiLocalnet, accountAddress) - require.NoError(t, faucetFundErr) +func (m *mockContractReader) HealthReport() map[string]error { + args := m.Called() + return args.Get(0).(map[string]error) +} - chainID, err := testutils.GetChainIdentifier(rpcUrl) +// TestLoopChainReader_GetLatestValue_VerifiesLOOPBoundary tests that the LoopChainReader +// properly serializes params to []byte and deserializes results using DecodeSuiJsonValue. +func TestLoopChainReader_GetLatestValue_VerifiesLOOPBoundary(t *testing.T) { + t.Parallel() + log := logger.Test(t) + + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + readIdentifier := "test-contract-echo_u64" + params := map[string]any{"val": uint64(42)} + + // Expected JSON params after serialization + expectedParams, err := json.Marshal(params) require.NoError(t, err) - testutils.PatchEnvironmentTOML("contracts/test", "local", chainID) - testutils.PatchEnvironmentTOML("contracts/test_secondary", "local", chainID) - - contractPath := testutils.BuildSetup(t, "contracts/test") - gasBudget := int(2000000000) - packageId, tx, err := testutils.PublishContract(t, "counter", contractPath, accountAddress, &gasBudget) + + // Mock return value (simulating what comes back from the LOOP boundary) + mockReturn := json.RawMessage("42") + + // Set up expectation: GetLatestValue receives []byte params + mockReader.On("GetLatestValue", + ctx, + readIdentifier, + primitives.Finalized, + mock.AnythingOfType("*[]uint8"), // params as *[]byte + mock.AnythingOfType("*[]uint8"), // returnVal as *[]byte + ).Run(func(args mock.Arguments) { + // Verify params were serialized correctly + paramBytes := args.Get(3).(*[]byte) + require.JSONEq(t, string(expectedParams), string(*paramBytes)) + + // Simulate setting the return value (as would happen across LOOP boundary) + returnVal := args.Get(4).(*[]byte) + *returnVal = []byte(mockReturn) + }).Return(nil) + + var result uint64 + err = loopReader.GetLatestValue(ctx, readIdentifier, primitives.Finalized, params, &result) + require.NoError(t, err) - require.NotNil(t, packageId) - require.NotNil(t, tx) + require.Equal(t, uint64(42), result) + mockReader.AssertExpectations(t) +} - // Set up the base ChainReader with echo function configurations - chainReaderConfigs := chainreaderConfig.ChainReaderConfig{ - IsLoopPlugin: true, - Modules: map[string]*chainreaderConfig.ChainReaderModule{ - "echo": { - Name: "echo", - Functions: map[string]*chainreaderConfig.ChainReaderFunction{ - "echo_u64": { - Name: "echo_u64", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "u64", - Name: "val", - Required: true, - }, - }, - }, - "echo_u256": { - Name: "echo_u256", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "u256", - Name: "val", - Required: true, - }, - }, - }, - "echo_u32_u64_tuple": { - Name: "echo_u32_u64_tuple", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "u32", - Name: "val1", - Required: true, - }, - { - Type: "u64", - Name: "val2", - Required: true, - }, - }, - ResultTupleToStruct: []string{"first", "second"}, - }, - "echo_string": { - Name: "echo_string", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "0x1::string::String", - Name: "val", - Required: true, - }, - }, - }, - "echo_byte_vector": { - Name: "echo_byte_vector", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "vector", - Name: "val", - Required: true, - }, - }, - }, - "echo_byte_vector_vector": { - Name: "echo_byte_vector_vector", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "vector>", - Name: "val", - Required: true, - }, - }, - }, - "simple_event_echo": { - Name: "simple_event_echo", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{ - { - Type: "u64", - Name: "number", - Required: true, - }, - }, - }, - }, - Events: map[string]*chainreaderConfig.ChainReaderEvent{ - "single_value_event": { - Name: "single_value_event", - EventType: "SingleValueEvent", - EventSelector: client.EventSelector{ - Package: packageId, - Module: "echo", - Event: "SingleValueEvent", - }, - }, - "double_value_event": { - Name: "double_value_event", - EventType: "DoubleValueEvent", - }, - "triple_value_event": { - Name: "triple_value_event", - EventType: "TripleValueEvent", - }, - }, - }, - "counter": { - Name: "counter", - Functions: map[string]*chainreaderConfig.ChainReaderFunction{ - "get_tuple_struct": { - Name: "get_tuple_struct", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{}, - ResultTupleToStruct: []string{"value", "address", "bool", "struct_tag"}, - }, - "get_ocr_config": { - Name: "get_ocr_config", - SignerAddress: accountAddress, - Params: []codec.SuiFunctionParam{}, - // used to wrap entire result - ResultTupleToStruct: []string{"OCRConfig"}, - // The Sui node renders the Move struct with snake_case keys. The core node decodes the LOOP - // bytes into cciptypes.OCRConfigResponse via DecodeSuiJsonValue (mapstructure), whose fuzzy - // matcher strips underscores and is case-insensitive: config_info->ConfigInfo, - // config_digest->ConfigDigest, n->N, is_signature_verification_enabled->... all match on their - // own. The lone exception is `big_f` ("bigf") which does NOT match `F` ("f"), so F silently - // decodes to 0. Rename it so it lands on OCRConfigResponse.ConfigInfo.F. This mirrors the fix - // in chainlink core's Sui contract_reader.go for the OffRamp latest_config_details read. - ResultFieldRenames: map[string]aptosCRConfig.RenamedField{ - "OCRConfig": { - SubFieldRenames: map[string]aptosCRConfig.RenamedField{ - "config_info": { - SubFieldRenames: map[string]aptosCRConfig.RenamedField{ - "big_f": {NewName: "f"}, - }, - }, - }, - }, - }, - }, - }, - }, +// TestLoopChainReader_GetLatestValue_DecodesComplexTypes tests decoding of complex +// return types using the Sui JSON decoder. +func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { + t.Parallel() + log := logger.Test(t) + + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + + tests := []struct { + name string + mockResponse string + expectedResult any + targetType any + }{ + { + name: "uint64", + mockResponse: `100`, + expectedResult: uint64(100), + targetType: new(uint64), }, - EventsIndexer: config.EventsIndexerConfig{ - PollingInterval: 2 * time.Second, - SyncTimeout: 60 * time.Second, + { + name: "string", + mockResponse: `"hello"`, + expectedResult: "hello", + targetType: new(string), }, - TransactionsIndexer: config.TransactionsIndexerConfig{ - PollingInterval: 2 * time.Second, - SyncTimeout: 60 * time.Second, + { + name: "struct", + mockResponse: `{"first": 10, "second": 20}`, + expectedResult: map[string]any{"first": float64(10), "second": float64(20)}, + targetType: new(map[string]any), }, } - - echoBinding := types.BoundContract{ - Name: "echo", - Address: packageId, // Package ID of the deployed echo contract + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockReader.ExpectedCalls = nil + + mockReader.On("GetLatestValue", + ctx, + "test-read", + primitives.Finalized, + mock.AnythingOfType("*[]uint8"), + mock.AnythingOfType("*[]uint8"), + ).Run(func(args mock.Arguments) { + returnVal := args.Get(4).(*[]byte) + *returnVal = []byte(tt.mockResponse) + }).Return(nil) + + switch v := tt.targetType.(type) { + case *uint64: + err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, v) + require.NoError(t, err) + require.Equal(t, tt.expectedResult, *v) + case *string: + err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, v) + require.NoError(t, err) + require.Equal(t, tt.expectedResult, *v) + case *map[string]any: + err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, v) + require.NoError(t, err) + require.Equal(t, tt.expectedResult, *v) + } + + mockReader.AssertExpectations(t) + }) } +} - counterBinding := types.BoundContract{ - Name: "counter", - Address: packageId, // Package ID of the deployed echo contract +// TestLoopChainReader_QueryKey_VerifiesExpressionSerialization tests that QueryKey +// properly serializes expressions for the LOOP boundary. +func TestLoopChainReader_QueryKey_VerifiesExpressionSerialization(t *testing.T) { + t.Parallel() + t.Skip("TODO: implement when QueryKey expression serialization is better understood") + + log := logger.Test(t) + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + contract := types.BoundContract{Name: "test", Address: "0x123"} + filter := query.KeyFilter{ + Key: "test_event", + Expressions: []query.Expression{ + // Add expression here + }, } - - // Set up DB - datastoreUrl := os.Getenv("TEST_DB_URL") - if datastoreUrl == "" { - t.Skip("Skipping persistent tests as TEST_DB_URL is not set in CI") + limitAndSort := query.LimitAndSort{ + Limit: query.CountLimit(10), } - db := sqltest.NewDB(t, datastoreUrl) - - // Create the indexers - txnIndexer := indexer.NewTransactionsIndexer( - db, - log, - map[string]*config.ChainReaderEvent{}, - ) - evIndexer := indexer.NewEventIndexer( - db, - log, - []*client.EventSelector{}, - ) - - chainPoller := indexer.NewChainPoller( - relayerClient, - log, - config.ChainPollerConfig{ - PollingInterval: 1 * time.Second, - SyncTimeout: 60 * time.Second, - BackfillCheckpointCount: testutils.Uint64Pointer(uint64(1)), + + // Mock event data as would come back from LOOP + mockEventData := []byte(`{"value": 123}`) + mockReader.On("QueryKey", + ctx, + contract, + mock.Anything, // filter with serialized expressions + limitAndSort, + mock.AnythingOfType("*[]uint8"), + ).Return([]types.Sequence{ + { + Data: &mockEventData, }, - evIndexer.GetEventSelectors, - ) - - indexerInstance := indexer.NewIndexerFromComponents( - log, - chainPoller, - evIndexer, - txnIndexer, - ) - - chainReader, err := reader.NewChainReader(ctx, log, relayerClient, chainReaderConfigs, db, indexerInstance, nil) - require.NoError(t, err) - - // Wrap the base chain reader with loop chain reader - loopReader := NewLoopChainReader(log, chainReader) - - // Bind the contracts - err = loopReader.Bind(context.Background(), []types.BoundContract{echoBinding, counterBinding}) - require.NoError(t, err) - - // Start the indexers - err = indexerInstance.Start(ctx) + }, nil) + + type TestEvent struct { + Value uint64 `json:"value"` + } + + var event TestEvent + sequences, err := loopReader.QueryKey(ctx, contract, filter, limitAndSort, &event) + require.NoError(t, err) + require.Len(t, sequences, 1) + require.Equal(t, uint64(123), event.Value) +} - log.Debugw("LoopChainReader setup complete") - - t.Run("LoopReader_GetLatestValue_EchoU64", func(t *testing.T) { - testValue := uint64(42) - var retUint64 uint64 - - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_u64"}, "-"), - primitives.Finalized, - map[string]any{ - "val": testValue, - }, - &retUint64, - ) - require.NoError(t, err) - require.Equal(t, testValue, retUint64) - }) - - t.Run("LoopReader_GetLatestValue_EchoU64_VariousValues", func(t *testing.T) { - testCases := []uint64{0, 1, 100, 1000, 1000000000} - - for _, testValue := range testCases { - t.Run(fmt.Sprintf("Value_%d", testValue), func(t *testing.T) { - var retUint64 uint64 - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_u64"}, "-"), - primitives.Finalized, - map[string]any{ - "val": testValue, - }, - &retUint64, - ) - require.NoError(t, err) - require.Equal(t, testValue, retUint64) - }) - } +// TestLoopChainReader_ProxiesCalls tests that all non-transforming calls are proxied +// directly to the underlying reader. +func TestLoopChainReader_ProxiesCalls(t *testing.T) { + t.Parallel() + log := logger.Test(t) + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + + t.Run("Name", func(t *testing.T) { + mockReader.On("Name").Return("test-reader") + name := loopReader.Name() + require.Equal(t, "test-reader", name) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_GetLatestValue_EchoU256", func(t *testing.T) { - t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") - testValue := big.NewInt(123456789) - var retBigInt *big.Int - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_u256"}, "-"), - primitives.Finalized, - map[string]any{ - "val": testValue, - }, - &retBigInt, - ) + + t.Run("Ready", func(t *testing.T) { + mockReader.On("Ready").Return(nil) + err := loopReader.Ready() require.NoError(t, err) - require.Equal(t, testValue, retBigInt) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_GetLatestValue_EchoU256_LargeValue", func(t *testing.T) { - t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") - // Test with a very large number - testValue := new(big.Int) - testValue.SetString("123456789012345678901234567890", 10) - var retBigInt *big.Int - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_u256"}, "-"), - primitives.Finalized, - map[string]any{ - "val": testValue, - }, - &retBigInt, - ) - require.NoError(t, err) - require.Equal(t, testValue, retBigInt) + + t.Run("HealthReport", func(t *testing.T) { + report := map[string]error{"test": nil} + mockReader.On("HealthReport").Return(report) + result := loopReader.HealthReport() + require.Equal(t, report, result) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_GetLatestValue_EchoTuple", func(t *testing.T) { - testVal1 := uint32(100) - testVal2 := uint64(200) - - type TupleResult struct { - First uint32 `json:"first"` - Second uint64 `json:"second"` - } - - var retTuple TupleResult - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_u32_u64_tuple"}, "-"), - primitives.Finalized, - map[string]any{ - "val1": testVal1, - "val2": testVal2, - }, - &retTuple, - ) + + t.Run("Start", func(t *testing.T) { + mockReader.On("Start", ctx).Return(nil) + err := loopReader.Start(ctx) require.NoError(t, err) - require.Equal(t, testVal1, retTuple.First) - require.Equal(t, testVal2, retTuple.Second) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_GetLatestValue_EchoString", func(t *testing.T) { - t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") - testString := "Hello, Sui!" - var retString string - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, echoBinding.Name, "echo_string"}, "-"), - primitives.Finalized, - map[string]any{ - "val": testString, - }, - &retString, - ) + + t.Run("Close", func(t *testing.T) { + mockReader.On("Close").Return(nil) + err := loopReader.Close() require.NoError(t, err) - require.Equal(t, testString, retString) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_EchoWithEvents_AndQueryEvents", func(t *testing.T) { - // Test data - testNumber := uint64(12345) - - // First, call the function that emits events - var retUint64 uint64 - err = loopReader.GetLatestValue( - ctx, - strings.Join([]string{packageId, echoBinding.Name, "simple_event_echo"}, "-"), - primitives.Finalized, - map[string]any{ - "number": testNumber, - }, - &retUint64, - ) + + t.Run("Bind", func(t *testing.T) { + bindings := []types.BoundContract{{Name: "test", Address: "0x123"}} + mockReader.On("Bind", ctx, bindings).Return(nil) + err := loopReader.Bind(ctx, bindings) require.NoError(t, err) - - // Define event structures to match the Move contract - type SingleValueEvent struct { - Value uint64 `json:"value"` - } - - // Query for SingleValueEvent - t.Run("QuerySingleValueEvent", func(t *testing.T) { - singleValueEvent := &SingleValueEvent{} - var sequences []types.Sequence - //nolint:govet - var err error - - evIndexer.AddEventSelector(ctx, &client.EventSelector{ - Package: packageId, - Module: "echo", - Event: "SingleValueEvent", - }) - - // Use relayerClient to call increment instead of using CLI - moveCallReq := client.MoveCallRequest{ - Signer: accountAddress, - PackageObjectId: packageId, - Module: "echo", - Function: "simple_event_echo", - TypeArguments: []any{}, - Arguments: []any{ - uint64(testNumber), - }, - GasBudget: 2000000, - } - - log.Debugw("Calling moveCall", "moveCallReq", moveCallReq) - - txMetadata, err := relayerClient.MoveCall(ctx, moveCallReq) - require.NoError(t, err) - - txResponse, err := relayerClient.SignAndSendTransaction(ctx, txMetadata.TxBytes, publicKeyBytes) - require.NoError(t, err) - - // Make sure the transaction succeeded to make sure the event is indexed - require.Eventually(t, func() bool { - status, err := relayerClient.GetTransactionStatus(ctx, txResponse.Transaction.GetDigest()) - log.Debugw("Transaction status for SingleValueEvent", "status", status, "error", err) - return err == nil && status.Status == "success" - }, 30*time.Second, 1*time.Second) - - require.Eventually(t, func() bool { - sequences, err = loopReader.QueryKey( - ctx, - echoBinding, - query.KeyFilter{ - Key: "single_value_event", - }, - query.LimitAndSort{ - SortBy: []query.SortBy{}, - Limit: query.CountLimit(10), - }, - singleValueEvent, - ) - if err != nil { - log.Errorw("Error querying for SingleValueEvent", "err", err) - } - - return err == nil && len(sequences) > 0 - }, 60*time.Second, 1*time.Second) - - require.NoError(t, err) - require.NotEmpty(t, sequences, "Expected to find SingleValueEvent") - log.Debugw("Sequences found", "sequences", sequences) - }) + mockReader.AssertExpectations(t) }) - - t.Run("LoopReader_GetLatestValue_GetTupleStruct", func(t *testing.T) { - t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") - var retTupleStruct map[string]any - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, counterBinding.Name, "get_tuple_struct"}, "-"), - primitives.Finalized, - map[string]any{}, - &retTupleStruct, - ) + + t.Run("Unbind", func(t *testing.T) { + bindings := []types.BoundContract{{Name: "test", Address: "0x123"}} + mockReader.On("Unbind", ctx, bindings).Return(nil) + err := loopReader.Unbind(ctx, bindings) require.NoError(t, err) - - log.Debugw("retTupleStruct", "retTupleStruct", retTupleStruct) - - require.NotEmpty(t, retTupleStruct, "Expected to find TupleStruct") - // Accept either float64 (from generic JSON map) or uint64 - if v, ok := retTupleStruct["value"].(float64); ok { - require.Equal(t, float64(42), v, "Expected value to be 42") - } else { - require.Equal(t, uint64(42), retTupleStruct["value"], "Expected value to be 42") - } - require.Equal(t, "0x1", retTupleStruct["address"], "Expected address to be 0x1") - require.Equal(t, true, retTupleStruct["bool"], "Expected bool to be true") + mockReader.AssertExpectations(t) }) +} - t.Run("LoopReader_GetLatestValue_GetOCRConfig", func(t *testing.T) { - type ConfigInfo struct { - ConfigDigest []byte `json:"config_digest"` - BigF uint64 `json:"big_f"` - N uint64 `json:"n"` - IsSignatureVerificationEnabled bool `json:"is_signature_verification_enabled"` - } - - type OCRConfig struct { - ConfigInfo ConfigInfo `json:"config_info"` - Signers [][]byte `json:"signers"` - Transmitters [][]byte `json:"transmitters"` - } - - type OCRConfigWrapped struct { - OCRConfig OCRConfig `json:"OCRConfig"` +// TestLoopChainReader_BatchGetLatestValues_VerifiesLOOPBoundary tests that +// BatchGetLatestValues properly serializes all requests and deserializes responses. +func TestLoopChainReader_BatchGetLatestValues_VerifiesLOOPBoundary(t *testing.T) { + t.Parallel() + log := logger.Test(t) + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + + request := types.BatchGetLatestValuesRequest{ + types.BoundContract{Name: "contract1", Address: "0x1"}: { + {ReadName: "read1", Params: map[string]any{"key": "value1"}}, + }, + } + + mockResponse := types.BatchGetLatestValuesResult{ + types.BoundContract{Name: "contract1", Address: "0x1"}: { + {ReadName: "read1"}, + }, + } + + // The mock will receive converted requests with []byte params + mockReader.On("BatchGetLatestValues", ctx, mock.Anything).Run(func(args mock.Arguments) { + // Verify the request was converted to use []byte params + convertedRequest := args.Get(1).(types.BatchGetLatestValuesRequest) + for _, reads := range convertedRequest { + for _, read := range reads { + // Params should be []byte after serialization + _, ok := read.Params.([]byte) + require.True(t, ok, "params should be []byte after LOOP serialization") + } } + }).Return(mockResponse, nil) + + result, err := loopReader.BatchGetLatestValues(ctx, request) + + require.NoError(t, err) + require.NotNil(t, result) + mockReader.AssertExpectations(t) +} - var retOCRConfig OCRConfigWrapped - err = loopReader.GetLatestValue( - context.Background(), - strings.Join([]string{packageId, counterBinding.Name, "get_ocr_config"}, "-"), - primitives.Finalized, - map[string]any{}, - &retOCRConfig, - ) - - require.NoError(t, err) - require.NotEmpty(t, retOCRConfig, "Expected to find OCRConfig") - log.Debugw("retOCRConfig", "retOCRConfig", retOCRConfig) - }) - - // Regression for the EVM->Sui commit blocker. The commit plugin's report-acceptance check - // (plugincommon.ConfigDigestsMatch -> ccipReader.GetOffRampConfigDigest) reads the OffRamp - // latest_config_details and decodes the relayer's LOOP bytes into cciptypes.OCRConfigResponse, - // then compares ConfigInfo.ConfigDigest against the DON's digest. - // - // IMPORTANT: the core node's Sui LOOP reader (suiloop.loopChainReader.decodeGLVReturnValue) does NOT - // plain json.Unmarshal into the typed target. It json.Unmarshals the bytes into a generic map and then - // runs codec.DecodeSuiJsonValue (mapstructure with a fuzzy, underscore-insensitive field matcher and - // hex/base64 type-conversion hooks). That decoder happily maps the Sui node's snake_case keys and - // decodes the base64 digest / 0x-hex transmitters correctly. The one field it can NOT map on its own is - // `big_f` -> `F` (the fuzzy matcher compares "bigf" vs "f"), so without a rename F silently stays 0. - // - // This test exercises that exact production path (via the loopReader wrapper into a typed - // OCRConfigResponse) and relies on the get_ocr_config ResultFieldRenames (big_f -> f) above. It guards - // that the OffRamp OCR config the commit plugin consumes decodes fully and correctly. - t.Run("LoopReader_GetOCRConfig_DecodesIntoCCIPOCRConfigResponse", func(t *testing.T) { - readID := strings.Join([]string{packageId, counterBinding.Name, "get_ocr_config"}, "-") - - // Capture the exact JSON bytes that cross the LOOP boundary (params/returnVal are *[]byte in - // IsLoopPlugin mode) purely for diagnostics on failure. - paramBytes, mErr := json.Marshal(map[string]any{}) - require.NoError(t, mErr) - var rawBytes []byte - require.NoError(t, chainReader.GetLatestValue(ctx, readID, primitives.Finalized, ¶mBytes, &rawBytes)) - log.Debugw("relayer-emitted get_ocr_config JSON", "json", string(rawBytes)) - - // Decode exactly like the core node does: the loopReader wrapper json.Unmarshals the LOOP bytes and - // then runs DecodeSuiJsonValue into the typed OCRConfigResponse. - var resp cciptypes.OCRConfigResponse - require.NoErrorf(t, - loopReader.GetLatestValue(ctx, readID, primitives.Finalized, map[string]any{}, &resp), - "relayer output must decode into OCRConfigResponse via the LOOP path; raw bytes: %s", string(rawBytes)) +// TestLoopChainReader_GetLatestValue_PropagateErrors tests that errors from the +// underlying reader are properly propagated. +func TestLoopChainReader_GetLatestValue_PropagateErrors(t *testing.T) { + t.Parallel() + log := logger.Test(t) + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + expectedErr := errors.New("underlying reader error") + + mockReader.On("GetLatestValue", + ctx, + "test-read", + primitives.Finalized, + mock.AnythingOfType("*[]uint8"), + mock.AnythingOfType("*[]uint8"), + ).Return(expectedErr) + + var result uint64 + err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, &result) + + require.Error(t, err) + require.ErrorIs(t, err, expectedErr) + mockReader.AssertExpectations(t) +} - // get_ocr_config returns config_digest [2,3,4,5], big_f 1, n 2, signature verification enabled. - info := resp.OCRConfig.ConfigInfo - require.Falsef(t, info.ConfigDigest.IsEmpty(), - "ConfigDigest decoded to zero from relayer output %s — a zero digest is exactly what makes the "+ - "commit plugin reject all EVM->Sui reports", string(rawBytes)) - require.Equal(t, []byte{2, 3, 4, 5}, info.ConfigDigest[:4], - "Move config_digest bytes must land in OCRConfigResponse.ConfigInfo.ConfigDigest") - require.Equal(t, uint8(1), info.F, "Move big_f must map (via rename) to OCRConfigResponse.ConfigInfo.F") - require.Equal(t, uint8(2), info.N, "Move n must map to OCRConfigResponse.ConfigInfo.N") - require.True(t, info.IsSignatureVerificationEnabled, - "Move is_signature_verification_enabled must map to OCRConfigResponse.ConfigInfo.IsSignatureVerificationEnabled") - require.NotEmpty(t, resp.OCRConfig.Signers, "signers must decode") - require.NotEmpty(t, resp.OCRConfig.Transmitters, "transmitters (vector
, 0x-hex) must decode") - }) +// TestLoopChainReader_DecodeErrors tests that decode errors are wrapped properly. +func TestLoopChainReader_DecodeErrors(t *testing.T) { + t.Parallel() + log := logger.Test(t) + mockReader := new(mockContractReader) + loopReader := NewLoopChainReader(log, mockReader) + + ctx := context.Background() + + // Return invalid JSON that can't be decoded into the target type + mockReader.On("GetLatestValue", + ctx, + "test-read", + primitives.Finalized, + mock.AnythingOfType("*[]uint8"), + mock.AnythingOfType("*[]uint8"), + ).Run(func(args mock.Arguments) { + returnVal := args.Get(4).(*[]byte) + *returnVal = []byte(`invalid json`) + }).Return(nil) + + var result uint64 + err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, &result) + + require.Error(t, err) + require.Contains(t, err.Error(), "failed to decode") + mockReader.AssertExpectations(t) } diff --git a/codec/types.go b/codec/types.go index 22d8d5601..a345b9e24 100644 --- a/codec/types.go +++ b/codec/types.go @@ -1,14 +1,94 @@ package codec import ( + "encoding/hex" "errors" + "fmt" "math/big" + "strings" + "unicode" "github.com/block-vision/sui-go-sdk/models" ) var AccountZero = make([]byte, 32) +// Object represents a Sui object with its ID and optional initial shared version. +type Object struct { + Id string + InitialSharedVersion *uint64 +} + +// IsSuiAddress returns true if addr is a valid Sui address/ObjectID. +// It is an improvement over the sui-go-sdk's IsValidSuiAddress function. +func IsSuiAddress(addr string) bool { + if !(strings.HasPrefix(addr, "0x") || strings.HasPrefix(addr, "0X")) { + return false + } + + h := addr[2:] + + // 1..64 hex chars + if len(h) == 0 || len(h) > 64 { + return false + } + + // hex only + for _, r := range h { + if !isHexRune(r) { + return false + } + } + + // hex.DecodeString requires even length; allow odd by left-padding one '0' + if len(h)%2 == 1 { + h = "0" + h + } + + b, err := hex.DecodeString(h) + if err != nil { + return false + } + + // must be ≤32 bytes (32 bytes == 64 hex chars) + return len(b) <= 32 +} + +// ToSuiAddress normalizes and validates a Sui address. +// It pads the address to 64 hex characters (32 bytes) with leading zeros. +func ToSuiAddress(address string) (string, error) { + normalized := NormalizeSuiAddress(address) + if !IsSuiAddress(string(normalized)) { + return "", fmt.Errorf("invalid sui address: %s", address) + } + + return string(normalized), nil +} + +// NormalizeSuiAddress normalizes a Sui address to 64 hex characters. +func NormalizeSuiAddress(address string) string { + if !strings.HasPrefix(address, "0x") && !strings.HasPrefix(address, "0X") { + address = "0x" + address + } + + // Remove 0x prefix for processing + h := address[2:] + + // Pad with leading zeros to 64 characters (32 bytes) + if len(h) < 64 { + h = strings.Repeat("0", 64-len(h)) + h + } + + // Convert to lowercase + h = strings.ToLower(h) + + return "0x" + h +} + +func isHexRune(r rune) bool { + return unicode.IsDigit(r) || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') +} + type PTBCommandDependency struct { CommandIndex uint16 ResultIndex *uint16 diff --git a/go.md b/go.md index 4dfab678c..375153253 100644 --- a/go.md +++ b/go.md @@ -37,9 +37,11 @@ flowchart LR click chainlink-protos/storage-service href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/workflows/go click chainlink-protos/workflows/go href "https://github.com/smartcontractkit/chainlink-protos" - chainlink-sui --> chainlink-aptos - chainlink-sui --> chainlink-ccip + chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" + chainlink-sui/codec --> chainlink-aptos + chainlink-sui/codec --> chainlink-ccip + click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" freeport click freeport href "https://github.com/smartcontractkit/freeport" go-sumtype2 @@ -66,8 +68,14 @@ flowchart LR end click chainlink-protos-repo href "https://github.com/smartcontractkit/chainlink-protos" + subgraph chainlink-sui-repo[chainlink-sui] + chainlink-sui + chainlink-sui/codec + end + click chainlink-sui-repo href "https://github.com/smartcontractkit/chainlink-sui" + classDef outline stroke-dasharray:6,fill:none; - class chainlink-common-repo,chainlink-protos-repo outline + class chainlink-common-repo,chainlink-protos-repo,chainlink-sui-repo outline ``` ## All modules ```mermaid @@ -189,9 +197,12 @@ flowchart LR click chainlink-protos/svr href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/workflows/go click chainlink-protos/workflows/go href "https://github.com/smartcontractkit/chainlink-protos" - chainlink-sui --> chainlink-aptos - chainlink-sui --> chainlink-ccip + chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" + chainlink-sui/codec --> chainlink-aptos + chainlink-sui/codec --> chainlink-ccip + chainlink-sui/codec --> chainlink-sui + click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/deployment --> chainlink-ccip/deployment click chainlink-sui/deployment href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/integration-tests --> chainlink-sui/deployment @@ -225,6 +236,7 @@ flowchart LR click grpc-proxy href "https://github.com/smartcontractkit/grpc-proxy" libocr --> go-sumtype2 click libocr href "https://github.com/smartcontractkit/libocr" + mcms --> chainlink-aptos mcms --> chainlink-canton mcms --> chainlink-ccip/chains/solana mcms --> chainlink-deployments-framework @@ -304,6 +316,7 @@ flowchart LR subgraph chainlink-sui-repo[chainlink-sui] chainlink-sui + chainlink-sui/codec chainlink-sui/deployment chainlink-sui/integration-tests end diff --git a/go.mod b/go.mod index 53b5f5d21..a23359fb1 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +replace github.com/smartcontractkit/chainlink-sui/codec => ./codec + require ( filippo.io/edwards25519 v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.5.0 // indirect diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 127365d4f..23b60e084 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -4,6 +4,8 @@ go 1.26.2 replace github.com/smartcontractkit/chainlink-sui => ../ +replace github.com/smartcontractkit/chainlink-sui/codec => ../codec + replace github.com/smartcontractkit/chainlink-sui/deployment => ../deployment require ( diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 1a7d6704f..a74375a07 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -625,8 +625,6 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5 h1:EiQx0LCPzxlfO9piSPeMCVSZAnp/BxAsPIGh/jBal18= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= diff --git a/codec/loop/connection_load_test.go b/relayer/chainreader/loop/connection_load_test.go similarity index 100% rename from codec/loop/connection_load_test.go rename to relayer/chainreader/loop/connection_load_test.go diff --git a/relayer/chainreader/loop/loop_reader_test.go b/relayer/chainreader/loop/loop_reader_test.go new file mode 100644 index 000000000..ba6b1be59 --- /dev/null +++ b/relayer/chainreader/loop/loop_reader_test.go @@ -0,0 +1,625 @@ +//go:build integration + +package loop_test + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "os" + "strings" + "testing" + "time" + + "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/reader" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil/sqltest" + "github.com/smartcontractkit/chainlink-common/pkg/types" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-common/pkg/types/query" + "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/stretchr/testify/require" + + aptosCRConfig "github.com/smartcontractkit/chainlink-aptos/relayer/chainreader/config" + + "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" + chainreaderConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/config" + "github.com/smartcontractkit/chainlink-sui/relayer/chainreader/indexer" + "github.com/smartcontractkit/chainlink-sui/relayer/client" + "github.com/smartcontractkit/chainlink-sui/relayer/codec" + "github.com/smartcontractkit/chainlink-sui/relayer/testutils" + + codecLoop "github.com/smartcontractkit/chainlink-sui/codec/loop" +) + +//nolint:paralleltest +func TestLoopChainReaderLocal(t *testing.T) { + log := logger.Test(t) + + cmd, err := testutils.StartSuiNode(testutils.CLI) + require.NoError(t, err) + + testutils.CleanupTestContracts() + + // Ensure the process is killed when the test completes. + t.Cleanup(func() { + testutils.CleanupTestContracts() + + if cmd.Process != nil { + perr := cmd.Process.Kill() + if perr != nil { + t.Logf("Failed to kill process: %v", perr) + } + } + }) + + log.Debugw("Started Sui node") + + runLoopChainReaderEchoTest(t, log, testutils.LocalGrpcURL) +} + +func runLoopChainReaderEchoTest(t *testing.T, log logger.Logger, rpcUrl string) { + t.Helper() + ctx := context.Background() + + keystoreInstance := testutils.NewTestKeystore(t) + accountAddress, publicKeyBytes := testutils.GetAccountAndKeyFromSui(keystoreInstance) + + relayerClient, clientErr := client.NewPTBClient(log, client.PTBClientConfig{ + GrpcTarget: rpcUrl, + GrpcToken: "test", + TransactionTimeout: 10 * time.Second, + MaxConcurrentRequests: 5, + KeystoreService: keystoreInstance, + DefaultRequestType: client.TransactionRequestType("WaitForLocalExecution"), + }) + require.NoError(t, clientErr) + + faucetFundErr := testutils.FundWithFaucet(log, testutils.SuiLocalnet, accountAddress) + require.NoError(t, faucetFundErr) + + chainID, err := testutils.GetChainIdentifier(rpcUrl) + require.NoError(t, err) + testutils.PatchEnvironmentTOML("contracts/test", "local", chainID) + testutils.PatchEnvironmentTOML("contracts/test_secondary", "local", chainID) + + contractPath := testutils.BuildSetup(t, "contracts/test") + gasBudget := int(2000000000) + packageId, tx, err := testutils.PublishContract(t, "counter", contractPath, accountAddress, &gasBudget) + require.NoError(t, err) + require.NotNil(t, packageId) + require.NotNil(t, tx) + + // Set up the base ChainReader with echo function configurations + chainReaderConfigs := chainreaderConfig.ChainReaderConfig{ + IsLoopPlugin: true, + Modules: map[string]*chainreaderConfig.ChainReaderModule{ + "echo": { + Name: "echo", + Functions: map[string]*chainreaderConfig.ChainReaderFunction{ + "echo_u64": { + Name: "echo_u64", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "u64", + Name: "val", + Required: true, + }, + }, + }, + "echo_u256": { + Name: "echo_u256", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "u256", + Name: "val", + Required: true, + }, + }, + }, + "echo_u32_u64_tuple": { + Name: "echo_u32_u64_tuple", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "u32", + Name: "val1", + Required: true, + }, + { + Type: "u64", + Name: "val2", + Required: true, + }, + }, + ResultTupleToStruct: []string{"first", "second"}, + }, + "echo_string": { + Name: "echo_string", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "0x1::string::String", + Name: "val", + Required: true, + }, + }, + }, + "echo_byte_vector": { + Name: "echo_byte_vector", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "vector", + Name: "val", + Required: true, + }, + }, + }, + "echo_byte_vector_vector": { + Name: "echo_byte_vector_vector", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "vector>", + Name: "val", + Required: true, + mi }, + }, + }, + "simple_event_echo": { + Name: "simple_event_echo", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{ + { + Type: "u64", + Name: "number", + Required: true, + }, + }, + }, + }, + Events: map[string]*chainreaderConfig.ChainReaderEvent{ + "single_value_event": { + Name: "single_value_event", + EventType: "SingleValueEvent", + EventSelector: client.EventSelector{ + Package: packageId, + Module: "echo", + Event: "SingleValueEvent", + }, + }, + "double_value_event": { + Name: "double_value_event", + EventType: "DoubleValueEvent", + }, + "triple_value_event": { + Name: "triple_value_event", + EventType: "TripleValueEvent", + }, + }, + }, + "counter": { + Name: "counter", + Functions: map[string]*chainreaderConfig.ChainReaderFunction{ + "get_tuple_struct": { + Name: "get_tuple_struct", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{}, + ResultTupleToStruct: []string{"value", "address", "bool", "struct_tag"}, + }, + "get_ocr_config": { + Name: "get_ocr_config", + SignerAddress: accountAddress, + Params: []codec.SuiFunctionParam{}, + // used to wrap entire result + ResultTupleToStruct: []string{"OCRConfig"}, + // The Sui node renders the Move struct with snake_case keys. The core node decodes the LOOP + // bytes into cciptypes.OCRConfigResponse via DecodeSuiJsonValue (mapstructure), whose fuzzy + // matcher strips underscores and is case-insensitive: config_info->ConfigInfo, + // config_digest->ConfigDigest, n->N, is_signature_verification_enabled->... all match on their + // own. The lone exception is `big_f` ("bigf") which does NOT match `F` ("f"), so F silently + // decodes to 0. Rename it so it lands on OCRConfigResponse.ConfigInfo.F. This mirrors the fix + // in chainlink core's Sui contract_reader.go for the OffRamp latest_config_details read. + ResultFieldRenames: map[string]aptosCRConfig.RenamedField{ + "OCRConfig": { + SubFieldRenames: map[string]aptosCRConfig.RenamedField{ + "config_info": { + SubFieldRenames: map[string]aptosCRConfig.RenamedField{ + "big_f": {NewName: "f"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + EventsIndexer: config.EventsIndexerConfig{ + PollingInterval: 2 * time.Second, + SyncTimeout: 60 * time.Second, + }, + TransactionsIndexer: config.TransactionsIndexerConfig{ + PollingInterval: 2 * time.Second, + SyncTimeout: 60 * time.Second, + }, + } + + echoBinding := types.BoundContract{ + Name: "echo", + Address: packageId, // Package ID of the deployed echo contract + } + + counterBinding := types.BoundContract{ + Name: "counter", + Address: packageId, // Package ID of the deployed echo contract + } + + // Set up DB + datastoreUrl := os.Getenv("TEST_DB_URL") + if datastoreUrl == "" { + t.Skip("Skipping persistent tests as TEST_DB_URL is not set in CI") + } + db := sqltest.NewDB(t, datastoreUrl) + + // Create the indexers + txnIndexer := indexer.NewTransactionsIndexer( + db, + log, + map[string]*config.ChainReaderEvent{}, + ) + evIndexer := indexer.NewEventIndexer( + db, + log, + []*client.EventSelector{}, + ) + + chainPoller := indexer.NewChainPoller( + relayerClient, + log, + config.ChainPollerConfig{ + PollingInterval: 1 * time.Second, + SyncTimeout: 60 * time.Second, + BackfillCheckpointCount: testutils.Uint64Pointer(uint64(1)), + }, + evIndexer.GetEventSelectors, + ) + + indexerInstance := indexer.NewIndexerFromComponents( + log, + chainPoller, + evIndexer, + txnIndexer, + ) + + chainReader, err := reader.NewChainReader(ctx, log, relayerClient, chainReaderConfigs, db, indexerInstance, nil) + require.NoError(t, err) + + // Wrap the base chain reader with loop chain reader + loopReader := codecLoop.NewLoopChainReader(log, chainReader) + + // Bind the contracts + err = loopReader.Bind(context.Background(), []types.BoundContract{echoBinding, counterBinding}) + require.NoError(t, err) + + // Start the indexers + err = indexerInstance.Start(ctx) + require.NoError(t, err) + + log.Debugw("LoopChainReader setup complete") + + t.Run("LoopReader_GetLatestValue_EchoU64", func(t *testing.T) { + testValue := uint64(42) + var retUint64 uint64 + + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_u64"}, "-"), + primitives.Finalized, + map[string]any{ + "val": testValue, + }, + &retUint64, + ) + require.NoError(t, err) + require.Equal(t, testValue, retUint64) + }) + + t.Run("LoopReader_GetLatestValue_EchoU64_VariousValues", func(t *testing.T) { + testCases := []uint64{0, 1, 100, 1000, 1000000000} + + for _, testValue := range testCases { + t.Run(fmt.Sprintf("Value_%d", testValue), func(t *testing.T) { + var retUint64 uint64 + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_u64"}, "-"), + primitives.Finalized, + map[string]any{ + "val": testValue, + }, + &retUint64, + ) + require.NoError(t, err) + require.Equal(t, testValue, retUint64) + }) + } + }) + + t.Run("LoopReader_GetLatestValue_EchoU256", func(t *testing.T) { + t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") + testValue := big.NewInt(123456789) + var retBigInt *big.Int + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_u256"}, "-"), + primitives.Finalized, + map[string]any{ + "val": testValue, + }, + &retBigInt, + ) + require.NoError(t, err) + require.Equal(t, testValue, retBigInt) + }) + + t.Run("LoopReader_GetLatestValue_EchoU256_LargeValue", func(t *testing.T) { + t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") + // Test with a very large number + testValue := new(big.Int) + testValue.SetString("123456789012345678901234567890", 10) + var retBigInt *big.Int + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_u256"}, "-"), + primitives.Finalized, + map[string]any{ + "val": testValue, + }, + &retBigInt, + ) + require.NoError(t, err) + require.Equal(t, testValue, retBigInt) + }) + + t.Run("LoopReader_GetLatestValue_EchoTuple", func(t *testing.T) { + testVal1 := uint32(100) + testVal2 := uint64(200) + + type TupleResult struct { + First uint32 `json:"first"` + Second uint64 `json:"second"` + } + + var retTuple TupleResult + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_u32_u64_tuple"}, "-"), + primitives.Finalized, + map[string]any{ + "val1": testVal1, + "val2": testVal2, + }, + &retTuple, + ) + require.NoError(t, err) + require.Equal(t, testVal1, retTuple.First) + require.Equal(t, testVal2, retTuple.Second) + }) + + t.Run("LoopReader_GetLatestValue_EchoString", func(t *testing.T) { + t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") + testString := "Hello, Sui!" + var retString string + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, echoBinding.Name, "echo_string"}, "-"), + primitives.Finalized, + map[string]any{ + "val": testString, + }, + &retString, + ) + require.NoError(t, err) + require.Equal(t, testString, retString) + }) + + t.Run("LoopReader_EchoWithEvents_AndQueryEvents", func(t *testing.T) { + // Test data + testNumber := uint64(12345) + + // First, call the function that emits events + var retUint64 uint64 + err = loopReader.GetLatestValue( + ctx, + strings.Join([]string{packageId, echoBinding.Name, "simple_event_echo"}, "-"), + primitives.Finalized, + map[string]any{ + "number": testNumber, + }, + &retUint64, + ) + require.NoError(t, err) + + // Define event structures to match the Move contract + type SingleValueEvent struct { + Value uint64 `json:"value"` + } + + // Query for SingleValueEvent + t.Run("QuerySingleValueEvent", func(t *testing.T) { + singleValueEvent := &SingleValueEvent{} + var sequences []types.Sequence + //nolint:govet + var err error + + evIndexer.AddEventSelector(ctx, &client.EventSelector{ + Package: packageId, + Module: "echo", + Event: "SingleValueEvent", + }) + + // Use relayerClient to call increment instead of using CLI + moveCallReq := client.MoveCallRequest{ + Signer: accountAddress, + PackageObjectId: packageId, + Module: "echo", + Function: "simple_event_echo", + TypeArguments: []any{}, + Arguments: []any{ + uint64(testNumber), + }, + GasBudget: 2000000, + } + + log.Debugw("Calling moveCall", "moveCallReq", moveCallReq) + + txMetadata, err := relayerClient.MoveCall(ctx, moveCallReq) + require.NoError(t, err) + + txResponse, err := relayerClient.SignAndSendTransaction(ctx, txMetadata.TxBytes, publicKeyBytes) + require.NoError(t, err) + + // Make sure the transaction succeeded to make sure the event is indexed + require.Eventually(t, func() bool { + status, err := relayerClient.GetTransactionStatus(ctx, txResponse.Transaction.GetDigest()) + log.Debugw("Transaction status for SingleValueEvent", "status", status, "error", err) + return err == nil && status.Status == "success" + }, 30*time.Second, 1*time.Second) + + require.Eventually(t, func() bool { + sequences, err = loopReader.QueryKey( + ctx, + echoBinding, + query.KeyFilter{ + Key: "single_value_event", + }, + query.LimitAndSort{ + SortBy: []query.SortBy{}, + Limit: query.CountLimit(10), + }, + singleValueEvent, + ) + if err != nil { + log.Errorw("Error querying for SingleValueEvent", "err", err) + } + + return err == nil && len(sequences) > 0 + }, 60*time.Second, 1*time.Second) + + require.NoError(t, err) + require.NotEmpty(t, sequences, "Expected to find SingleValueEvent") + log.Debugw("Sequences found", "sequences", sequences) + }) + }) + + t.Run("LoopReader_GetLatestValue_GetTupleStruct", func(t *testing.T) { + t.Skip("Skipping, the entire test suite will be removed in favor of DefaultAccessor") + var retTupleStruct map[string]any + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, counterBinding.Name, "get_tuple_struct"}, "-"), + primitives.Finalized, + map[string]any{}, + &retTupleStruct, + ) + require.NoError(t, err) + + log.Debugw("retTupleStruct", "retTupleStruct", retTupleStruct) + + require.NotEmpty(t, retTupleStruct, "Expected to find TupleStruct") + // Accept either float64 (from generic JSON map) or uint64 + if v, ok := retTupleStruct["value"].(float64); ok { + require.Equal(t, float64(42), v, "Expected value to be 42") + } else { + require.Equal(t, uint64(42), retTupleStruct["value"], "Expected value to be 42") + } + require.Equal(t, "0x1", retTupleStruct["address"], "Expected address to be 0x1") + require.Equal(t, true, retTupleStruct["bool"], "Expected bool to be true") + }) + + t.Run("LoopReader_GetLatestValue_GetOCRConfig", func(t *testing.T) { + type ConfigInfo struct { + ConfigDigest []byte `json:"config_digest"` + BigF uint64 `json:"big_f"` + N uint64 `json:"n"` + IsSignatureVerificationEnabled bool `json:"is_signature_verification_enabled"` + } + + type OCRConfig struct { + ConfigInfo ConfigInfo `json:"config_info"` + Signers [][]byte `json:"signers"` + Transmitters [][]byte `json:"transmitters"` + } + + type OCRConfigWrapped struct { + OCRConfig OCRConfig `json:"OCRConfig"` + } + + var retOCRConfig OCRConfigWrapped + err = loopReader.GetLatestValue( + context.Background(), + strings.Join([]string{packageId, counterBinding.Name, "get_ocr_config"}, "-"), + primitives.Finalized, + map[string]any{}, + &retOCRConfig, + ) + + require.NoError(t, err) + require.NotEmpty(t, retOCRConfig, "Expected to find OCRConfig") + log.Debugw("retOCRConfig", "retOCRConfig", retOCRConfig) + }) + + // Regression for the EVM->Sui commit blocker. The commit plugin's report-acceptance check + // (plugincommon.ConfigDigestsMatch -> ccipReader.GetOffRampConfigDigest) reads the OffRamp + // latest_config_details and decodes the relayer's LOOP bytes into cciptypes.OCRConfigResponse, + // then compares ConfigInfo.ConfigDigest against the DON's digest. + // + // IMPORTANT: the core node's Sui LOOP reader (suiloop.loopChainReader.decodeGLVReturnValue) does NOT + // plain json.Unmarshal into the typed target. It json.Unmarshals the LOOP bytes into a generic map and then + // runs codec.DecodeSuiJsonValue (mapstructure with a fuzzy, underscore-insensitive field matcher and + // hex/base64 type-conversion hooks). That decoder happily maps the Sui node's snake_case keys and + // decodes the base64 digest / 0x-hex transmitters correctly. The one field it can NOT map on its own is + // `big_f` -> `F` (the fuzzy matcher compares "bigf" vs "f"), so without a rename F silently stays 0. + // + // This test exercises that exact production path (via the loopReader wrapper into a typed + // OCRConfigResponse) and relies on the get_ocr_config ResultFieldRenames (big_f -> f) above. It guards + // that the OffRamp OCR config the commit plugin consumes decodes fully and correctly. + t.Run("LoopReader_GetOCRConfig_DecodesIntoCCIPOCRConfigResponse", func(t *testing.T) { + readID := strings.Join([]string{packageId, counterBinding.Name, "get_ocr_config"}, "-") + + // Capture the exact JSON bytes that cross the LOOP boundary (params/returnVal are *[]byte in + // IsLoopPlugin mode) purely for diagnostics on failure. + paramBytes, mErr := json.Marshal(map[string]any{}) + require.NoError(t, mErr) + var rawBytes []byte + require.NoError(t, chainReader.GetLatestValue(ctx, readID, primitives.Finalized, ¶mBytes, &rawBytes)) + log.Debugw("relayer-emitted get_ocr_config JSON", "json", string(rawBytes)) + + // Decode exactly like the core node does: the loopReader wrapper json.Unmarshals the LOOP bytes and + // then runs DecodeSuiJsonValue into the typed OCRConfigResponse. + var resp cciptypes.OCRConfigResponse + require.NoErrorf(t, + loopReader.GetLatestValue(ctx, readID, primitives.Finalized, map[string]any{}, &resp), + "relayer output must decode into OCRConfigResponse via the LOOP path; raw bytes: %s", string(rawBytes)) + + // get_ocr_config returns config_digest [2,3,4,5], big_f 1, n 2, signature verification enabled. + info := resp.OCRConfig.ConfigInfo + require.Falsef(t, info.ConfigDigest.IsEmpty(), + "ConfigDigest decoded to zero from relayer output %s — a zero digest is exactly what makes the "+ + "commit plugin reject all EVM->Sui reports", string(rawBytes)) + require.Equal(t, []byte{2, 3, 4, 5}, info.ConfigDigest[:4], + "Move config_digest bytes must land in OCRConfigResponse.ConfigInfo.ConfigDigest") + require.Equal(t, uint8(1), info.F, "Move big_f must map (via rename) to OCRConfigResponse.ConfigInfo.F") + require.Equal(t, uint8(2), info.N, "Move n must map to OCRConfigResponse.ConfigInfo.N") + require.True(t, info.IsSignatureVerificationEnabled, + "Move is_signature_verification_enabled must map to OCRConfigResponse.ConfigInfo.IsSignatureVerificationEnabled") + require.NotEmpty(t, resp.OCRConfig.Signers, "signers must decode") + require.NotEmpty(t, resp.OCRConfig.Transmitters, "transmitters (vector
, 0x-hex) must decode") + }) +} From 842c5b8cf73ab25f42c4b4e027349e8732679304 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Thu, 2 Jul 2026 15:12:01 -0400 Subject: [PATCH 4/8] fix --- bindings/bind/common.go | 1 + bindings/bind/json_decoder.go | 1 + bindings/bind/utils.go | 2 + codec/go.mod | 107 +------ codec/go.sum | 409 +----------------------- deployment/go.mod | 2 + deployment/go.sum | 2 - go.sum | 2 - relayer/chainreader/loop/loop_reader.go | 4 +- scripts/go.mod | 3 + scripts/go.sum | 2 - 11 files changed, 16 insertions(+), 519 deletions(-) diff --git a/bindings/bind/common.go b/bindings/bind/common.go index 5f63fc80f..274a227c9 100644 --- a/bindings/bind/common.go +++ b/bindings/bind/common.go @@ -34,6 +34,7 @@ type transactionObjectChangesClient interface { } // Object is an alias for codec.Object for backward compatibility. +// // Deprecated: use codec.Object directly. type Object = codec.Object diff --git a/bindings/bind/json_decoder.go b/bindings/bind/json_decoder.go index c37f5ca74..4117d71e2 100644 --- a/bindings/bind/json_decoder.go +++ b/bindings/bind/json_decoder.go @@ -12,6 +12,7 @@ import ( ) // DecodeJSONReturn decodes gRPC/JSON Move return values into the provided target. +// // Deprecated: use codec.DecodeJSONReturn directly. func DecodeJSONReturn(data any, target any) error { return codec.DecodeJSONReturn(data, target) diff --git a/bindings/bind/utils.go b/bindings/bind/utils.go index 4d03049e6..962c023e8 100644 --- a/bindings/bind/utils.go +++ b/bindings/bind/utils.go @@ -10,12 +10,14 @@ import ( ) // IsSuiAddress returns true if addr is a valid Sui address/ObjectID. +// // Deprecated: use codec.IsSuiAddress directly. func IsSuiAddress(addr string) bool { return codec.IsSuiAddress(addr) } // ToSuiAddress normalizes and validates a Sui address. +// // Deprecated: use codec.ToSuiAddress directly. func ToSuiAddress(address string) (string, error) { return codec.ToSuiAddress(address) diff --git a/codec/go.mod b/codec/go.mod index a8a40105c..94d205ae4 100644 --- a/codec/go.mod +++ b/codec/go.mod @@ -8,148 +8,47 @@ require ( github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 + github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 github.com/stretchr/testify v1.11.1 - google.golang.org/grpc v1.81.1 ) require ( filippo.io/edwards25519 v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.5.0 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect - github.com/XSAM/otelsql v0.37.0 // indirect - github.com/apache/arrow-go/v18 v18.3.1 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect - github.com/buger/jsonparser v1.1.2 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2 // indirect - github.com/cloudevents/sdk-go/v2 v2.16.2 // indirect github.com/coder/websocket v1.8.14 // indirect - github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/ethereum/go-ethereum v1.17.3 // indirect - github.com/fatih/color v1.19.0 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.13 // indirect - github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.3 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/flatbuffers v25.2.10+incompatible // indirect - github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/grafana/otel-profiling-go v0.5.1 // indirect - github.com/grafana/pyroscope-go v1.2.8 // indirect - github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect - github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect - github.com/hashicorp/go-hclog v1.6.3 // indirect - github.com/hashicorp/go-plugin v1.8.0 // indirect - github.com/hashicorp/yamux v0.1.2 // indirect github.com/hasura/go-graphql-client v0.15.1 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/holiman/uint256 v1.3.2 // indirect - github.com/invopop/jsonschema v0.13.0 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.9.2 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/jinzhu/copier v0.4.0 // indirect - github.com/jmoiron/sqlx v1.4.0 // indirect - github.com/jpillora/backoff v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.5 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/lib/pq v1.12.3 // indirect - github.com/mailru/easyjson v0.9.0 // indirect - github.com/marcboeker/go-duckdb v1.8.5 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/run v1.2.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.3.0 // indirect - github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v1.20.99 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - github.com/samber/lo v1.53.0 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect - github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.102 // indirect - github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect - github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect - github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect - github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e // indirect - github.com/test-go/testify v1.1.4 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/x448/float16 v0.8.4 // indirect - github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/otel v1.44.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.20.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect - go.opentelemetry.io/otel/sdk v1.44.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect golang.org/x/crypto v0.52.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/mod v0.36.0 // indirect - golang.org/x/net v0.55.0 // indirect - golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.45.0 // indirect - golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 // indirect - golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.45.0 // indirect - golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/codec/go.sum b/codec/go.sum index d42f62f93..1aca58872 100644 --- a/codec/go.sum +++ b/codec/go.sum @@ -1,330 +1,88 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= -github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= -github.com/XSAM/otelsql v0.37.0 h1:ya5RNw028JW0eJW8Ma4AmoKxAYsJSGuNVbC7F1J457A= -github.com/XSAM/otelsql v0.37.0/go.mod h1:LHbCu49iU8p255nCn1oi04oX2UjSoRcUMiKEHo2a5qM= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4Dj3ixk= -github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= -github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= -github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= github.com/aptos-labs/aptos-go-sdk v1.13.0 h1:epv7K/tIbAEO2RfogwGacICBig8rrigJj24fDsy6KTg= github.com/aptos-labs/aptos-go-sdk v1.13.0/go.mod h1:FTgKp0RLfEefllCdkCj0jPU14xWk11yA7SFVfCDLUj8= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/block-vision/sui-go-sdk v1.2.1 h1:uwvGbzfcrS4SsIaakclYxy0qgEF1XWIUtTYWXB4PoAw= github.com/block-vision/sui-go-sdk v1.2.1/go.mod h1:t8mWASwfyv+EyqHGO9ZrcDiCJWGOFEXqq50TMJ8GQco= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= -github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= -github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2 h1:ydUjnKn4RoCeN8rge3F/deT52w2WJMmIC5mHNUq+Ut8= -github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.2/go.mod h1:Bny999RuVUtNjzTGa9HCHpXjrLGMipJVq5kqVpudBl0= -github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK1D+NnQM= -github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= -github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= -github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI= github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q5LDd4Q= github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= -github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= -github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY= -github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= -github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= -github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= -github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= -github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= -github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M= -github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8= -github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= -github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= -github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= -github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= -github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.3.5 h1:b3taDMxCBCBVgyRrS1AZVHO14ubMYZB++QpNhBg+Nyo= github.com/hashicorp/go-memdb v1.3.5/go.mod h1:8IVKKBkVe+fxFgdFOYxzQQNjz+sWCyHCdIC/+5+Vy1Y= -github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs= -github.com/hashicorp/go-plugin v1.8.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= -github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hasura/go-graphql-client v0.15.1 h1:mCb5I+8Bk3FU3GKWvf/zDXkTh7FbGlqJmP3oisBdnN8= github.com/hasura/go-graphql-client v0.15.1/go.mod h1:jfSZtBER3or+88Q9vFhWHiFMPppfYILRyl+0zsgPIIw= github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= -github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= -github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= -github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= -github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= -github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= -github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= -github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= -github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= -github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= -github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v1.20.99 h1:vZEybF3CT0t6L0UjsOtHRML7vuIglHocmvJMMH/se4M= -github.com/prometheus/common v1.20.99/go.mod h1:VX44Tebe4qpuTK+MQWg25h4fJGKBqzObSdxuB7y8K/Y= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= -github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= -github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/smartcontractkit/chain-selectors v1.0.102 h1:qYP4+72HfvogCHR5ymwRFee36WH77514ZBj299SVCBA= github.com/smartcontractkit/chain-selectors v1.0.102/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb h1:uSXBvj/idCBg7hMLmek8YYNa0JKggqlTkHYdtIooeNM= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb/go.mod h1:2xVZJ3o9udYFeJhwyHXAMlNhptJ99uoiGjzfOicYNX8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 h1:5bxDnwI0wuPoC0H5H3H2n9CnQPb5iakR6UmAY4j8KUg= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= -github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= -github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= -github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= -github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= -github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 h1:L2Y1W2j+GcgGJ8Af6PkXznAUGOMwfOatk9nooE57Gx4= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5/go.mod h1:YYlFG2/G5/Q+BzByqcsvIZxS3vW8dgnxz6j+VTwEYj4= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= -github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -333,205 +91,42 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= -go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= -go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= -go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= -go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= -go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= -golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= diff --git a/deployment/go.mod b/deployment/go.mod index a70ebb5e1..5076477d7 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -7,6 +7,8 @@ replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlin replace github.com/smartcontractkit/chainlink-sui => ../ +replace github.com/smartcontractkit/chainlink-sui/codec => ../codec + require ( github.com/Masterminds/semver/v3 v3.5.0 github.com/aptos-labs/aptos-go-sdk v1.13.0 diff --git a/deployment/go.sum b/deployment/go.sum index 48e74c14b..fe3bae7b7 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -623,8 +623,6 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= diff --git a/go.sum b/go.sum index 2a17f280a..2225de6a4 100644 --- a/go.sum +++ b/go.sum @@ -308,8 +308,6 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/relayer/chainreader/loop/loop_reader.go b/relayer/chainreader/loop/loop_reader.go index 94d285f29..48ac7b615 100644 --- a/relayer/chainreader/loop/loop_reader.go +++ b/relayer/chainreader/loop/loop_reader.go @@ -8,9 +8,9 @@ import ( ) //go:fix inline -const READ_COMPONENTS_COUNT = loop.READ_COMPONENTS_COUNT +const ReadComponentsCount = loop.READ_COMPONENTS_COUNT -//Deprecated: use loop.NewLoopChainReader +// Deprecated: use loop.NewLoopChainReader. // //go:fix inline func NewLoopChainReader(log logger.Logger, reader types.ContractReader) types.ContractReader { diff --git a/scripts/go.mod b/scripts/go.mod index 0101c1d69..1215a36c3 100644 --- a/scripts/go.mod +++ b/scripts/go.mod @@ -6,6 +6,8 @@ replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlin replace github.com/smartcontractkit/chainlink-sui => ../ +replace github.com/smartcontractkit/chainlink-sui/codec => ../codec + replace github.com/smartcontractkit/chainlink-sui/deployment => ../deployment require github.com/smartcontractkit/chainlink-sui/deployment v0.0.0-00010101000000-000000000000 @@ -136,6 +138,7 @@ require ( github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect github.com/smartcontractkit/chainlink-sui v0.0.0 // indirect + github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad // indirect github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect diff --git a/scripts/go.sum b/scripts/go.sum index 4d7e3f8d7..a51b4bd72 100644 --- a/scripts/go.sum +++ b/scripts/go.sum @@ -625,8 +625,6 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad h1:TLPyJKy8O1pmjWvqzM+F6PridciiLCqtZy+dt/8Hwm0= -github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260702152904-78d359a411ad/go.mod h1:xuj5kTw87rnxV3FgB7q0kjnpkievgB9dgoCnz4plMNM= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5 h1:EiQx0LCPzxlfO9piSPeMCVSZAnp/BxAsPIGh/jBal18= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.5/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= From 6be7df12957f1baefe2b8a54b93e1eda39e1dc2b Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Thu, 2 Jul 2026 15:27:43 -0400 Subject: [PATCH 5/8] fix --- bindings/bind/common.go | 2 +- go.md | 5 +++-- relayer/chainreader/loop/loop_reader_test.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bindings/bind/common.go b/bindings/bind/common.go index 274a227c9..56b4da3c1 100644 --- a/bindings/bind/common.go +++ b/bindings/bind/common.go @@ -11,8 +11,8 @@ import ( "github.com/block-vision/sui-go-sdk/signer" "github.com/block-vision/sui-go-sdk/transaction" - "github.com/smartcontractkit/chainlink-sui/codec" bindutils "github.com/smartcontractkit/chainlink-sui/bindings/utils" + "github.com/smartcontractkit/chainlink-sui/codec" "github.com/smartcontractkit/chainlink-sui/relayer/client" ) diff --git a/go.md b/go.md index 375153253..def1d75de 100644 --- a/go.md +++ b/go.md @@ -37,10 +37,11 @@ flowchart LR click chainlink-protos/storage-service href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/workflows/go click chainlink-protos/workflows/go href "https://github.com/smartcontractkit/chainlink-protos" + chainlink-sui --> chainlink-ccip chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/codec --> chainlink-aptos - chainlink-sui/codec --> chainlink-ccip + chainlink-sui/codec --> chainlink-sui click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" freeport click freeport href "https://github.com/smartcontractkit/freeport" @@ -197,10 +198,10 @@ flowchart LR click chainlink-protos/svr href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/workflows/go click chainlink-protos/workflows/go href "https://github.com/smartcontractkit/chainlink-protos" + chainlink-sui --> chainlink-ccip chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/codec --> chainlink-aptos - chainlink-sui/codec --> chainlink-ccip chainlink-sui/codec --> chainlink-sui click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/deployment --> chainlink-ccip/deployment diff --git a/relayer/chainreader/loop/loop_reader_test.go b/relayer/chainreader/loop/loop_reader_test.go index ba6b1be59..449571bf5 100644 --- a/relayer/chainreader/loop/loop_reader_test.go +++ b/relayer/chainreader/loop/loop_reader_test.go @@ -168,7 +168,7 @@ func runLoopChainReaderEchoTest(t *testing.T, log logger.Logger, rpcUrl string) Type: "vector>", Name: "val", Required: true, - mi }, + }, }, }, "simple_event_echo": { From fbdeff742d0e5f36828ba0cc170344f7ff47f413 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Thu, 2 Jul 2026 16:33:11 -0400 Subject: [PATCH 6/8] fix test --- codec/decoder_test.go | 6 ++---- codec/go.mod | 2 +- codec/go.sum | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/codec/decoder_test.go b/codec/decoder_test.go index d31d24838..eead67443 100644 --- a/codec/decoder_test.go +++ b/codec/decoder_test.go @@ -18,8 +18,6 @@ import ( aptosBCS "github.com/aptos-labs/aptos-go-sdk/bcs" - "github.com/smartcontractkit/chainlink-sui/shared" - "github.com/stretchr/testify/require" ) @@ -895,12 +893,12 @@ func TestDecodeBase64(t *testing.T) { testData := []byte("Hello, World!") encoded := base64.StdEncoding.EncodeToString(testData) - decoded, err := shared.DecodeBase64(encoded) + decoded, err := base64.StdEncoding.DecodeString(encoded) require.NoError(t, err) require.Equal(t, testData, decoded) // Test invalid base64 - _, err = shared.DecodeBase64("invalid_base64!!!") + _, err = base64.StdEncoding.DecodeString("invalid_base64!!!") require.Error(t, err) } diff --git a/codec/go.mod b/codec/go.mod index 94d205ae4..dd0924253 100644 --- a/codec/go.mod +++ b/codec/go.mod @@ -8,7 +8,6 @@ require ( github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 - github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 github.com/stretchr/testify v1.11.1 ) @@ -45,6 +44,7 @@ require ( go.uber.org/zap v1.28.0 // indirect golang.org/x/crypto v0.52.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/sys v0.45.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/codec/go.sum b/codec/go.sum index 1aca58872..ff68b7652 100644 --- a/codec/go.sum +++ b/codec/go.sum @@ -73,8 +73,6 @@ github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5 h1:L2Y1W2j+GcgGJ8Af6PkXznAUGOMwfOatk9nooE57Gx4= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260702142810-715ee5ac4ef5/go.mod h1:YYlFG2/G5/Q+BzByqcsvIZxS3vW8dgnxz6j+VTwEYj4= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= From 2c7766372fa79236abf3e5b86d8db5e3052f7500 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Thu, 2 Jul 2026 16:51:38 -0400 Subject: [PATCH 7/8] fix --- go.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/go.md b/go.md index def1d75de..c2a06ec75 100644 --- a/go.md +++ b/go.md @@ -41,7 +41,6 @@ flowchart LR chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/codec --> chainlink-aptos - chainlink-sui/codec --> chainlink-sui click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" freeport click freeport href "https://github.com/smartcontractkit/freeport" @@ -202,7 +201,6 @@ flowchart LR chainlink-sui --> chainlink-sui/codec click chainlink-sui href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/codec --> chainlink-aptos - chainlink-sui/codec --> chainlink-sui click chainlink-sui/codec href "https://github.com/smartcontractkit/chainlink-sui" chainlink-sui/deployment --> chainlink-ccip/deployment click chainlink-sui/deployment href "https://github.com/smartcontractkit/chainlink-sui" @@ -237,7 +235,6 @@ flowchart LR click grpc-proxy href "https://github.com/smartcontractkit/grpc-proxy" libocr --> go-sumtype2 click libocr href "https://github.com/smartcontractkit/libocr" - mcms --> chainlink-aptos mcms --> chainlink-canton mcms --> chainlink-ccip/chains/solana mcms --> chainlink-deployments-framework From a7c63c5c170d5e78f9171dfa0b9b2698464ee896 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Fri, 3 Jul 2026 10:45:19 -0400 Subject: [PATCH 8/8] fix --- bindings/bind/utils.go | 7 -- codec/loop/loop_reader_test.go | 130 ++++++++++++++++++--------------- codec/types.go | 2 +- 3 files changed, 73 insertions(+), 66 deletions(-) diff --git a/bindings/bind/utils.go b/bindings/bind/utils.go index 962c023e8..eba9c6670 100644 --- a/bindings/bind/utils.go +++ b/bindings/bind/utils.go @@ -2,7 +2,6 @@ package bind import ( "fmt" - "unicode" "github.com/block-vision/sui-go-sdk/models" @@ -30,9 +29,3 @@ func GetFailedTxError(tx *models.SuiTransactionBlockResponse) error { return fmt.Errorf("transaction failed with error: %s", tx.Effects.Status.Error) } - -func isHexRune(r rune) bool { - return unicode.IsDigit(r) || - ('a' <= r && r <= 'f') || - ('A' <= r && r <= 'F') -} diff --git a/codec/loop/loop_reader_test.go b/codec/loop/loop_reader_test.go index fa6e92176..37b89f548 100644 --- a/codec/loop/loop_reader_test.go +++ b/codec/loop/loop_reader_test.go @@ -1,4 +1,4 @@ -//go:build integration +//go:build unit package loop @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "testing" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -21,6 +20,7 @@ import ( // It simulates the LOOP boundary behavior where params and return values are []byte. type mockContractReader struct { mock.Mock + types.UnimplementedContractReader } func (m *mockContractReader) Name() string { @@ -30,16 +30,30 @@ func (m *mockContractReader) Name() string { func (m *mockContractReader) GetLatestValue(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error { args := m.Called(ctx, readIdentifier, confidenceLevel, params, returnVal) - - // Simulate the LOOP boundary: copy the mock return value to the returnVal pointer - if ret := args.Get(0); ret != nil { - // returnVal is expected to be *[]byte + + switch len(args) { + case 0: + return nil + case 1: + ret := args.Get(0) + if ret == nil { + return nil + } + if err, ok := ret.(error); ok { + return err + } if rv, ok := returnVal.(*[]byte); ok { *rv = ret.([]byte) } + return nil + default: + if ret := args.Get(0); ret != nil { + if rv, ok := returnVal.(*[]byte); ok { + *rv = ret.([]byte) + } + } + return args.Error(1) } - - return args.Error(1) } func (m *mockContractReader) BatchGetLatestValues(ctx context.Context, request types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) { @@ -49,12 +63,12 @@ func (m *mockContractReader) BatchGetLatestValues(ctx context.Context, request t func (m *mockContractReader) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) { args := m.Called(ctx, contract, filter, limitAndSort, sequenceDataType) - + // Simulate LOOP boundary: sequenceDataType is *[]byte, we return mock data if ret := args.Get(0); ret != nil { return ret.([]types.Sequence), nil } - + return nil, args.Error(1) } @@ -93,41 +107,41 @@ func (m *mockContractReader) HealthReport() map[string]error { func TestLoopChainReader_GetLatestValue_VerifiesLOOPBoundary(t *testing.T) { t.Parallel() log := logger.Test(t) - + mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() readIdentifier := "test-contract-echo_u64" params := map[string]any{"val": uint64(42)} - + // Expected JSON params after serialization expectedParams, err := json.Marshal(params) require.NoError(t, err) - + // Mock return value (simulating what comes back from the LOOP boundary) mockReturn := json.RawMessage("42") - + // Set up expectation: GetLatestValue receives []byte params - mockReader.On("GetLatestValue", - ctx, - readIdentifier, - primitives.Finalized, + mockReader.On("GetLatestValue", + ctx, + readIdentifier, + primitives.Finalized, mock.AnythingOfType("*[]uint8"), // params as *[]byte mock.AnythingOfType("*[]uint8"), // returnVal as *[]byte ).Run(func(args mock.Arguments) { // Verify params were serialized correctly paramBytes := args.Get(3).(*[]byte) require.JSONEq(t, string(expectedParams), string(*paramBytes)) - + // Simulate setting the return value (as would happen across LOOP boundary) returnVal := args.Get(4).(*[]byte) *returnVal = []byte(mockReturn) }).Return(nil) - + var result uint64 err = loopReader.GetLatestValue(ctx, readIdentifier, primitives.Finalized, params, &result) - + require.NoError(t, err) require.Equal(t, uint64(42), result) mockReader.AssertExpectations(t) @@ -138,12 +152,12 @@ func TestLoopChainReader_GetLatestValue_VerifiesLOOPBoundary(t *testing.T) { func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { t.Parallel() log := logger.Test(t) - + mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() - + tests := []struct { name string mockResponse string @@ -169,11 +183,11 @@ func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { targetType: new(map[string]any), }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockReader.ExpectedCalls = nil - + mockReader.On("GetLatestValue", ctx, "test-read", @@ -184,7 +198,7 @@ func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { returnVal := args.Get(4).(*[]byte) *returnVal = []byte(tt.mockResponse) }).Return(nil) - + switch v := tt.targetType.(type) { case *uint64: err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, v) @@ -199,7 +213,7 @@ func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { require.NoError(t, err) require.Equal(t, tt.expectedResult, *v) } - + mockReader.AssertExpectations(t) }) } @@ -210,15 +224,15 @@ func TestLoopChainReader_GetLatestValue_DecodesComplexTypes(t *testing.T) { func TestLoopChainReader_QueryKey_VerifiesExpressionSerialization(t *testing.T) { t.Parallel() t.Skip("TODO: implement when QueryKey expression serialization is better understood") - + log := logger.Test(t) mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() contract := types.BoundContract{Name: "test", Address: "0x123"} filter := query.KeyFilter{ - Key: "test_event", + Key: "test_event", Expressions: []query.Expression{ // Add expression here }, @@ -226,7 +240,7 @@ func TestLoopChainReader_QueryKey_VerifiesExpressionSerialization(t *testing.T) limitAndSort := query.LimitAndSort{ Limit: query.CountLimit(10), } - + // Mock event data as would come back from LOOP mockEventData := []byte(`{"value": 123}`) mockReader.On("QueryKey", @@ -240,14 +254,14 @@ func TestLoopChainReader_QueryKey_VerifiesExpressionSerialization(t *testing.T) Data: &mockEventData, }, }, nil) - + type TestEvent struct { Value uint64 `json:"value"` } - + var event TestEvent sequences, err := loopReader.QueryKey(ctx, contract, filter, limitAndSort, &event) - + require.NoError(t, err) require.Len(t, sequences, 1) require.Equal(t, uint64(123), event.Value) @@ -260,23 +274,23 @@ func TestLoopChainReader_ProxiesCalls(t *testing.T) { log := logger.Test(t) mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() - + t.Run("Name", func(t *testing.T) { mockReader.On("Name").Return("test-reader") name := loopReader.Name() require.Equal(t, "test-reader", name) mockReader.AssertExpectations(t) }) - + t.Run("Ready", func(t *testing.T) { mockReader.On("Ready").Return(nil) err := loopReader.Ready() require.NoError(t, err) mockReader.AssertExpectations(t) }) - + t.Run("HealthReport", func(t *testing.T) { report := map[string]error{"test": nil} mockReader.On("HealthReport").Return(report) @@ -284,21 +298,21 @@ func TestLoopChainReader_ProxiesCalls(t *testing.T) { require.Equal(t, report, result) mockReader.AssertExpectations(t) }) - + t.Run("Start", func(t *testing.T) { mockReader.On("Start", ctx).Return(nil) err := loopReader.Start(ctx) require.NoError(t, err) mockReader.AssertExpectations(t) }) - + t.Run("Close", func(t *testing.T) { mockReader.On("Close").Return(nil) err := loopReader.Close() require.NoError(t, err) mockReader.AssertExpectations(t) }) - + t.Run("Bind", func(t *testing.T) { bindings := []types.BoundContract{{Name: "test", Address: "0x123"}} mockReader.On("Bind", ctx, bindings).Return(nil) @@ -306,7 +320,7 @@ func TestLoopChainReader_ProxiesCalls(t *testing.T) { require.NoError(t, err) mockReader.AssertExpectations(t) }) - + t.Run("Unbind", func(t *testing.T) { bindings := []types.BoundContract{{Name: "test", Address: "0x123"}} mockReader.On("Unbind", ctx, bindings).Return(nil) @@ -323,21 +337,21 @@ func TestLoopChainReader_BatchGetLatestValues_VerifiesLOOPBoundary(t *testing.T) log := logger.Test(t) mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() - + request := types.BatchGetLatestValuesRequest{ types.BoundContract{Name: "contract1", Address: "0x1"}: { {ReadName: "read1", Params: map[string]any{"key": "value1"}}, }, } - + mockResponse := types.BatchGetLatestValuesResult{ types.BoundContract{Name: "contract1", Address: "0x1"}: { {ReadName: "read1"}, }, } - + // The mock will receive converted requests with []byte params mockReader.On("BatchGetLatestValues", ctx, mock.Anything).Run(func(args mock.Arguments) { // Verify the request was converted to use []byte params @@ -350,9 +364,9 @@ func TestLoopChainReader_BatchGetLatestValues_VerifiesLOOPBoundary(t *testing.T) } } }).Return(mockResponse, nil) - + result, err := loopReader.BatchGetLatestValues(ctx, request) - + require.NoError(t, err) require.NotNil(t, result) mockReader.AssertExpectations(t) @@ -365,10 +379,10 @@ func TestLoopChainReader_GetLatestValue_PropagateErrors(t *testing.T) { log := logger.Test(t) mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() expectedErr := errors.New("underlying reader error") - + mockReader.On("GetLatestValue", ctx, "test-read", @@ -376,10 +390,10 @@ func TestLoopChainReader_GetLatestValue_PropagateErrors(t *testing.T) { mock.AnythingOfType("*[]uint8"), mock.AnythingOfType("*[]uint8"), ).Return(expectedErr) - + var result uint64 err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, &result) - + require.Error(t, err) require.ErrorIs(t, err, expectedErr) mockReader.AssertExpectations(t) @@ -391,9 +405,9 @@ func TestLoopChainReader_DecodeErrors(t *testing.T) { log := logger.Test(t) mockReader := new(mockContractReader) loopReader := NewLoopChainReader(log, mockReader) - + ctx := context.Background() - + // Return invalid JSON that can't be decoded into the target type mockReader.On("GetLatestValue", ctx, @@ -405,10 +419,10 @@ func TestLoopChainReader_DecodeErrors(t *testing.T) { returnVal := args.Get(4).(*[]byte) *returnVal = []byte(`invalid json`) }).Return(nil) - + var result uint64 err := loopReader.GetLatestValue(ctx, "test-read", primitives.Finalized, map[string]any{}, &result) - + require.Error(t, err) require.Contains(t, err.Error(), "failed to decode") mockReader.AssertExpectations(t) diff --git a/codec/types.go b/codec/types.go index a345b9e24..ba2c7a4be 100644 --- a/codec/types.go +++ b/codec/types.go @@ -118,7 +118,7 @@ func (p PointerTag) Validate() error { return errors.New("PointerTag.Module is required") } if p.PointerName == "" { - return errors.New("PointerTag.Pointer is required") + return errors.New("PointerTag.PointerName is required") } // FieldName is optional - it's looked up from common.PointerConfigs if p.DerivationKey == "" {