@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
@@ -1,62 +1,19 @@
1
- {% if accounts.length > 0 %}
2
- // Resolved accounts.
3
- {% for account in accounts | sort(false, false, 'dependencyPosition') %}
4
- {% if not account.defaultsTo %}
5
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }};
6
- {% elif account.defaultsTo.kind === 'account' %}
7
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? {{ account.defaultsTo.name | camelCase }}Account;
8
- {% elif account.defaultsTo.kind === 'pda' %}
9
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? find{{ account.defaultsTo.pdaAccount | pascalCase }}Pda(context,
10
- {%- if (account.defaultsTo.seeds | length) > 0 -%}
11
- {
12
- {%- for seedKey, seedValue in account.defaultsTo.seeds -%}
13
- {%- if seedValue.kind === 'value' -%}
14
- {{ seedKey }}: {{ seedValue.render }},
15
- {%- elif seedValue.kind === 'account' -%}
16
- {{ seedKey }}: publicKey({{ seedValue.name | camelCase }}Account),
17
- {%- else -%}
18
- {{ seedKey }}: {{ argsObj }}.{{ seedValue.name | camelCase }},
19
- {%- endif -%}
20
- {%- endfor -%}
21
- }
22
- {%- endif -%}
23
- );
24
- {% elif account.defaultsTo.kind === 'publicKey' %}
25
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? publicKey('{{ account.defaultsTo.publicKey }}');
26
- {% elif account.defaultsTo.kind === 'program' %}
27
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? { ...context.programs.getPublicKey('{{ account.defaultsTo.program.name }}', '{{ account.defaultsTo.program.publicKey }}'), isWritable: false };
28
- {% elif account.defaultsTo.kind === 'programId' %}
29
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? { ...programId, isWritable: false };
30
- {% elif account.defaultsTo.kind === 'identity' and (account.isSigner !== false) %}
31
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? context.identity;
32
- {% elif account.defaultsTo.kind === 'identity' %}
33
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? context.identity.publicKey;
34
- {% elif account.defaultsTo.kind === 'payer' and (account.isSigner !== false) %}
35
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? context.payer;
36
- {% elif account.defaultsTo.kind === 'payer' %}
37
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }} ?? context.payer.publicKey;
38
- {% else %}
39
- const {{ account.name | camelCase }}Account = {{ accountsObj }}.{{ account.name | camelCase }};
40
- {% endif %}
41
- {% endfor %}
42
- {% endif %}
43
-
44
- {% for account in accounts | sort(false, false, 'position') %}
1
+ {% for account in accounts %}
45
2
  {% set isWritableString = 'true' if account.isWritable else 'false' %}
46
3
  // {{ account.name | camelCase | titleCase }}{% if account.resolvedIsOptional %} (optional){% endif %}.
47
4
  {% if account.resolvedIsOptional %}
48
- if ({{ account.name | camelCase }}Account) {
5
+ if (resolvedAccounts.{{ account.name | camelCase }}) {
49
6
  {% endif %}
50
7
  {% if account.resolvedIsSigner === 'either' %}
51
- if (isSigner({{ account.name | camelCase }}Account)) {
52
- signers.push({{ account.name | camelCase }}Account);
8
+ if (isSigner(resolvedAccounts.{{ account.name | camelCase }})) {
9
+ signers.push(resolvedAccounts.{{ account.name | camelCase }});
53
10
  }
54
- keys.push({ pubkey: publicKey({{ account.name | camelCase }}Account), isSigner: isSigner({{ account.name | camelCase }}Account), isWritable: isWritable({{ account.name | camelCase }}Account, {{ isWritableString }}) });
11
+ keys.push({ pubkey: publicKey(resolvedAccounts.{{ account.name | camelCase }}), isSigner: isSigner(resolvedAccounts.{{ account.name | camelCase }}), isWritable: isWritable(resolvedAccounts.{{ account.name | camelCase }}, {{ isWritableString }}) });
55
12
  {% elif account.resolvedIsSigner %}
56
- signers.push({{ account.name | camelCase }}Account);
57
- keys.push({ pubkey: {{ account.name | camelCase }}Account.publicKey, isSigner: true, isWritable: isWritable({{ account.name | camelCase }}Account, {{ isWritableString }}) });
13
+ signers.push(resolvedAccounts.{{ account.name | camelCase }});
14
+ keys.push({ pubkey: resolvedAccounts.{{ account.name | camelCase }}.publicKey, isSigner: true, isWritable: isWritable(resolvedAccounts.{{ account.name | camelCase }}, {{ isWritableString }}) });
58
15
  {% else %}
59
- keys.push({ pubkey: {{ account.name | camelCase }}Account, isSigner: false, isWritable: isWritable({{ account.name | camelCase }}Account, {{ isWritableString }}) });
16
+ keys.push({ pubkey: resolvedAccounts.{{ account.name | camelCase }}, isSigner: false, isWritable: isWritable(resolvedAccounts.{{ account.name | camelCase }}, {{ isWritableString }}) });
60
17
  {% endif %}
61
18
  {% if account.resolvedIsOptional %}
62
19
  }
@@ -1,7 +1,36 @@
1
- {% if not instruction.isLinked and instruction.hasData %}
2
- // Arguments.
3
- {{ macros.exportType(instruction.name | pascalCase + 'InstructionData', typeManifest) }}
1
+ {% if not instruction.hasLinkedArgs and instruction.hasData %}
2
+ // Data.
3
+ {{ macros.exportType(instruction.name | pascalCase + 'InstructionData', argManifest) }}
4
4
 
5
- {{ macros.exportSerializer(instruction.name | pascalCase + 'InstructionData', typeManifest) }}
5
+ {{ macros.exportSerializer(instruction.name | pascalCase + 'InstructionData', argManifest) }}
6
+ {% endif %}
6
7
 
7
- {% endif %}
8
+ {% if not instruction.hasLinkedExtraArgs and instruction.hasExtraArgs %}
9
+ // Extra Args.
10
+ export type {{ instruction.name | pascalCase + 'InstructionExtraArgs' }} = {{ extraArgManifest.looseType }};
11
+ {% endif %}
12
+
13
+ {% if instruction.hasAnyArgs %}
14
+ {% set argType %}
15
+ {% if instruction.hasArgs and instruction.hasExtraArgs %}
16
+ {{ instruction.name | pascalCase + 'InstructionDataArgs' }} & {{ instruction.name | pascalCase + 'InstructionExtraArgs' }}
17
+ {% elif instruction.hasArgs %}
18
+ {{ instruction.name | pascalCase + 'InstructionDataArgs' }}
19
+ {% elif instruction.hasExtraArgs %}
20
+ {{ instruction.name | pascalCase + 'InstructionExtraArgs' }}
21
+ {% endif %}
22
+ {% endset %}
23
+
24
+ // Args.
25
+ {% if argDefaultKeys.length > 0 %}
26
+ type PickPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
27
+ export type {{ instruction.name | pascalCase + 'InstructionArgs' }} = PickPartial<
28
+ {{ argType }},
29
+ {% for key in argDefaultKeys %}
30
+ "{{ key }}"{% if not loop.last %} | {% endif %}
31
+ {% endfor %}
32
+ >;
33
+ {% else %}
34
+ export type {{ instruction.name | pascalCase + 'InstructionArgs' }} = {{ argType }};
35
+ {% endif %}
36
+ {% endif %}
@@ -0,0 +1,63 @@
1
+ {% set hasResolvedAccounts = accounts.length > 0 or instruction.hasResolvers %}
2
+ {% set hasResolvedArgs = instruction.hasArgs or instruction.hasArgDefaults or instruction.hasResolvers %}
3
+
4
+ {% if resolvedInputs.length > 0 or hasResolvedAccounts or hasResolvedArgs %}
5
+ // Resolved inputs.
6
+ {% if hasResolvedAccounts %}
7
+ const resolvedAccounts: any = { ...{{ accountsObj }} };
8
+ {% endif %}
9
+ {% if hasResolvedArgs %}
10
+ const resolvedArgs: any = { ...{{ argsObj }} };
11
+ {% endif %}
12
+ {% for input in resolvedInputs %}
13
+ {% if input.kind === 'account' and input.defaultsTo %}
14
+ {% if input.defaultsTo.kind === 'account' %}
15
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? resolvedAccounts.{{ input.defaultsTo.name | camelCase }};
16
+ {% elif input.defaultsTo.kind === 'pda' %}
17
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? find{{ input.defaultsTo.pdaAccount | pascalCase }}Pda(context,
18
+ {%- if (input.defaultsTo.seeds | length) > 0 -%}
19
+ {
20
+ {%- for seedKey, seedValue in input.defaultsTo.seeds -%}
21
+ {%- if seedValue.kind === 'value' -%}
22
+ {{ seedKey }}: {{ seedValue.render }},
23
+ {%- elif seedValue.kind === 'account' -%}
24
+ {{ seedKey }}: publicKey(resolvedAccounts.{{ seedValue.name | camelCase }}),
25
+ {%- else -%}
26
+ {{ seedKey }}: resolvedArgs.{{ seedValue.name | camelCase }},
27
+ {%- endif -%}
28
+ {%- endfor -%}
29
+ }
30
+ {%- endif -%}
31
+ );
32
+ {% elif input.defaultsTo.kind === 'publicKey' %}
33
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? publicKey('{{ input.defaultsTo.publicKey }}');
34
+ {% elif input.defaultsTo.kind === 'program' %}
35
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? { ...context.programs.getPublicKey('{{ input.defaultsTo.program.name }}', '{{ input.defaultsTo.program.publicKey }}'), isWritable: false };
36
+ {% elif input.defaultsTo.kind === 'programId' %}
37
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? programId;
38
+ {% elif input.defaultsTo.kind === 'identity' and (input.isSigner !== false) %}
39
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? context.identity;
40
+ {% elif input.defaultsTo.kind === 'identity' %}
41
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? context.identity.publicKey;
42
+ {% elif input.defaultsTo.kind === 'payer' and (input.isSigner !== false) %}
43
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? context.payer;
44
+ {% elif input.defaultsTo.kind === 'payer' %}
45
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? context.payer.publicKey;
46
+ {% elif input.defaultsTo.kind === 'resolver' %}
47
+ resolvedAccounts.{{ input.name | camelCase }} = resolvedAccounts.{{ input.name | camelCase }} ?? {{ input.defaultsTo.name | camelCase }}(context, resolvedAccounts, resolvedArgs, programId);
48
+ {% endif %}
49
+ {% elif input.kind === 'arg' %}
50
+ {% if input.defaultsTo.kind === 'arg' %}
51
+ resolvedArgs.{{ input.name | camelCase }} = resolvedArgs.{{ input.name | camelCase }} ?? resolvedArgs.{{ input.defaultsTo.name | camelCase }};
52
+ {% elif input.defaultsTo.kind === 'account' %}
53
+ resolvedArgs.{{ input.name | camelCase }} = resolvedArgs.{{ input.name | camelCase }} ?? resolvedAccounts.{{ input.defaultsTo.name | camelCase }};
54
+ {% elif input.defaultsTo.kind === 'accountBump' %}
55
+ resolvedArgs.{{ input.name | camelCase }} = resolvedArgs.{{ input.name | camelCase }} ?? resolvedAccounts.{{ input.defaultsTo.name | camelCase }}.bump;
56
+ {% elif input.defaultsTo.kind === 'value' %}
57
+ resolvedArgs.{{ input.name | camelCase }} = resolvedArgs.{{ input.name | camelCase }} ?? {{ input.defaultsTo.render }};
58
+ {% elif input.defaultsTo.kind === 'resolver' %}
59
+ resolvedArgs.{{ input.name | camelCase }} = resolvedArgs.{{ input.name | camelCase }} ?? {{ input.defaultsTo.name | camelCase }}(context, resolvedAccounts, resolvedArgs, programId);
60
+ {% endif %}
61
+ {% endif %}
62
+ {% endfor %}
63
+ {% endif %}
@@ -63,10 +63,15 @@ export class BaseNodeOrNullVisitor implements Visitor<nodes.Node | null> {
63
63
  const args = instruction.args.accept(this);
64
64
  if (args === null) return null;
65
65
  nodes.assertTypeStructOrDefinedLinkNode(args);
66
+ const extraArgs = instruction.extraArgs?.accept(this) ?? null;
67
+ if (extraArgs !== null) {
68
+ nodes.assertTypeStructOrDefinedLinkNode(args);
69
+ }
66
70
  return new nodes.InstructionNode(
67
71
  instruction.metadata,
68
72
  instruction.accounts,
69
73
  args,
74
+ extraArgs as nodes.TypeStructNode | nodes.TypeDefinedLinkNode | null,
70
75
  instruction.subInstructions
71
76
  .map((ix) => ix.accept(this))
72
77
  .filter(
@@ -51,10 +51,15 @@ export class BaseNodeVisitor implements Visitor<nodes.Node> {
51
51
  visitInstruction(instruction: nodes.InstructionNode): nodes.Node {
52
52
  const args = instruction.args.accept(this);
53
53
  nodes.assertTypeStructOrDefinedLinkNode(args);
54
+ const extraArgs = instruction.extraArgs?.accept(this) ?? null;
55
+ if (extraArgs !== null) {
56
+ nodes.assertTypeStructOrDefinedLinkNode(args);
57
+ }
54
58
  return new nodes.InstructionNode(
55
59
  instruction.metadata,
56
60
  instruction.accounts,
57
61
  args,
62
+ extraArgs as nodes.TypeStructNode | nodes.TypeDefinedLinkNode | null,
58
63
  instruction.subInstructions
59
64
  .map((ix) => ix.accept(this))
60
65
  .filter(
@@ -21,6 +21,7 @@ export abstract class BaseVoidVisitor implements Visitor<void> {
21
21
 
22
22
  visitInstruction(instruction: nodes.InstructionNode): void {
23
23
  instruction.args.accept(this);
24
+ instruction.extraArgs?.accept(this);
24
25
  instruction.subInstructions.forEach((ix) => ix.accept(this));
25
26
  }
26
27
 
@@ -1,8 +1,9 @@
1
1
  import * as nodes from '../../nodes';
2
+ import { mainCase } from '../../utils';
2
3
  import { NodeStack } from '../NodeStack';
3
4
  import { ValidatorBag } from '../ValidatorBag';
4
5
  import { Visitor } from '../Visitor';
5
- import { GetResolvedInstructionAccountsVisitor } from './GetResolvedInstructionAccountsVisitor';
6
+ import { GetResolvedInstructionInputsVisitor } from './GetResolvedInstructionInputsVisitor';
6
7
 
7
8
  export class GetDefaultValidatorBagVisitor implements Visitor<ValidatorBag> {
8
9
  protected stack: NodeStack = new NodeStack();
@@ -84,7 +85,7 @@ export class GetDefaultValidatorBagVisitor implements Visitor<ValidatorBag> {
84
85
  });
85
86
 
86
87
  // Check for cyclic dependencies in account defaults.
87
- const cyclicCheckVisitor = new GetResolvedInstructionAccountsVisitor();
88
+ const cyclicCheckVisitor = new GetResolvedInstructionInputsVisitor();
88
89
  try {
89
90
  instruction.accept(cyclicCheckVisitor);
90
91
  } catch (error) {
@@ -95,8 +96,53 @@ export class GetDefaultValidatorBagVisitor implements Visitor<ValidatorBag> {
95
96
  );
96
97
  }
97
98
 
99
+ // Check args.
98
100
  bag.mergeWith([instruction.args.accept(this)]);
99
101
 
102
+ // Check extra args.
103
+ if (instruction.extraArgs) {
104
+ bag.mergeWith([instruction.extraArgs.accept(this)]);
105
+ if (
106
+ nodes.isTypeStructNode(instruction.args) &&
107
+ nodes.isTypeStructNode(instruction.extraArgs)
108
+ ) {
109
+ const names = [
110
+ ...instruction.args.fields.map(({ name }) => mainCase(name)),
111
+ ...instruction.extraArgs.fields.map(({ name }) => mainCase(name)),
112
+ ];
113
+ const duplicates = names.filter((e, i, a) => a.indexOf(e) !== i);
114
+ const uniqueDuplicates = [...new Set(duplicates)];
115
+ const hasConflictingNames = uniqueDuplicates.length > 0;
116
+ if (hasConflictingNames) {
117
+ bag.error(
118
+ `The names of the following instruction arguments are conflicting: ` +
119
+ `[${uniqueDuplicates.join(', ')}].`,
120
+ instruction,
121
+ this.stack
122
+ );
123
+ }
124
+ }
125
+ }
126
+
127
+ // Check arg defaults.
128
+ Object.entries(instruction.metadata.argDefaults).forEach(
129
+ ([name, defaultsTo]) => {
130
+ if (defaultsTo.kind === 'accountBump') {
131
+ const defaultAccount = instruction.accounts.find(
132
+ (account) => account.name === defaultsTo.name
133
+ );
134
+ if (defaultAccount && defaultAccount.isSigner !== false) {
135
+ bag.error(
136
+ `Argument ${name} cannot default to the bump attribute of ` +
137
+ `the [${defaultsTo.name}] account as it may be a Signer.`,
138
+ instruction,
139
+ this.stack
140
+ );
141
+ }
142
+ }
143
+ }
144
+ );
145
+
100
146
  // Check sub-instructions.
101
147
  bag.mergeWith(instruction.subInstructions.map((ix) => ix.accept(this)));
102
148
 
@@ -32,11 +32,12 @@ export class GetNodeInlineStringVisitor implements Visitor<string> {
32
32
  visitInstruction(instruction: nodes.InstructionNode): string {
33
33
  const accounts = instruction.accounts.map((account) => account.name);
34
34
  const args = instruction.args.accept(this);
35
+ const extraArgs = instruction.extraArgs?.accept(this);
36
+ const extraArgsString = extraArgs ? `,extraArgs:(${extraArgs})` : '';
35
37
  return (
36
38
  `${INSTRUCTION_PREFIX}[${instruction.name}](` +
37
39
  `accounts:(${accounts.join(',')}),` +
38
- `args:(${args})` +
39
- `)`
40
+ `args:(${args})${extraArgsString})`
40
41
  );
41
42
  }
42
43
 
@@ -91,6 +91,12 @@ export class GetNodeTreeStringVisitor implements Visitor<string> {
91
91
  this.indent += 1;
92
92
  children.push(instruction.args.accept(this));
93
93
  this.indent -= 1;
94
+ if (instruction.extraArgs) {
95
+ children.push(this.indented('extra arguments:'));
96
+ this.indent += 1;
97
+ children.push(instruction.extraArgs.accept(this));
98
+ this.indent -= 1;
99
+ }
94
100
  this.indent -= 1;
95
101
  return children.join('\n');
96
102
  }
@@ -0,0 +1,275 @@
1
+ import type * as nodes from '../../nodes';
2
+ import { BaseThrowVisitor } from '../BaseThrowVisitor';
3
+
4
+ type InstructionNodeInput =
5
+ | ({ kind: 'arg' } & InstructionNodeArg)
6
+ | ({ kind: 'account' } & nodes.InstructionNodeAccount);
7
+
8
+ type InstructionNodeArg = {
9
+ name: string;
10
+ defaultsTo: nodes.InstructionNodeArgDefaults;
11
+ };
12
+
13
+ export type ResolvedInstructionAccount = nodes.InstructionNodeAccount & {
14
+ kind: 'account';
15
+ isPda: boolean;
16
+ dependsOn: nodes.InstructionNodeInputDependency[];
17
+ resolvedIsSigner: boolean | 'either';
18
+ resolvedIsOptional: boolean;
19
+ };
20
+
21
+ export type ResolvedInstructionArg = InstructionNodeArg & {
22
+ kind: 'arg';
23
+ dependsOn: nodes.InstructionNodeInputDependency[];
24
+ };
25
+
26
+ export type ResolvedInstructionInput =
27
+ | ResolvedInstructionAccount
28
+ | ResolvedInstructionArg;
29
+
30
+ export class GetResolvedInstructionInputsVisitor extends BaseThrowVisitor<
31
+ ResolvedInstructionInput[]
32
+ > {
33
+ protected stack: InstructionNodeInput[] = [];
34
+
35
+ protected resolved: ResolvedInstructionInput[] = [];
36
+
37
+ protected visitedAccounts = new Map<string, ResolvedInstructionAccount>();
38
+
39
+ protected visitedArgs = new Map<string, ResolvedInstructionArg>();
40
+
41
+ protected error: string | null = null;
42
+
43
+ getError(): string | null {
44
+ return this.error;
45
+ }
46
+
47
+ visitInstruction(
48
+ instruction: nodes.InstructionNode
49
+ ): ResolvedInstructionInput[] {
50
+ // Ensure we always start with a clean slate.
51
+ this.error = null;
52
+ this.stack = [];
53
+ this.resolved = [];
54
+ this.visitedAccounts = new Map();
55
+ this.visitedArgs = new Map();
56
+
57
+ const inputs: InstructionNodeInput[] = [
58
+ ...instruction.accounts.map((account) => ({
59
+ kind: 'account' as const,
60
+ ...account,
61
+ })),
62
+ ...Object.entries(instruction.metadata.argDefaults).map(
63
+ ([argName, argDefault]) => ({
64
+ kind: 'arg' as const,
65
+ name: argName,
66
+ defaultsTo: argDefault,
67
+ })
68
+ ),
69
+ ];
70
+
71
+ // Visit all instruction accounts.
72
+ inputs.forEach((input) => {
73
+ this.resolveInstructionInput(instruction, input);
74
+ });
75
+
76
+ return this.resolved;
77
+ }
78
+
79
+ resolveInstructionInput(
80
+ instruction: nodes.InstructionNode,
81
+ input: InstructionNodeInput
82
+ ): void {
83
+ // Ensure we don't visit the same input twice.
84
+ if (
85
+ (input.kind === 'account' && this.visitedAccounts.has(input.name)) ||
86
+ (input.kind === 'arg' && this.visitedArgs.has(input.name))
87
+ ) {
88
+ return;
89
+ }
90
+
91
+ // Ensure we don't have a circular dependency.
92
+ const isCircular = this.stack.some(
93
+ ({ kind, name }) => kind === input.kind && name === input.name
94
+ );
95
+ if (isCircular) {
96
+ const cycle = [...this.stack.map(({ name }) => name), input.name].join(
97
+ ' -> '
98
+ );
99
+ this.error =
100
+ `Circular dependency detected in the accounts and args of ` +
101
+ `the "${instruction.name}" instruction. ` +
102
+ `Got the following dependency cycle: ${cycle}.`;
103
+ throw new Error(this.error);
104
+ }
105
+
106
+ // Resolve whilst keeping track of the stack.
107
+ this.stack.push(input);
108
+ const resolved =
109
+ input.kind === 'account'
110
+ ? this.resolveInstructionAccount(instruction, input)
111
+ : this.resolveInstructionArg(instruction, input);
112
+ this.stack.pop();
113
+
114
+ // Store the resolved input.
115
+ this.resolved.push(resolved);
116
+ if (resolved.kind === 'account') {
117
+ this.visitedAccounts.set(input.name, resolved);
118
+ } else {
119
+ this.visitedArgs.set(input.name, resolved);
120
+ }
121
+ }
122
+
123
+ resolveInstructionAccount(
124
+ instruction: nodes.InstructionNode,
125
+ account: nodes.InstructionNodeAccount & { kind: 'account' }
126
+ ): ResolvedInstructionAccount {
127
+ // Get account dependencies.
128
+ const dependsOn: nodes.InstructionNodeInputDependency[] = [];
129
+ if (account.defaultsTo?.kind === 'account') {
130
+ dependsOn.push({ kind: 'account', name: account.defaultsTo.name });
131
+ } else if (account.defaultsTo?.kind === 'pda') {
132
+ const accounts = new Set<string>();
133
+ const args = new Set<string>();
134
+ Object.values(account.defaultsTo.seeds).forEach((seed) => {
135
+ if (seed.kind === 'account') {
136
+ accounts.add(seed.name);
137
+ } else if (seed.kind === 'arg') {
138
+ args.add(seed.name);
139
+ }
140
+ });
141
+ dependsOn.push(
142
+ ...[...accounts].map((name) => ({ kind: 'account' as const, name })),
143
+ ...[...accounts].map((name) => ({ kind: 'arg' as const, name }))
144
+ );
145
+ } else if (account.defaultsTo?.kind === 'resolver') {
146
+ dependsOn.push(...account.defaultsTo.dependsOn);
147
+ }
148
+
149
+ // Visit account dependencies first.
150
+ this.resolveInstructionDependencies(instruction, account, dependsOn);
151
+
152
+ const resolved: ResolvedInstructionAccount = {
153
+ ...account,
154
+ isPda: Object.values(instruction.metadata.argDefaults).some(
155
+ (argDefault) =>
156
+ argDefault.kind === 'accountBump' && argDefault.name === account.name
157
+ ),
158
+ dependsOn,
159
+ resolvedIsSigner: account.isSigner,
160
+ resolvedIsOptional: account.isOptional,
161
+ };
162
+
163
+ switch (resolved.defaultsTo?.kind) {
164
+ case 'account':
165
+ const defaultAccount = this.visitedAccounts.get(
166
+ resolved.defaultsTo.name
167
+ )!;
168
+ const resolvedIsPublicKey =
169
+ account.isSigner === false && defaultAccount.isSigner === false;
170
+ const resolvedIsSigner =
171
+ account.isSigner === true && defaultAccount.isSigner === true;
172
+ const resolvedIsOptionalSigner =
173
+ !resolvedIsPublicKey && !resolvedIsSigner;
174
+ resolved.resolvedIsSigner = resolvedIsOptionalSigner
175
+ ? 'either'
176
+ : resolvedIsSigner;
177
+ resolved.resolvedIsOptional = defaultAccount.isOptional;
178
+ break;
179
+ case 'publicKey':
180
+ case 'program':
181
+ case 'programId':
182
+ resolved.resolvedIsSigner =
183
+ account.isSigner === false ? false : 'either';
184
+ resolved.resolvedIsOptional = false;
185
+ break;
186
+ case 'pda':
187
+ resolved.resolvedIsSigner =
188
+ account.isSigner === false ? false : 'either';
189
+ resolved.resolvedIsOptional = false;
190
+ const { seeds } = resolved.defaultsTo;
191
+ Object.keys(seeds).forEach((seedKey) => {
192
+ const seed = seeds[seedKey];
193
+ if (seed.kind !== 'account') return;
194
+ const dependency = this.visitedAccounts.get(seed.name)!;
195
+ if (dependency.resolvedIsOptional) {
196
+ this.error =
197
+ `Cannot use optional account "${seed.name}" as the "${seedKey}" PDA seed ` +
198
+ `for the "${account.name}" account of the "${instruction.name}" instruction.`;
199
+ throw new Error(this.error);
200
+ }
201
+ });
202
+ break;
203
+ case 'identity':
204
+ case 'payer':
205
+ resolved.resolvedIsOptional = false;
206
+ break;
207
+ case 'resolver':
208
+ resolved.resolvedIsOptional = resolved.defaultsTo.resolvedIsOptional;
209
+ resolved.resolvedIsSigner = resolved.defaultsTo.resolvedIsSigner;
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+
215
+ return resolved;
216
+ }
217
+
218
+ resolveInstructionArg(
219
+ instruction: nodes.InstructionNode,
220
+ arg: InstructionNodeArg & { kind: 'arg' }
221
+ ): ResolvedInstructionArg {
222
+ // Get account dependencies.
223
+ const dependsOn: nodes.InstructionNodeInputDependency[] = [];
224
+ if (
225
+ arg.defaultsTo.kind === 'account' ||
226
+ arg.defaultsTo.kind === 'accountBump'
227
+ ) {
228
+ dependsOn.push({ kind: 'account', name: arg.defaultsTo.name });
229
+ } else if (arg.defaultsTo.kind === 'arg') {
230
+ dependsOn.push({ kind: 'arg', name: arg.defaultsTo.name });
231
+ } else if (arg.defaultsTo.kind === 'resolver') {
232
+ dependsOn.push(...arg.defaultsTo.dependsOn);
233
+ }
234
+
235
+ // Visit account dependencies first.
236
+ this.resolveInstructionDependencies(instruction, arg, dependsOn);
237
+
238
+ return { ...arg, dependsOn };
239
+ }
240
+
241
+ resolveInstructionDependencies(
242
+ instruction: nodes.InstructionNode,
243
+ parent: InstructionNodeInput,
244
+ dependencies: nodes.InstructionNodeInputDependency[]
245
+ ): void {
246
+ dependencies.forEach((dependency) => {
247
+ let input: InstructionNodeInput | null = null;
248
+ if (dependency.kind === 'account') {
249
+ const dependencyAccount = instruction.accounts.find(
250
+ ({ name }) => name === dependency.name
251
+ );
252
+ if (!dependencyAccount) {
253
+ this.error =
254
+ `Account "${dependency.name}" is not a valid dependency of ${parent.kind} ` +
255
+ `"${parent.name}" in the "${instruction.name}" instruction.`;
256
+ throw new Error(this.error);
257
+ }
258
+ input = { kind: 'account', ...dependencyAccount };
259
+ } else if (dependency.kind === 'arg') {
260
+ const dependencyArg =
261
+ instruction.metadata.argDefaults[dependency.name] ?? null;
262
+ if (dependencyArg) {
263
+ input = {
264
+ kind: 'arg',
265
+ name: dependency.name,
266
+ defaultsTo: dependencyArg,
267
+ };
268
+ }
269
+ }
270
+ if (input) {
271
+ this.resolveInstructionInput(instruction, input);
272
+ }
273
+ });
274
+ }
275
+ }
@@ -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';
@@ -86,6 +86,7 @@ export class AutoSetAnchorDiscriminatorsVisitor extends BaseNodeVisitor {
86
86
  discriminatorField,
87
87
  ...instruction.args.fields,
88
88
  ]),
89
+ instruction.extraArgs,
89
90
  instruction.subInstructions
90
91
  );
91
92
  }
@@ -92,6 +92,7 @@ export class CreateSubInstructionsFromEnumArgsVisitor extends TransformNodesVisi
92
92
  subFields
93
93
  )
94
94
  ),
95
+ node.extraArgs,
95
96
  []
96
97
  );
97
98
  }
@@ -101,6 +102,7 @@ export class CreateSubInstructionsFromEnumArgsVisitor extends TransformNodesVisi
101
102
  node.metadata,
102
103
  node.accounts,
103
104
  node.args,
105
+ node.extraArgs,
104
106
  [...node.subInstructions, ...subInstructions]
105
107
  );
106
108
  },
@@ -13,6 +13,7 @@ export class FlattenInstructionArgsStructVisitor extends TransformNodesVisitor {
13
13
  instruction.metadata,
14
14
  instruction.accounts,
15
15
  flattenStruct(instruction.args),
16
+ instruction.extraArgs,
16
17
  instruction.subInstructions
17
18
  );
18
19
  },
@@ -251,6 +251,7 @@ export class SetInstructionAccountDefaultValuesVisitor extends BaseNodeVisitor {
251
251
  instruction.metadata,
252
252
  instructionAccounts,
253
253
  instruction.args,
254
+ instruction.extraArgs,
254
255
  instruction.subInstructions
255
256
  );
256
257
  }
@@ -43,6 +43,7 @@ export class SetInstructionDiscriminatorsVisitor extends TransformNodesVisitor {
43
43
  discriminatorField,
44
44
  ...node.args.fields,
45
45
  ]),
46
+ node.extraArgs,
46
47
  node.subInstructions
47
48
  );
48
49
  },