Documentation ¶
Overview ¶
Example ¶
Example demonstrates how to use the codec package. It will make use of each Modifier provided in the package, along with their config.
package main import ( "context" "encoding/json" "fmt" "math" "time" "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/types" ) // This example demonstrates how you can have config for itemTypes with one codec, // one that is modified before encoding for on-chain and modified after decoding for off-chain // the other is left unmodified during encoding and decoding. const ( anyUnmodifiedTypeName = "Unmodified" anyModifiedStructTypeName = "SecondItem" anyExtractorTypeName = "ExtractProperty" ) var _ types.RemoteCodec = &ExampleStructJSONCodec{} type ExampleStructJSONCodec struct{} func (ExampleStructJSONCodec) Encode(_ context.Context, item any, _ string) ([]byte, error) { return json.Marshal(item) } func (ExampleStructJSONCodec) GetMaxEncodingSize(_ context.Context, n int, _ string) (int, error) { // not used in the example, and not really valid for json. return math.MaxInt32, nil } func (ExampleStructJSONCodec) Decode(_ context.Context, raw []byte, into any, _ string) error { err := json.Unmarshal(raw, into) if err != nil { return fmt.Errorf("%w: %w", types.ErrInvalidType, err) } return nil } func (ExampleStructJSONCodec) GetMaxDecodingSize(ctx context.Context, n int, _ string) (int, error) { // not used in the example, and not really valid for json. return math.MaxInt32, nil } func (ExampleStructJSONCodec) CreateType(_ string, _ bool) (any, error) { // parameters here are unused in the example, but can be used to determine what type to expect. // this allows remote execution to know how to decode the incoming message // and for [codec.NewModifierCodec] to know what type to expect for intermediate phases. return &OnChainStruct{}, nil } type OnChainStruct struct { Aa int64 Bb string Cc bool Dd string Ee int64 Ff string } const config = ` [ { "Type" : "drop", "Fields" : ["Bb"] }, { "Type" : "hard code", "OnChainValues" : {"Ff" : "dog", "Bb" : "bb"}, "OffChainValues" : {"Zz" : "foo"}}, { "Type" : "rename", "Fields" : {"Aa" : "Bb"}}, { "Type" : "extract element", "Extractions" : {"Dd" : "middle"}}, { "Type" : "epoch to time", "Fields" : ["Ee"]} ] ` const extractConfig = `[ { "Type" : "extract property", "FieldName" : "Bb" } ]` // config converts the OnChainStruct to this structure type OffChainStruct struct { Bb int64 Cc bool Dd []string Ee *time.Time Zz string } // Example demonstrates how to use the codec package. // It will make use of each [Modifier] provided in the package, along with their config. func main() { mods, err := createModsFromConfig() if err != nil { fmt.Println(err) return } c, err := codec.NewModifierCodec(&ExampleStructJSONCodec{}, mods) if err != nil { fmt.Println(err) return } input := &OnChainStruct{ Aa: 10, Bb: "20", Cc: true, Dd: "great example", Ee: 631515600, Ff: "dog", } ctx := context.Background() b, err := c.Encode(ctx, input, anyUnmodifiedTypeName) if err != nil { fmt.Println(err) return } fmt.Println("Encoded: " + string(b)) output := &OnChainStruct{} if err = c.Decode(ctx, b, output, anyUnmodifiedTypeName); err != nil { fmt.Println(err) return } fmt.Printf("Decoded: %+v\n", output) anyTimeEpoch := int64(631515600) t := time.Unix(anyTimeEpoch, 0) modifedInput := &OffChainStruct{ Bb: 10, Cc: true, Dd: []string{"terrible example", "great example", "not this one :("}, Ee: &t, Zz: "foo", } b, err = c.Encode(ctx, modifedInput, anyModifiedStructTypeName) if err != nil { fmt.Println(err) return } fmt.Println("Encoded with modifications: " + string(b)) output2 := &OffChainStruct{} if err = c.Decode(ctx, b, output2, anyModifiedStructTypeName); err != nil { fmt.Println(err) return } fmt.Printf("Decoded with modifications: %+v\n", output2) // using Encode for the extractor is a lossy operation and should not be used on writes if b, err = c.Encode(ctx, "test", anyExtractorTypeName); err != nil { fmt.Println(err) return } fmt.Printf("Encoded for Extraction: %+v\n", string(b)) var extractedVal string if err = c.Decode(ctx, b, &extractedVal, anyExtractorTypeName); err != nil { fmt.Println(err) return } fmt.Printf("Decoded to extracted value: %+v\n", extractedVal) } func createModsFromConfig() (codec.Modifier, error) { modifierConfig := &codec.ModifiersConfig{} if err := json.Unmarshal([]byte(config), modifierConfig); err != nil { return nil, err } mod, err := modifierConfig.ToModifier() if err != nil { return nil, err } extractorConfig := &codec.ModifiersConfig{} if err = json.Unmarshal([]byte(extractConfig), extractorConfig); err != nil { return nil, err } exMod, err := extractorConfig.ToModifier() if err != nil { return nil, err } modByItemType := map[string]codec.Modifier{ anyModifiedStructTypeName: mod, anyUnmodifiedTypeName: codec.MultiModifier{}, anyExtractorTypeName: exMod, } return codec.NewByItemTypeModifier(modByItemType) }
Output: Encoded: {"Aa":10,"Bb":"20","Cc":true,"Dd":"great example","Ee":631515600,"Ff":"dog"} Decoded: &{Aa:10 Bb:20 Cc:true Dd:great example Ee:631515600 Ff:dog} Encoded with modifications: {"Aa":10,"Bb":"","Cc":true,"Dd":"great example","Ee":631515600,"Ff":"dog"} Decoded with modifications: &{Bb:10 Cc:true Dd:[great example] Ee:1990-01-05 05:00:00 +0000 UTC Zz:foo} Encoded for Extraction: {"Aa":0,"Bb":"test","Cc":false,"Dd":"","Ee":0,"Ff":""} Decoded to extracted value: test
Index ¶
- func BigIntHook(_, to reflect.Type, data any) (any, error)
- func Convert(from, to reflect.Value, hook mapstructure.DecodeHookFunc) error
- func EpochToTimeHook(from reflect.Type, to reflect.Type, data any) (any, error)
- func FitsInNBitsSigned(n int, bi *big.Int) bool
- func NewModifierCodec(codec types.RemoteCodec, modifier Modifier, ...) (types.RemoteCodec, error)
- func NumberHook(from reflect.Type, to reflect.Type, val any) (any, error)
- func SliceToArrayVerifySizeHook(from reflect.Type, to reflect.Type, data any) (any, error)
- type AddressBytesToStringModifierConfig
- type AddressModifier
- type DropModifierConfig
- type ElementExtractorLocation
- type ElementExtractorModifierConfig
- type EpochToTimeModifierConfig
- type HardCodeModifierConfig
- type Modifier
- func NewAddressBytesToStringModifier(fields []string, modifier AddressModifier) Modifier
- func NewByItemTypeModifier(modByItemType map[string]Modifier) (Modifier, error)
- func NewElementExtractor(fields map[string]*ElementExtractorLocation) Modifier
- func NewEpochToTimeModifier(fields []string) Modifier
- func NewHardCoder(onChain map[string]any, offChain map[string]any, ...) (Modifier, error)
- func NewPropertyExtractor(fieldName string) Modifier
- func NewRenamer(fields map[string]string) Modifier
- func NewWrapperModifier(fields map[string]string) Modifier
- type ModifierConfig
- type ModifierType
- type ModifiersConfig
- type MultiModifier
- type Number
- type PathMappingError
- type PropertyExtractorConfig
- type RenameModifierConfig
- type WrapperModifierConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BigIntHook ¶
BigIntHook is a mapstructure hook that converts number types to *math/big.Int and vice versa. Float values are cast to an int64 before being converted to a *math/big.Int.
func Convert ¶
Convert uses mapstructure and the hook provided to convert from into to. Note that the use of mapstructure is avoided if to and from are the same type, and are both pointers, or if to is a pointer to the type from is. In those cases, to is simply set to from, or to point to it. Arrays and slices are converted by converting each element using mapstructure.
func EpochToTimeHook ¶
EpochToTimeHook is a mapstructure hook that converts a unix epoch to a time.Time and vice versa. To do this, time.Unix and time.Time.Unix are used.
func FitsInNBitsSigned ¶
FitsInNBitsSigned returns if the *math/big.Int can fit in n bits as a signed integer. Namely, if it's in the range [-2^(n-1), 2^(n-1) - 1]
func NewModifierCodec ¶
func NewModifierCodec(codec types.RemoteCodec, modifier Modifier, hooks ...mapstructure.DecodeHookFunc) (types.RemoteCodec, error)
NewModifierCodec returns a codec that calls the modifier before calling codec functions. hooks are applied to the mapstructure decoding when Encode or Decode is called.
func SliceToArrayVerifySizeHook ¶
SliceToArrayVerifySizeHook is a mapstructure hook that verifies if a slice is being assigned to an array, it will have the same number of elements. The default in mapstructure is to allow the slice to be smaller and will zero out the remaining elements in that case.
Types ¶
type AddressBytesToStringModifierConfig ¶
type AddressBytesToStringModifierConfig struct { Fields []string // Modifier is skipped in JSON serialization, will be injected later. Modifier AddressModifier `json:"-"` }
AddressBytesToStringModifierConfig is used to transform address byte fields into string fields. It holds the list of fields that should be modified and the chain-specific logic to do the modifications.
func (*AddressBytesToStringModifierConfig) MarshalJSON ¶
func (c *AddressBytesToStringModifierConfig) MarshalJSON() ([]byte, error)
func (*AddressBytesToStringModifierConfig) ToModifier ¶
func (c *AddressBytesToStringModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type AddressModifier ¶
type AddressModifier interface { // EncodeAddress converts byte array representing an address into its string form using chain-specific logic. EncodeAddress([]byte) (string, error) // DecodeAddress converts a string representation of an address back into its byte array form using chain-specific logic. DecodeAddress(string) ([]byte, error) // Length returns the expected byte length of the address for the specific chain. Length() int }
AddressModifier defines the interface for encoding, decoding, and handling addresses. This interface allows for chain-specific logic to be injected into the modifier without modifying the common repository.
type DropModifierConfig ¶
type DropModifierConfig struct {
Fields []string
}
DropModifierConfig drops all fields listed. The casing of the first character is ignored to allow compatibility. Note that unused fields are ignored by types.Codec. This is only required if you want to rename a field to an already used name. For example, if a struct has fields A and B, and you want to rename A to B, then you need to either also rename B or drop it.
func (*DropModifierConfig) MarshalJSON ¶
func (d *DropModifierConfig) MarshalJSON() ([]byte, error)
func (*DropModifierConfig) ToModifier ¶
func (d *DropModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type ElementExtractorLocation ¶
type ElementExtractorLocation int
ElementExtractorLocation is used to determine which element to extract from a slice or array. The default is ElementExtractorLocationMiddle, which will extract the middle element. valid json values are "first", "middle", and "last".
const ( ElementExtractorLocationFirst ElementExtractorLocation = iota ElementExtractorLocationMiddle ElementExtractorLocationLast ElementExtractorLocationDefault = ElementExtractorLocationMiddle )
func (ElementExtractorLocation) MarshalJSON ¶
func (e ElementExtractorLocation) MarshalJSON() ([]byte, error)
func (*ElementExtractorLocation) UnmarshalJSON ¶
func (e *ElementExtractorLocation) UnmarshalJSON(b []byte) error
type ElementExtractorModifierConfig ¶
type ElementExtractorModifierConfig struct { // Key is the name of the field to extract from and the value is which element to extract. Extractions map[string]*ElementExtractorLocation }
ElementExtractorModifierConfig is used to extract an element from a slice or array
func (*ElementExtractorModifierConfig) MarshalJSON ¶
func (e *ElementExtractorModifierConfig) MarshalJSON() ([]byte, error)
func (*ElementExtractorModifierConfig) ToModifier ¶
func (e *ElementExtractorModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type EpochToTimeModifierConfig ¶
type EpochToTimeModifierConfig struct {
Fields []string
}
EpochToTimeModifierConfig is used to convert epoch seconds as uint64 fields on-chain to time.Time
func (*EpochToTimeModifierConfig) MarshalJSON ¶
func (e *EpochToTimeModifierConfig) MarshalJSON() ([]byte, error)
func (*EpochToTimeModifierConfig) ToModifier ¶
func (e *EpochToTimeModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type HardCodeModifierConfig ¶
HardCodeModifierConfig is used to hard code values into the map. Note that hard-coding values will override other values.
func (*HardCodeModifierConfig) MarshalJSON ¶
func (h *HardCodeModifierConfig) MarshalJSON() ([]byte, error)
func (*HardCodeModifierConfig) ToModifier ¶
func (h *HardCodeModifierConfig) ToModifier(onChainHooks ...mapstructure.DecodeHookFunc) (Modifier, error)
type Modifier ¶
type Modifier interface { RetypeToOffChain(onChainType reflect.Type, itemType string) (reflect.Type, error) // TransformToOnChain transforms a type returned from AdjustForInput into the outputType. // You may also pass a pointer to the type returned by AdjustForInput to get a pointer to outputType. TransformToOnChain(offChainValue any, itemType string) (any, error) // TransformToOffChain is the reverse of TransformForOnChain input. // It is used to send back the object after it has been decoded TransformToOffChain(onChainValue any, itemType string) (any, error) }
Modifier allows you to modify the off-chain type to be used on-chain, and vice-versa. A modifier is set up by retyping the on-chain type to a type used off-chain.
func NewAddressBytesToStringModifier ¶
func NewAddressBytesToStringModifier(fields []string, modifier AddressModifier) Modifier
NewAddressBytesToStringModifier creates and returns a new modifier that transforms address byte arrays to their corresponding string representation (or vice versa) based on the provided AddressModifier.
The fields parameter specifies which fields within a struct should be modified. The AddressModifier is injected into the modifier to handle chain-specific logic during the contractReader relayer configuration.
func NewByItemTypeModifier ¶
NewByItemTypeModifier returns a Modifier that uses modByItemType to determine which Modifier to use for a given itemType.
func NewElementExtractor ¶
func NewElementExtractor(fields map[string]*ElementExtractorLocation) Modifier
NewElementExtractor creates a modifier that extracts an element from a slice or array. fields is used to determine which fields to extract elements from and which element to extract. This modifier is lossy, as TransformToOffChain will always return a slice of length 1 with the single element, so calling TransformToOnChain, then TransformToOffChain will not return the original value, if it has multiple elements.
func NewEpochToTimeModifier ¶
NewEpochToTimeModifier converts all fields from time.Time off-chain to int64.
func NewHardCoder ¶
func NewHardCoder(onChain map[string]any, offChain map[string]any, hooks ...mapstructure.DecodeHookFunc) (Modifier, error)
NewHardCoder creates a modifier that will hard-code values for on-chain and off-chain types The modifier will override any values of the same name, if you need an overwritten value to be used in a different field, NewRenamer must be used before NewHardCoder.
func NewPropertyExtractor ¶
NewPropertyExtractor creates a modifier that will extract a single property from a struct. This modifier is lossy, as TransformToOffchain will discard unwanted struct properties and return a single element. Calling TransformToOnchain will result in unset properties.
func NewRenamer ¶
func NewWrapperModifier ¶
type ModifierConfig ¶
type ModifierType ¶
type ModifierType string
const ( ModifierRename ModifierType = "rename" ModifierDrop ModifierType = "drop" ModifierHardCode ModifierType = "hard code" ModifierExtractElement ModifierType = "extract element" ModifierEpochToTime ModifierType = "epoch to time" ModifierExtractProperty ModifierType = "extract property" ModifierAddressToString ModifierType = "address to string" ModifierWrapper ModifierType = "wrapper" )
type ModifiersConfig ¶
type ModifiersConfig []ModifierConfig
ModifiersConfig unmarshalls as a list of ModifierConfig by using a field called Type The values available for Type are case-insensitive and the config they require are below: - rename -> RenameModifierConfig - drop -> DropModifierConfig - hard code -> HardCodeModifierConfig - extract element -> ElementExtractorModifierConfig - epoch to time -> EpochToTimeModifierConfig - address to string -> AddressBytesToStringModifierConfig - field wrapper -> WrapperModifierConfig
func (*ModifiersConfig) ToModifier ¶
func (m *ModifiersConfig) ToModifier(onChainHooks ...mapstructure.DecodeHookFunc) (Modifier, error)
func (*ModifiersConfig) UnmarshalJSON ¶
func (m *ModifiersConfig) UnmarshalJSON(data []byte) error
type MultiModifier ¶
type MultiModifier []Modifier
MultiModifier is a Modifier that applies each element for the slice in-order (reverse order for TransformForOnChain).
func (MultiModifier) RetypeToOffChain ¶
func (MultiModifier) TransformToOffChain ¶
func (c MultiModifier) TransformToOffChain(onChainValue any, itemType string) (any, error)
func (MultiModifier) TransformToOnChain ¶
func (c MultiModifier) TransformToOnChain(offChainValue any, itemType string) (any, error)
type Number ¶
type Number string
func (Number) MarshalCBOR ¶
func (Number) MarshalJSON ¶
func (*Number) UnmarshalCBOR ¶
func (*Number) UnmarshalJSON ¶
type PathMappingError ¶
func (PathMappingError) Cause ¶
func (e PathMappingError) Cause() error
func (PathMappingError) Error ¶
func (e PathMappingError) Error() string
type PropertyExtractorConfig ¶
type PropertyExtractorConfig struct {
FieldName string
}
func (*PropertyExtractorConfig) MarshalJSON ¶
func (c *PropertyExtractorConfig) MarshalJSON() ([]byte, error)
func (*PropertyExtractorConfig) ToModifier ¶
func (c *PropertyExtractorConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type RenameModifierConfig ¶
RenameModifierConfig renames all fields in the map from the key to the value The casing of the first character is ignored to allow compatibility of go convention for public fields and on-chain names.
func (*RenameModifierConfig) MarshalJSON ¶
func (r *RenameModifierConfig) MarshalJSON() ([]byte, error)
func (*RenameModifierConfig) ToModifier ¶
func (r *RenameModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)
type WrapperModifierConfig ¶
type WrapperModifierConfig struct { // Fields key defines the fields to be wrapped and the name of the wrapper struct. // The field becomes a subfield of the wrapper struct where the name of the subfield is map value. Fields map[string]string }
WrapperModifierConfig replaces each field based on cfg map keys with a struct containing one field with the value of the original field which has is named based on map values. Wrapper modifier does not maintain the original pointers. Wrapper modifier config shouldn't edit fields that affect each other since the results are not deterministic.
Example #1: Based on this input struct: type example struct { A string } And the wrapper config defined as: {"D": "W"} Result: type example struct { D } where D is a struct that contains the original value of D under the name W: type D struct { W string } Example #2: Wrapper modifier works on any type of field, including nested fields or nested fields in slices etc.! Based on this input struct: type example struct { A []B } type B struct { C string D string } And the wrapper config defined as: {"A.C": "E", "A.D": "F"} Result: type example struct { A []B } type B struct { C type struct { E string } D type struct { F string } } Where each element of slice A under fields C.E and D.F retains the values of their respective input slice elements A.C and A.D .
func (*WrapperModifierConfig) MarshalJSON ¶
func (r *WrapperModifierConfig) MarshalJSON() ([]byte, error)
func (*WrapperModifierConfig) ToModifier ¶
func (r *WrapperModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error)