@@ -24,15 +24,31 @@ const (
2424
2525// snakeToPascal converts a snake_case string to PascalCase.
2626// e.g. "transmission_id" -> "TransmissionId", "don_id" -> "DonId"
27- func snakeToPascal (s string ) string {
28- caser := cases .Title (language .English , cases .NoLower )
27+ // Returns an error if the conversion produces an empty string (e.g. underscore-only inputs).
28+ func snakeToPascal (s string ) (string , error ) {
29+ caser := cases .Title (language .English )
2930 parts := strings .Split (s , "_" )
30- for i , part := range parts {
31+ var result strings.Builder
32+ for _ , part := range parts {
3133 if len (part ) > 0 {
32- parts [ i ] = caser .String (part )
34+ result . WriteString ( caser .String (part ) )
3335 }
3436 }
35- return strings .Join (parts , "" )
37+ if result .Len () == 0 {
38+ return "" , fmt .Errorf ("%w: field name %q converts to an empty PascalCase string" , commontypes .ErrInvalidConfig , s )
39+ }
40+ return result .String (), nil
41+ }
42+
43+ func validateUniqueFieldNames (named []commonencodings.NamedTypeCodec ) error {
44+ seen := make (map [string ]struct {}, len (named ))
45+ for _ , n := range named {
46+ if _ , exists := seen [n .Name ]; exists {
47+ return fmt .Errorf ("%w: duplicate PascalCase field name %q" , commontypes .ErrInvalidConfig , n .Name )
48+ }
49+ seen [n .Name ] = struct {}{}
50+ }
51+ return nil
3652}
3753
3854func CreateCodecEntry (idlDefinition interface {}, offChainName string , idl anchoridl.Idl , mod commoncodec.Modifier ) (entry solcommoncodec.Entry , err error ) {
@@ -163,7 +179,14 @@ func asStruct(
163179 return name , nil , err
164180 }
165181
166- named [idx ] = commonencodings.NamedTypeCodec {Name : snakeToPascal (fieldName ), Codec : typedCodec }
182+ pascalName , err := snakeToPascal (fieldName )
183+ if err != nil {
184+ return name , nil , err
185+ }
186+ named [idx ] = commonencodings.NamedTypeCodec {Name : pascalName , Codec : typedCodec }
187+ }
188+ if err := validateUniqueFieldNames (named ); err != nil {
189+ return name , nil , err
167190 }
168191 structCodec , err := commonencodings .NewStructCodec (named )
169192 if err != nil {
@@ -189,7 +212,11 @@ func asStructForInstructionArgs(
189212 if err != nil {
190213 return nil , err
191214 }
192- named [idx ] = commonencodings.NamedTypeCodec {Name : snakeToPascal (fieldName ), Codec : typedCodec }
215+ pascalName , err := snakeToPascal (fieldName )
216+ if err != nil {
217+ return nil , err
218+ }
219+ named [idx ] = commonencodings.NamedTypeCodec {Name : pascalName , Codec : typedCodec }
193220 }
194221
195222 var isVecOrArray bool
@@ -209,6 +236,10 @@ func asStructForInstructionArgs(
209236 return named [0 ].Codec , nil
210237 }
211238
239+ if err := validateUniqueFieldNames (named ); err != nil {
240+ return nil , err
241+ }
242+
212243 structCodec , err := commonencodings .NewStructCodec (named )
213244 if err != nil {
214245 return nil , err
0 commit comments