@metaplex-foundation/kinobi 0.6.0 → 0.7.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/cjs/nodes/InstructionNode.js +53 -15
  2. package/dist/cjs/nodes/InstructionNode.js.map +1 -1
  3. package/dist/cjs/renderers/js/GetJavaScriptRenderMapVisitor.js +77 -27
  4. package/dist/cjs/renderers/js/GetJavaScriptRenderMapVisitor.js.map +1 -1
  5. package/dist/cjs/renderers/js/GetJavaScriptTypeManifestVisitor.js +3 -0
  6. package/dist/cjs/renderers/js/GetJavaScriptTypeManifestVisitor.js.map +1 -1
  7. package/dist/cjs/renderers/js/GetJavaScriptValidatorBagVisitor.js +2 -0
  8. package/dist/cjs/renderers/js/GetJavaScriptValidatorBagVisitor.js.map +1 -1
  9. package/dist/cjs/renderers/js/templates/instructionsPage.njk +11 -24
  10. package/dist/cjs/renderers/js/templates/instructionsPageAccountMetas.njk +8 -51
  11. package/dist/cjs/renderers/js/templates/instructionsPageArgs.njk +34 -5
  12. package/dist/cjs/renderers/js/templates/instructionsPageResolvedInputs.njk +63 -0
  13. package/dist/cjs/visitors/BaseNodeOrNullVisitor.js +6 -1
  14. package/dist/cjs/visitors/BaseNodeOrNullVisitor.js.map +1 -1
  15. package/dist/cjs/visitors/BaseNodeVisitor.js +6 -1
  16. package/dist/cjs/visitors/BaseNodeVisitor.js.map +1 -1
  17. package/dist/cjs/visitors/BaseVoidVisitor.js +2 -0
  18. package/dist/cjs/visitors/BaseVoidVisitor.js.map +1 -1
  19. package/dist/cjs/visitors/aggregators/GetDefaultValidatorBagVisitor.js +32 -2
  20. package/dist/cjs/visitors/aggregators/GetDefaultValidatorBagVisitor.js.map +1 -1
  21. package/dist/cjs/visitors/aggregators/GetNodeInlineStringVisitor.js +4 -2
  22. package/dist/cjs/visitors/aggregators/GetNodeInlineStringVisitor.js.map +1 -1
  23. package/dist/cjs/visitors/aggregators/GetNodeTreeStringVisitor.js +6 -0
  24. package/dist/cjs/visitors/aggregators/GetNodeTreeStringVisitor.js.map +1 -1
  25. package/dist/cjs/visitors/aggregators/GetResolvedInstructionInputsVisitor.js +201 -0
  26. package/dist/cjs/visitors/aggregators/GetResolvedInstructionInputsVisitor.js.map +1 -0
  27. package/dist/cjs/visitors/aggregators/index.js +1 -1
  28. package/dist/cjs/visitors/aggregators/index.js.map +1 -1
  29. package/dist/cjs/visitors/transformers/AutoSetAnchorDiscriminatorsVisitor.js +1 -1
  30. package/dist/cjs/visitors/transformers/AutoSetAnchorDiscriminatorsVisitor.js.map +1 -1
  31. package/dist/cjs/visitors/transformers/CreateSubInstructionsFromEnumArgsVisitor.js +2 -2
  32. package/dist/cjs/visitors/transformers/CreateSubInstructionsFromEnumArgsVisitor.js.map +1 -1
  33. package/dist/cjs/visitors/transformers/FlattenInstructionArgsStructVisitor.js +1 -1
  34. package/dist/cjs/visitors/transformers/FlattenInstructionArgsStructVisitor.js.map +1 -1
  35. package/dist/cjs/visitors/transformers/SetInstructionAccountDefaultValuesVisitor.js +1 -1
  36. package/dist/cjs/visitors/transformers/SetInstructionAccountDefaultValuesVisitor.js.map +1 -1
  37. package/dist/cjs/visitors/transformers/SetInstructionDiscriminatorsVisitor.js +1 -1
  38. package/dist/cjs/visitors/transformers/SetInstructionDiscriminatorsVisitor.js.map +1 -1
  39. package/dist/cjs/visitors/transformers/UpdateInstructionsVisitor.js +8 -5
  40. package/dist/cjs/visitors/transformers/UpdateInstructionsVisitor.js.map +1 -1
  41. package/dist/cjs/visitors/transformers/UseCustomInstructionSerializerVisitor.js +1 -1
  42. package/dist/cjs/visitors/transformers/UseCustomInstructionSerializerVisitor.js.map +1 -1
  43. package/dist/types/idl/IdlInstruction.d.ts +0 -1
  44. package/dist/types/nodes/InstructionNode.d.ts +53 -14
  45. package/dist/types/renderers/js/GetJavaScriptRenderMapVisitor.d.ts +10 -2
  46. package/dist/types/renderers/js/GetJavaScriptTypeManifestVisitor.d.ts +1 -0
  47. package/dist/types/visitors/aggregators/GetResolvedInstructionInputsVisitor.d.ts +41 -0
  48. package/dist/types/visitors/aggregators/index.d.ts +1 -1
  49. package/dist/types/visitors/transformers/UpdateInstructionsVisitor.d.ts +7 -2
  50. package/package.json +1 -1
  51. package/src/idl/IdlInstruction.ts +0 -1
  52. package/src/nodes/InstructionNode.ts +99 -21
  53. package/src/renderers/js/GetJavaScriptRenderMapVisitor.ts +99 -36
  54. package/src/renderers/js/GetJavaScriptTypeManifestVisitor.ts +6 -0
  55. package/src/renderers/js/GetJavaScriptValidatorBagVisitor.ts +2 -0
  56. package/src/renderers/js/templates/instructionsPage.njk +11 -24
  57. package/src/renderers/js/templates/instructionsPageAccountMetas.njk +8 -51
  58. package/src/renderers/js/templates/instructionsPageArgs.njk +34 -5
  59. package/src/renderers/js/templates/instructionsPageResolvedInputs.njk +63 -0
  60. package/src/visitors/BaseNodeOrNullVisitor.ts +5 -0
  61. package/src/visitors/BaseNodeVisitor.ts +5 -0
  62. package/src/visitors/BaseVoidVisitor.ts +1 -0
  63. package/src/visitors/aggregators/GetDefaultValidatorBagVisitor.ts +48 -2
  64. package/src/visitors/aggregators/GetNodeInlineStringVisitor.ts +3 -2
  65. package/src/visitors/aggregators/GetNodeTreeStringVisitor.ts +6 -0
  66. package/src/visitors/aggregators/GetResolvedInstructionInputsVisitor.ts +275 -0
  67. package/src/visitors/aggregators/index.ts +1 -1
  68. package/src/visitors/transformers/AutoSetAnchorDiscriminatorsVisitor.ts +1 -0
  69. package/src/visitors/transformers/CreateSubInstructionsFromEnumArgsVisitor.ts +2 -0
  70. package/src/visitors/transformers/FlattenInstructionArgsStructVisitor.ts +1 -0
  71. package/src/visitors/transformers/SetInstructionAccountDefaultValuesVisitor.ts +1 -0
  72. package/src/visitors/transformers/SetInstructionDiscriminatorsVisitor.ts +1 -0
  73. package/src/visitors/transformers/UpdateInstructionsVisitor.ts +19 -14
  74. package/src/visitors/transformers/UseCustomInstructionSerializerVisitor.ts +1 -0
  75. package/dist/cjs/visitors/aggregators/GetResolvedInstructionAccountsVisitor.js +0 -123
  76. package/dist/cjs/visitors/aggregators/GetResolvedInstructionAccountsVisitor.js.map +0 -1
  77. package/dist/types/visitors/aggregators/GetResolvedInstructionAccountsVisitor.d.ts +0 -18
  78. package/src/visitors/aggregators/GetResolvedInstructionAccountsVisitor.ts +0 -155
@@ -0,0 +1,41 @@
1
+ import type * as nodes from '../../nodes';
2
+ import { BaseThrowVisitor } from '../BaseThrowVisitor';
3
+ type InstructionNodeInput = ({
4
+ kind: 'arg';
5
+ } & InstructionNodeArg) | ({
6
+ kind: 'account';
7
+ } & nodes.InstructionNodeAccount);
8
+ type InstructionNodeArg = {
9
+ name: string;
10
+ defaultsTo: nodes.InstructionNodeArgDefaults;
11
+ };
12
+ export type ResolvedInstructionAccount = nodes.InstructionNodeAccount & {
13
+ kind: 'account';
14
+ isPda: boolean;
15
+ dependsOn: nodes.InstructionNodeInputDependency[];
16
+ resolvedIsSigner: boolean | 'either';
17
+ resolvedIsOptional: boolean;
18
+ };
19
+ export type ResolvedInstructionArg = InstructionNodeArg & {
20
+ kind: 'arg';
21
+ dependsOn: nodes.InstructionNodeInputDependency[];
22
+ };
23
+ export type ResolvedInstructionInput = ResolvedInstructionAccount | ResolvedInstructionArg;
24
+ export declare class GetResolvedInstructionInputsVisitor extends BaseThrowVisitor<ResolvedInstructionInput[]> {
25
+ protected stack: InstructionNodeInput[];
26
+ protected resolved: ResolvedInstructionInput[];
27
+ protected visitedAccounts: Map<string, ResolvedInstructionAccount>;
28
+ protected visitedArgs: Map<string, ResolvedInstructionArg>;
29
+ protected error: string | null;
30
+ getError(): string | null;
31
+ visitInstruction(instruction: nodes.InstructionNode): ResolvedInstructionInput[];
32
+ resolveInstructionInput(instruction: nodes.InstructionNode, input: InstructionNodeInput): void;
33
+ resolveInstructionAccount(instruction: nodes.InstructionNode, account: nodes.InstructionNodeAccount & {
34
+ kind: 'account';
35
+ }): ResolvedInstructionAccount;
36
+ resolveInstructionArg(instruction: nodes.InstructionNode, arg: InstructionNodeArg & {
37
+ kind: 'arg';
38
+ }): ResolvedInstructionArg;
39
+ resolveInstructionDependencies(instruction: nodes.InstructionNode, parent: InstructionNodeInput, dependencies: nodes.InstructionNodeInputDependency[]): void;
40
+ }
41
+ export {};
@@ -3,4 +3,4 @@ export * from './GetDefaultValidatorBagVisitor';
3
3
  export * from './GetDefinedTypeHistogramVisitor';
4
4
  export * from './GetNodeInlineStringVisitor';
5
5
  export * from './GetNodeTreeStringVisitor';
6
- export * from './GetResolvedInstructionAccountsVisitor';
6
+ export * from './GetResolvedInstructionInputsVisitor';
@@ -1,11 +1,12 @@
1
1
  import * as nodes from '../../nodes';
2
+ import { Dependency } from '../Dependency';
2
3
  import { InstructionNodeAccountDefaultsInput } from './SetInstructionAccountDefaultValuesVisitor';
3
4
  import { NodeTransformer, TransformNodesVisitor } from './TransformNodesVisitor';
4
5
  export type InstructionUpdates = NodeTransformer<nodes.InstructionNode> | {
5
6
  delete: true;
6
7
  } | (InstructionMetadataUpdates & {
7
- accounts?: InstructionAccountUpdates;
8
8
  args?: Record<string, string>;
9
+ extraArgs?: nodes.TypeStructNode | nodes.TypeDefinedLinkNode | null;
9
10
  });
10
11
  export type InstructionMetadataUpdates = Partial<Omit<nodes.InstructionNodeMetadata, 'bytesCreatedOnChain'> & {
11
12
  bytesCreatedOnChain: InstructionNodeBytesCreatedOnChainInput | null;
@@ -25,8 +26,12 @@ type InstructionNodeBytesCreatedOnChainInput = {
25
26
  } | {
26
27
  kind: 'account';
27
28
  name: string;
28
- dependency?: string;
29
+ dependency?: Dependency;
29
30
  includeHeader?: boolean;
31
+ } | {
32
+ kind: 'resolver';
33
+ name: string;
34
+ dependency?: Dependency;
30
35
  };
31
36
  export declare class UpdateInstructionsVisitor extends TransformNodesVisitor {
32
37
  readonly map: Record<string, InstructionUpdates>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaplex-foundation/kinobi",
3
- "version": "0.6.0",
3
+ "version": "0.7.0-alpha.0",
4
4
  "description": "Generate powerful clients for your Solana programs",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -17,7 +17,6 @@ export type IdlInstructionAccount = {
17
17
  isOptional?: boolean;
18
18
  optional?: boolean;
19
19
  desc?: string;
20
- pdaBumpArg?: string;
21
20
  };
22
21
 
23
22
  export type IdlInstructionArg = {
@@ -17,6 +17,7 @@ export type InstructionNodeMetadata = {
17
17
  docs: string[];
18
18
  internal: boolean;
19
19
  bytesCreatedOnChain: InstructionNodeBytesCreatedOnChain | null;
20
+ argDefaults: Record<string, InstructionNodeArgDefaults>;
20
21
  };
21
22
 
22
23
  export type InstructionNodeAccount = {
@@ -26,10 +27,28 @@ export type InstructionNodeAccount = {
26
27
  isOptional: boolean;
27
28
  description: string;
28
29
  defaultsTo: InstructionNodeAccountDefaults | null;
29
- pdaBumpArg: string | null;
30
30
  };
31
31
 
32
+ export type InstructionNodeInputDependency = {
33
+ kind: 'account' | 'arg';
34
+ name: string;
35
+ };
36
+
37
+ export type InstructionNodeArgDefaults =
38
+ | { kind: 'arg'; name: string }
39
+ | { kind: 'account'; name: string }
40
+ | { kind: 'accountBump'; name: string }
41
+ | { kind: 'value'; value: ValueNode }
42
+ | {
43
+ kind: 'resolver';
44
+ name: string;
45
+ dependency: Dependency;
46
+ dependsOn: InstructionNodeInputDependency[];
47
+ };
48
+
32
49
  export type InstructionNodeAccountDefaults =
50
+ | { kind: 'programId' }
51
+ | { kind: 'program'; program: { name: string; publicKey: string } }
33
52
  | { kind: 'publicKey'; publicKey: string }
34
53
  | { kind: 'account'; name: string }
35
54
  | { kind: 'identity' }
@@ -40,8 +59,14 @@ export type InstructionNodeAccountDefaults =
40
59
  dependency: Dependency;
41
60
  seeds: Record<string, InstructionNodeAccountDefaultsSeed>;
42
61
  }
43
- | { kind: 'program'; program: { name: string; publicKey: string } }
44
- | { kind: 'programId' };
62
+ | {
63
+ kind: 'resolver';
64
+ name: string;
65
+ dependency: Dependency;
66
+ resolvedIsSigner: boolean | 'either';
67
+ resolvedIsOptional: boolean;
68
+ dependsOn: InstructionNodeInputDependency[];
69
+ };
45
70
 
46
71
  export type InstructionNodeAccountDefaultsSeed =
47
72
  | { kind: 'account'; name: string }
@@ -54,9 +79,10 @@ export type InstructionNodeBytesCreatedOnChain =
54
79
  | {
55
80
  kind: 'account';
56
81
  name: string;
57
- dependency: string;
82
+ dependency: Dependency;
58
83
  includeHeader: boolean;
59
- };
84
+ }
85
+ | { kind: 'resolver'; name: string; dependency: Dependency };
60
86
 
61
87
  export class InstructionNode implements Visitable {
62
88
  readonly nodeClass = 'InstructionNode' as const;
@@ -67,12 +93,15 @@ export class InstructionNode implements Visitable {
67
93
 
68
94
  readonly args: TypeStructNode | TypeDefinedLinkNode;
69
95
 
96
+ readonly extraArgs: TypeStructNode | TypeDefinedLinkNode | null;
97
+
70
98
  readonly subInstructions: InstructionNode[];
71
99
 
72
100
  constructor(
73
101
  metadata: InstructionNodeMetadata,
74
102
  accounts: InstructionNodeAccount[],
75
103
  args: InstructionNode['args'],
104
+ extraArgs: InstructionNode['extraArgs'],
76
105
  subInstructions: InstructionNode[]
77
106
  ) {
78
107
  const bytes = metadata.bytesCreatedOnChain;
@@ -83,6 +112,21 @@ export class InstructionNode implements Visitable {
83
112
  bytes && 'name' in bytes
84
113
  ? { ...bytes, name: mainCase(bytes.name) }
85
114
  : bytes,
115
+ argDefaults: Object.fromEntries(
116
+ Object.entries(metadata.argDefaults).map(([key, value]) => {
117
+ const newValue = { ...value };
118
+ if ('name' in newValue) {
119
+ newValue.name = mainCase(newValue.name);
120
+ }
121
+ if (newValue.kind === 'resolver') {
122
+ newValue.dependsOn = newValue.dependsOn.map((dep) => ({
123
+ ...dep,
124
+ name: mainCase(dep.name),
125
+ }));
126
+ }
127
+ return [mainCase(key), newValue];
128
+ })
129
+ ),
86
130
  };
87
131
  this.accounts = accounts.map((account) => {
88
132
  const { defaultsTo } = account;
@@ -100,10 +144,17 @@ export class InstructionNode implements Visitable {
100
144
  : { ...seed, name: mainCase(seed.name) },
101
145
  ])
102
146
  );
147
+ } else if (defaultsTo?.kind === 'resolver') {
148
+ defaultsTo.name = mainCase(defaultsTo.name);
149
+ defaultsTo.dependsOn = defaultsTo.dependsOn.map((dep) => ({
150
+ ...dep,
151
+ name: mainCase(dep.name),
152
+ }));
103
153
  }
104
154
  return { ...account, name: mainCase(account.name), defaultsTo };
105
155
  });
106
156
  this.args = args;
157
+ this.extraArgs = extraArgs;
107
158
  this.subInstructions = subInstructions;
108
159
  }
109
160
 
@@ -118,6 +169,7 @@ export class InstructionNode implements Visitable {
118
169
  docs: idl.docs ?? [],
119
170
  internal: false,
120
171
  bytesCreatedOnChain: null,
172
+ argDefaults: {},
121
173
  };
122
174
 
123
175
  const accounts = (idl.accounts ?? []).map(
@@ -135,7 +187,6 @@ export class InstructionNode implements Visitable {
135
187
  isOptional && useProgramIdForOptionalAccounts
136
188
  ? { kind: 'programId' }
137
189
  : null,
138
- pdaBumpArg: account.pdaBumpArg ?? null,
139
190
  };
140
191
  }
141
192
  );
@@ -164,7 +215,7 @@ export class InstructionNode implements Visitable {
164
215
  ]);
165
216
  }
166
217
 
167
- return new InstructionNode(metadata, accounts, args, []);
218
+ return new InstructionNode(metadata, accounts, args, null, []);
168
219
  }
169
220
 
170
221
  accept<T>(visitor: Visitor<T>): T {
@@ -186,20 +237,16 @@ export class InstructionNode implements Visitable {
186
237
  return this.metadata.docs;
187
238
  }
188
239
 
189
- get isLinked(): boolean {
240
+ get hasLinkedArgs(): boolean {
190
241
  return isTypeDefinedLinkNode(this.args);
191
242
  }
192
243
 
193
- get hasAccounts(): boolean {
194
- return this.accounts.length > 0;
195
- }
196
-
197
- get pdaAccounts(): InstructionNodeAccount[] {
198
- return this.accounts.filter((account) => account.pdaBumpArg !== null);
244
+ get hasLinkedExtraArgs(): boolean {
245
+ return isTypeDefinedLinkNode(this.extraArgs);
199
246
  }
200
247
 
201
- get hasPdaAccounts(): boolean {
202
- return this.pdaAccounts.length > 0;
248
+ get hasAccounts(): boolean {
249
+ return this.accounts.length > 0;
203
250
  }
204
251
 
205
252
  get hasData(): boolean {
@@ -215,12 +262,43 @@ export class InstructionNode implements Visitable {
215
262
  return nonOmittedFields.length > 0;
216
263
  }
217
264
 
218
- get hasRequiredArgs(): boolean {
219
- if (isTypeDefinedLinkNode(this.args)) return true;
220
- const requiredFields = this.args.fields.filter(
221
- (field) => field.metadata.defaultsTo === null
265
+ get hasExtraArgs(): boolean {
266
+ if (isTypeDefinedLinkNode(this.extraArgs)) return true;
267
+ const nonOmittedFields =
268
+ this.extraArgs?.fields.filter(
269
+ (field) => field.metadata.defaultsTo?.strategy !== 'omitted'
270
+ ) ?? [];
271
+ return nonOmittedFields.length > 0;
272
+ }
273
+
274
+ get hasAnyArgs(): boolean {
275
+ return this.hasArgs || this.hasExtraArgs;
276
+ }
277
+
278
+ get hasArgDefaults(): boolean {
279
+ return Object.keys(this.metadata.argDefaults).length > 0;
280
+ }
281
+
282
+ get hasArgResolvers(): boolean {
283
+ return Object.values(this.metadata.argDefaults).some(
284
+ ({ kind }) => kind === 'resolver'
285
+ );
286
+ }
287
+
288
+ get hasAccountResolvers(): boolean {
289
+ return this.accounts.some(
290
+ ({ defaultsTo }) => defaultsTo?.kind === 'resolver'
291
+ );
292
+ }
293
+
294
+ get hasByteResolver(): boolean {
295
+ return this.metadata.bytesCreatedOnChain?.kind === 'resolver';
296
+ }
297
+
298
+ get hasResolvers(): boolean {
299
+ return (
300
+ this.hasArgResolvers || this.hasAccountResolvers || this.hasByteResolver
222
301
  );
223
- return requiredFields.length > 0;
224
302
  }
225
303
  }
226
304
 
@@ -6,9 +6,10 @@ import { camelCase, pascalCase } from '../../utils';
6
6
  import {
7
7
  Visitor,
8
8
  BaseThrowVisitor,
9
- GetResolvedInstructionAccountsVisitor,
10
- ResolvedInstructionAccount,
9
+ GetResolvedInstructionInputsVisitor,
11
10
  Dependency,
11
+ ResolvedInstructionInput,
12
+ ResolvedInstructionAccount,
12
13
  } from '../../visitors';
13
14
  import { RenderMap } from '../RenderMap';
14
15
  import { resolveTemplate } from '../utils';
@@ -38,6 +39,9 @@ export type GetJavaScriptRenderMapOptions = {
38
39
  setImportStrategy: (
39
40
  importStrategy: 'all' | 'looseOnly' | 'strictOnly'
40
41
  ) => void;
42
+ setDefinedName: (
43
+ definedName: { strict: string; loose: string } | null
44
+ ) => void;
41
45
  };
42
46
  };
43
47
 
@@ -46,9 +50,7 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
46
50
 
47
51
  private program: nodes.ProgramNode | null = null;
48
52
 
49
- private resolvedInstructionAccountVisitor: Visitor<
50
- ResolvedInstructionAccount[]
51
- >;
53
+ private resolvedInstructionInputVisitor: Visitor<ResolvedInstructionInput[]>;
52
54
 
53
55
  constructor(options: GetJavaScriptRenderMapOptions = {}) {
54
56
  super();
@@ -72,8 +74,8 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
72
74
  typeManifestVisitor:
73
75
  options.typeManifestVisitor ?? new GetJavaScriptTypeManifestVisitor(),
74
76
  };
75
- this.resolvedInstructionAccountVisitor =
76
- new GetResolvedInstructionAccountsVisitor();
77
+ this.resolvedInstructionInputVisitor =
78
+ new GetResolvedInstructionInputsVisitor();
77
79
  }
78
80
 
79
81
  visitRoot(root: nodes.RootNode): RenderMap {
@@ -305,28 +307,42 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
305
307
  'transactionBuilder',
306
308
  ]);
307
309
 
308
- // Accounts.
309
- const accounts = instruction
310
- .accept(this.resolvedInstructionAccountVisitor)
311
- .map((account) => {
312
- const hasDefaultValue = !!account.defaultsTo;
313
- if (account.defaultsTo?.kind === 'pda') {
314
- const { seeds } = account.defaultsTo;
310
+ // Resolved inputs.
311
+ const resolvedInputs = instruction
312
+ .accept(this.resolvedInstructionInputVisitor)
313
+ .map((input: ResolvedInstructionInput) => {
314
+ if (input.kind === 'account' && input.defaultsTo?.kind === 'pda') {
315
+ const { seeds } = input.defaultsTo;
315
316
  Object.keys(seeds).forEach((seed: string) => {
316
317
  const seedValue = seeds[seed];
317
318
  if (seedValue.kind !== 'value') return;
318
- const seedManifest = renderJavaScriptValueNode(seedValue.value);
319
- (seedValue as any).render = seedManifest.render;
320
- imports.mergeWith(seedManifest.imports);
319
+ const valueManifest = renderJavaScriptValueNode(seedValue.value);
320
+ (seedValue as any).render = valueManifest.render;
321
+ imports.mergeWith(valueManifest.imports);
321
322
  });
322
323
  }
323
- return {
324
- ...account,
325
- type: this.getInstructionAccountType(account),
326
- optionalSign: hasDefaultValue || account.isOptional ? '?' : '',
327
- hasDefaultValue,
328
- };
324
+ if (input.kind === 'arg' && input.defaultsTo.kind === 'value') {
325
+ const { defaultsTo } = input;
326
+ const valueManifest = renderJavaScriptValueNode(defaultsTo.value);
327
+ (defaultsTo as any).render = valueManifest.render;
328
+ imports.mergeWith(valueManifest.imports);
329
+ }
330
+ return input;
329
331
  });
332
+
333
+ // Accounts.
334
+ const accounts = instruction.accounts.map((account) => {
335
+ const hasDefaultValue = !!account.defaultsTo;
336
+ const resolvedAccount = resolvedInputs.find(
337
+ (input) => input.kind === 'account' && input.name === account.name
338
+ ) as ResolvedInstructionAccount;
339
+ return {
340
+ ...resolvedAccount,
341
+ type: this.getInstructionAccountType(resolvedAccount),
342
+ optionalSign: hasDefaultValue || account.isOptional ? '?' : '',
343
+ hasDefaultValue,
344
+ };
345
+ });
330
346
  imports.mergeWith(this.getInstructionAccountImports(accounts));
331
347
  if (accounts.length > 0) {
332
348
  imports
@@ -334,16 +350,41 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
334
350
  .addAlias('core', 'checkForIsWritableOverride', 'isWritable');
335
351
  }
336
352
 
337
- // Arguments.
338
- const typeManifest = instruction.accept(this.typeManifestVisitor);
339
- imports.mergeWith(typeManifest.imports);
353
+ // Args.
354
+ const argManifest = instruction.accept(this.typeManifestVisitor);
355
+ imports.mergeWith(argManifest.imports);
340
356
  if (!nodes.isTypeDefinedLinkNode(instruction.args) && instruction.hasData) {
341
357
  imports.add('core', ['Serializer']);
342
358
  }
343
359
 
360
+ // Extra args.
361
+ let extraArgManifest: JavaScriptTypeManifest | null = null;
362
+ if (instruction.extraArgs) {
363
+ this.typeManifestVisitor.setDefinedName({
364
+ strict: `${pascalCase(instruction.name)}InstructionExtra`,
365
+ loose: `${pascalCase(instruction.name)}InstructionExtraArgs`,
366
+ });
367
+ if (nodes.isTypeDefinedLinkNode(instruction.extraArgs)) {
368
+ this.typeManifestVisitor.setImportStrategy('looseOnly');
369
+ }
370
+ extraArgManifest = instruction.extraArgs.accept(this.typeManifestVisitor);
371
+ this.typeManifestVisitor.setDefinedName(null);
372
+ this.typeManifestVisitor.setImportStrategy('all');
373
+ imports.mergeWith(extraArgManifest.imports);
374
+ }
375
+
376
+ // Arg defaults.
377
+ const argDefaultKeys = Object.keys(instruction.metadata.argDefaults);
378
+ const argDefaults = Object.values(instruction.metadata.argDefaults);
379
+ argDefaults.forEach((argDefault) => {
380
+ if (argDefault.kind === 'resolver') {
381
+ imports.add(argDefault.dependency, camelCase(argDefault.name));
382
+ }
383
+ });
384
+
344
385
  // Bytes created on chain.
345
386
  const bytes = instruction.metadata.bytesCreatedOnChain;
346
- if (bytes && bytes.includeHeader) {
387
+ if (bytes && 'includeHeader' in bytes && bytes.includeHeader) {
347
388
  imports.add('core', 'ACCOUNT_HEADER_SIZE');
348
389
  }
349
390
  if (bytes?.kind === 'account') {
@@ -353,6 +394,8 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
353
394
  ? 'generatedAccounts'
354
395
  : bytes.dependency;
355
396
  imports.add(dependency, `get${accountName}Size`);
397
+ } else if (bytes?.kind === 'resolver') {
398
+ imports.add(bytes.dependency, camelCase(bytes.name));
356
399
  }
357
400
 
358
401
  // Remove imports from the same module.
@@ -364,7 +407,10 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
364
407
 
365
408
  // canMergeAccountsAndArgs
366
409
  let canMergeAccountsAndArgs = false;
367
- if (!nodes.isTypeDefinedLinkNode(instruction.args)) {
410
+ if (
411
+ !nodes.isTypeDefinedLinkNode(instruction.args) &&
412
+ !nodes.isTypeDefinedLinkNode(instruction.extraArgs)
413
+ ) {
368
414
  const accountsAndArgsConflicts =
369
415
  this.getMergeConflictsForInstructionAccountsAndArgs(instruction);
370
416
  if (accountsAndArgsConflicts.length > 0) {
@@ -380,17 +426,25 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
380
426
  canMergeAccountsAndArgs = accountsAndArgsConflicts.length === 0;
381
427
  }
382
428
 
429
+ const hasAccountDefaultKinds = (
430
+ kinds: Array<nodes.InstructionNodeAccountDefaults['kind']>
431
+ ) =>
432
+ accounts.some((a) => a.defaultsTo && kinds.includes(a.defaultsTo.kind));
433
+
383
434
  return new RenderMap().add(
384
435
  `instructions/${camelCase(instruction.name)}.ts`,
385
436
  this.render('instructionsPage.njk', {
386
437
  instruction,
387
438
  imports: imports.toString(this.options.dependencyMap),
388
439
  program: this.program,
440
+ resolvedInputs,
389
441
  accounts,
390
- needsEddsa: accounts.some((a) => a.defaultsTo?.kind === 'pda'),
391
- needsIdentity: accounts.some((a) => a.defaultsTo?.kind === 'identity'),
392
- needsPayer: accounts.some((a) => a.defaultsTo?.kind === 'payer'),
393
- typeManifest,
442
+ needsEddsa: hasAccountDefaultKinds(['pda', 'resolver']),
443
+ needsIdentity: hasAccountDefaultKinds(['identity', 'resolver']),
444
+ needsPayer: hasAccountDefaultKinds(['payer', 'resolver']),
445
+ argDefaultKeys,
446
+ argManifest,
447
+ extraArgManifest,
394
448
  canMergeAccountsAndArgs,
395
449
  })
396
450
  );
@@ -423,7 +477,7 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
423
477
  protected getInstructionAccountType(
424
478
  account: ResolvedInstructionAccount
425
479
  ): string {
426
- if (account.pdaBumpArg) return 'Pda';
480
+ if (account.isPda && account.isSigner === false) return 'Pda';
427
481
  if (account.isSigner === 'either') return 'PublicKey | Signer';
428
482
  return account.isSigner ? 'Signer' : 'PublicKey';
429
483
  }
@@ -433,13 +487,12 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
433
487
  ): JavaScriptImportMap {
434
488
  const imports = new JavaScriptImportMap();
435
489
  accounts.forEach((account) => {
436
- if (account.pdaBumpArg) {
490
+ if (account.isPda && account.isSigner === false) {
437
491
  imports.add('core', 'Pda');
438
492
  }
439
493
  if (account.defaultsTo?.kind === 'publicKey') {
440
494
  imports.add('core', 'publicKey');
441
- }
442
- if (account.defaultsTo?.kind === 'pda') {
495
+ } else if (account.defaultsTo?.kind === 'pda') {
443
496
  const pdaAccount = pascalCase(account.defaultsTo.pdaAccount);
444
497
  const dependency =
445
498
  account.defaultsTo.dependency === 'generated'
@@ -451,6 +504,11 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
451
504
  imports.add('core', 'publicKey');
452
505
  }
453
506
  });
507
+ } else if (account.defaultsTo?.kind === 'resolver') {
508
+ imports.add(
509
+ account.defaultsTo.dependency,
510
+ camelCase(account.defaultsTo.name)
511
+ );
454
512
  }
455
513
  if (account.resolvedIsSigner === 'either') {
456
514
  imports.add('core', ['PublicKey', 'publicKey', 'Signer', 'isSigner']);
@@ -467,9 +525,14 @@ export class GetJavaScriptRenderMapVisitor extends BaseThrowVisitor<RenderMap> {
467
525
  instruction: nodes.InstructionNode
468
526
  ): string[] {
469
527
  nodes.assertTypeStructNode(instruction.args);
528
+ let extraArgsFields: nodes.TypeStructFieldNode[] = [];
529
+ if (nodes.isTypeStructNode(instruction.extraArgs)) {
530
+ extraArgsFields = instruction.extraArgs.fields;
531
+ }
470
532
  const allNames = [
471
533
  ...instruction.accounts.map((account) => account.name),
472
534
  ...instruction.args.fields.map((field) => field.name),
535
+ ...extraArgsFields.map((field) => field.name),
473
536
  ];
474
537
  const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i);
475
538
  return [...new Set(duplicates)];
@@ -31,6 +31,12 @@ export class GetJavaScriptTypeManifestVisitor
31
31
  this.importStrategy = strategy;
32
32
  }
33
33
 
34
+ setDefinedName(
35
+ definedName: GetJavaScriptTypeManifestVisitor['definedName']
36
+ ): void {
37
+ this.definedName = definedName;
38
+ }
39
+
34
40
  visitRoot(): JavaScriptTypeManifest {
35
41
  throw new Error(
36
42
  'Cannot get type manifest for root node. Please select a child node.'
@@ -86,6 +86,8 @@ export class GetJavaScriptValidatorBagVisitor extends GetDefaultValidatorBagVisi
86
86
  [`${pascalCaseName}InstructionData`]: 'type',
87
87
  [`${pascalCaseName}InstructionDataArgs`]: 'type',
88
88
  [`get${pascalCaseName}InstructionDataSerializer`]: 'function',
89
+ [`${pascalCaseName}InstructionExtraArgs`]: 'type',
90
+ [`${pascalCaseName}InstructionArgs`]: 'type',
89
91
  }),
90
92
  ]);
91
93
  }
@@ -17,25 +17,15 @@ export function {{ instruction.name | camelCase }}(
17
17
  {{- '| \'payer\'' if needsPayer else '' }}
18
18
  >,
19
19
  {% set accountsType = instruction.name | pascalCase + 'InstructionAccounts' %}
20
- {% if instruction.hasPdaAccounts %}
21
- {% set argsType %}
22
- Omit<{{ instruction.name | pascalCase + 'InstructionDataArgs' }},
23
- {%- for account in instruction.pdaAccounts -%}
24
- "{{ account.pdaBumpArg }}"{% if not loop.last %} | {% endif %}
25
- {%- endfor -%}
26
- >
27
- {% endset %}
28
- {% else %}
29
- {% set argsType = instruction.name | pascalCase + 'InstructionDataArgs' %}
30
- {% endif %}
20
+ {% set argsType = instruction.name | pascalCase + 'InstructionArgs' %}
31
21
  {% if canMergeAccountsAndArgs %}
32
22
  {% set accountsObj = 'input' %}
33
23
  {% set argsObj = 'input' %}
34
- {% if instruction.hasAccounts and instruction.hasArgs %}
24
+ {% if instruction.hasAccounts and instruction.hasAnyArgs %}
35
25
  input: {{ accountsType }} & {{ argsType }},
36
26
  {% elif instruction.hasAccounts %}
37
27
  input: {{ accountsType }},
38
- {% elif instruction.hasArgs %}
28
+ {% elif instruction.hasAnyArgs %}
39
29
  input: {{ argsType }},
40
30
  {% endif %}
41
31
  {% else %}
@@ -44,7 +34,7 @@ export function {{ instruction.name | camelCase }}(
44
34
  {% if instruction.hasAccounts %}
45
35
  accounts: {{ accountsType }},
46
36
  {% endif %}
47
- {% if instruction.hasArgs %}
37
+ {% if instruction.hasAnyArgs %}
48
38
  args: {{ argsType }},
49
39
  {% endif %}
50
40
  {% endif %}
@@ -53,20 +43,15 @@ export function {{ instruction.name | camelCase }}(
53
43
  const keys: AccountMeta[] = [];
54
44
 
55
45
  // Program ID.
56
- const programId = context.programs.getPublicKey('{{ program.name | camelCase }}', '{{ program.metadata.publicKey }}');
46
+ const programId = { ...context.programs.getPublicKey('{{ program.name | camelCase }}', '{{ program.metadata.publicKey }}'), isWritable: false };
47
+
48
+ {% include "instructionsPageResolvedInputs.njk" %}
57
49
 
58
50
  {% include "instructionsPageAccountMetas.njk" -%}
59
51
 
60
52
  // Data.
61
- {% if instruction.hasArgs and instruction.hasPdaAccounts %}
62
- const data = get{{ instruction.name | pascalCase }}InstructionDataSerializer(context).serialize({
63
- ...{{ argsObj }},
64
- {% for account in instruction.pdaAccounts %}
65
- {{ account.pdaBumpArg }}: {{ account.name | camelCase }}Account.bump,
66
- {% endfor %}
67
- });
68
- {% elif instruction.hasArgs %}
69
- const data = get{{ instruction.name | pascalCase }}InstructionDataSerializer(context).serialize({{ argsObj }});
53
+ {% if instruction.hasArgs %}
54
+ const data = get{{ instruction.name | pascalCase }}InstructionDataSerializer(context).serialize(resolvedArgs);
70
55
  {% elif instruction.hasData %}
71
56
  const data = get{{ instruction.name | pascalCase }}InstructionDataSerializer(context).serialize({});
72
57
  {% else %}
@@ -81,6 +66,8 @@ export function {{ instruction.name | camelCase }}(
81
66
  const bytesCreatedOnChain = Number({{ argsObj }}.{{ bytes.name }}){% if bytes.includeHeader %} + ACCOUNT_HEADER_SIZE{% endif %};
82
67
  {% elif bytes.kind === 'account' %}
83
68
  const bytesCreatedOnChain = get{{ bytes.name | pascalCase }}Size(){% if bytes.includeHeader %} + ACCOUNT_HEADER_SIZE{% endif %};
69
+ {% elif bytes.kind === 'resolver' %}
70
+ const bytesCreatedOnChain = {{ bytes.name | camelCase }}(context, resolvedAccounts, resolvedArgs, programId);
84
71
  {% else %}
85
72
  const bytesCreatedOnChain = 0;
86
73
  {% endif %}