@@ -2,6 +2,9 @@ import { PublicKey } from "@solana/web3.js";
22import BN from "bn.js" ;
33import { Idl } from "../../" ;
44import {
5+ IdlEnumFields ,
6+ IdlEnumFieldsNamed ,
7+ IdlEnumFieldsTuple ,
58 IdlField ,
69 IdlInstruction ,
710 IdlType ,
@@ -140,37 +143,134 @@ type ArgsTuple<A extends IdlField[], Defined> = {
140143 ? DecodeType < A [ K ] [ "type" ] , Defined >
141144 : unknown ;
142145} & unknown [ ] ;
146+ /**
147+ * flat {a: number, b: {c: string}} into number | string
148+ */
149+ type UnboxToUnion < T > = T extends ( infer U ) [ ]
150+ ? UnboxToUnion < U >
151+ : T extends Record < string , never > // empty object, eg: named enum variant without fields
152+ ? "__empty_object__"
153+ : T extends Record < string , infer V > // object with props, eg: struct
154+ ? UnboxToUnion < V >
155+ : T ;
156+
157+ /**
158+ * decode single enum.field
159+ */
160+ declare type DecodeEnumField < F , Defined > = F extends IdlType
161+ ? DecodeType < F , Defined >
162+ : never ;
163+
164+ /**
165+ * decode enum variant: named or tuple
166+ */
167+ declare type DecodeEnumFields <
168+ F extends IdlEnumFields ,
169+ Defined
170+ > = F extends IdlEnumFieldsNamed
171+ ? {
172+ [ F2 in F [ number ] as F2 [ "name" ] ] : DecodeEnumField < F2 [ "type" ] , Defined > ;
173+ }
174+ : F extends IdlEnumFieldsTuple
175+ ? {
176+ [ F3 in keyof F as Exclude < F3 , keyof unknown [ ] > ] : DecodeEnumField <
177+ F [ F3 ] ,
178+ Defined
179+ > ;
180+ }
181+ : Record < string , never > ;
143182
144- type FieldsOfType < I extends IdlTypeDef > = NonNullable <
145- I [ "type" ] extends IdlTypeDefTyStruct
146- ? I [ "type" ] [ "fields" ]
147- : I [ "type" ] extends IdlTypeDefTyEnum
148- ? I [ "type" ] [ "variants" ] [ number ] [ "fields" ]
149- : any [ ]
150- > [ number ] ;
183+ /**
184+ * Since TypeScript do not provide OneOf helper we can
185+ * simply mark enum variants with +?
186+ */
187+ declare type DecodeEnum < K extends IdlTypeDefTyEnum , Defined > = {
188+ // X = IdlEnumVariant
189+ [ X in K [ "variants" ] [ number ] as Uncapitalize < X [ "name" ] > ] +?: DecodeEnumFields <
190+ NonNullable < X [ "fields" ] > ,
191+ Defined
192+ > ;
193+ } ;
151194
152- export type TypeDef < I extends IdlTypeDef , Defined > = {
153- [ F in FieldsOfType < I > as F [ "name" ] ] : DecodeType < F [ "type" ] , Defined > ;
195+ type DecodeStruct < I extends IdlTypeDefTyStruct , Defined > = {
196+ [ F in I [ "fields" ] [ number ] as F [ "name" ] ] : DecodeType < F [ "type" ] , Defined > ;
154197} ;
155198
199+ export type TypeDef <
200+ I extends IdlTypeDef ,
201+ Defined
202+ > = I [ "type" ] extends IdlTypeDefTyEnum
203+ ? DecodeEnum < I [ "type" ] , Defined >
204+ : I [ "type" ] extends IdlTypeDefTyStruct
205+ ? DecodeStruct < I [ "type" ] , Defined >
206+ : never ;
207+
156208type TypeDefDictionary < T extends IdlTypeDef [ ] , Defined > = {
157209 [ K in T [ number ] as K [ "name" ] ] : TypeDef < K , Defined > ;
158210} ;
159211
160- type NestedTypeDefDictionary < T extends IdlTypeDef [ ] > = {
161- [ Outer in T [ number ] as Outer [ "name" ] ] : TypeDef <
162- Outer ,
163- {
164- [ Inner in T [ number ] as Inner [ "name" ] ] : Inner extends Outer
165- ? never
166- : TypeDef < Inner , Record < string , never > > ;
167- }
168- > ;
212+ type DecodedHelper < T extends IdlTypeDef [ ] , Defined > = {
213+ [ D in T [ number ] as D [ "name" ] ] : TypeDef < D , Defined > ;
169214} ;
170215
171- export type IdlTypes < T extends Idl > = NestedTypeDefDictionary <
172- NonNullable < T [ "types" ] >
173- > ;
216+ type UnknownType = "__unknown_defined_type__" ;
217+ /**
218+ * empty "defined" object to produce UnknownType instead of never/unknown during idl types decoding
219+ * */
220+ type EmptyDefined = Record < UnknownType , never > ;
221+
222+ type RecursiveDepth2 <
223+ T extends IdlTypeDef [ ] ,
224+ Defined = EmptyDefined ,
225+ Decoded = DecodedHelper < T , Defined >
226+ > = UnknownType extends UnboxToUnion < Decoded >
227+ ? RecursiveDepth3 < T , DecodedHelper < T , Defined > >
228+ : Decoded ;
229+
230+ type RecursiveDepth3 <
231+ T extends IdlTypeDef [ ] ,
232+ Defined = EmptyDefined ,
233+ Decoded = DecodedHelper < T , Defined >
234+ > = UnknownType extends UnboxToUnion < Decoded >
235+ ? RecursiveDepth4 < T , DecodedHelper < T , Defined > >
236+ : Decoded ;
237+
238+ type RecursiveDepth4 <
239+ T extends IdlTypeDef [ ] ,
240+ Defined = EmptyDefined
241+ > = DecodedHelper < T , Defined > ;
242+
243+ /**
244+ * typescript can't handle truly recursive type (RecursiveTypes instead of RecursiveDepth2).
245+ * Hence we're doing "recursion" of depth=4 manually
246+ * */
247+ type RecursiveTypes <
248+ T extends IdlTypeDef [ ] ,
249+ Defined = EmptyDefined ,
250+ Decoded = DecodedHelper < T , Defined >
251+ > =
252+ // check if some of decoded types is Unknown (not decoded properly)
253+ UnknownType extends UnboxToUnion < Decoded >
254+ ? RecursiveDepth2 < T , DecodedHelper < T , Defined > >
255+ : Decoded ;
256+
257+ export type IdlTypes < T extends Idl > = RecursiveTypes < NonNullable < T [ "types" ] > > ;
258+
259+ type IdlEventType <
260+ I extends Idl ,
261+ Event extends NonNullable < I [ "events" ] > [ number ] ,
262+ Defined
263+ > = {
264+ [ F in Event [ "fields" ] [ number ] as F [ "name" ] ] : DecodeType < F [ "type" ] , Defined > ;
265+ } ;
266+
267+ export type IdlEvents < I extends Idl , Defined = IdlTypes < I > > = {
268+ [ E in NonNullable < I [ "events" ] > [ number ] as E [ "name" ] ] : IdlEventType <
269+ I ,
270+ E ,
271+ Defined
272+ > ;
273+ } ;
174274
175275export type IdlAccounts < T extends Idl > = TypeDefDictionary <
176276 NonNullable < T [ "accounts" ] > ,
0 commit comments