getResolvedInstructionInputsVisitor.test.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import {
  2. accountValueNode,
  3. instructionAccountNode,
  4. instructionArgumentNode,
  5. instructionNode,
  6. numberTypeNode,
  7. pdaSeedValueNode,
  8. pdaValueNode,
  9. publicKeyTypeNode,
  10. } from '@codama/nodes';
  11. import { expect, test } from 'vitest';
  12. import { getResolvedInstructionInputsVisitor, visit } from '../src';
  13. test('it returns all instruction accounts in order of resolution', () => {
  14. // Given the following instruction node with an account that defaults to another account.
  15. const node = instructionNode({
  16. accounts: [
  17. instructionAccountNode({
  18. defaultValue: accountValueNode('authority'),
  19. isSigner: true,
  20. isWritable: false,
  21. name: 'owner',
  22. }),
  23. instructionAccountNode({
  24. isSigner: true,
  25. isWritable: false,
  26. name: 'authority',
  27. }),
  28. ],
  29. name: 'myInstruction',
  30. });
  31. // When we get its resolved inputs.
  32. const result = visit(node, getResolvedInstructionInputsVisitor());
  33. // Then we expect the accounts to be in order of resolution.
  34. expect(result).toEqual([
  35. {
  36. ...node.accounts[1],
  37. dependsOn: [],
  38. isPda: false,
  39. resolvedIsOptional: false,
  40. resolvedIsSigner: true,
  41. },
  42. {
  43. ...node.accounts[0],
  44. dependsOn: [accountValueNode('authority')],
  45. isPda: false,
  46. resolvedIsOptional: false,
  47. resolvedIsSigner: true,
  48. },
  49. ]);
  50. });
  51. test('it sets the resolved signer to either when a non signer defaults to a signer account', () => {
  52. // Given the following instruction node such that a non signer account defaults to a signer account.
  53. const node = instructionNode({
  54. accounts: [
  55. instructionAccountNode({
  56. defaultValue: accountValueNode('authority'),
  57. isSigner: false,
  58. isWritable: false,
  59. name: 'owner',
  60. }),
  61. instructionAccountNode({
  62. isSigner: true,
  63. isWritable: false,
  64. name: 'authority',
  65. }),
  66. ],
  67. name: 'myInstruction',
  68. });
  69. // When we get its resolved inputs.
  70. const result = visit(node, getResolvedInstructionInputsVisitor());
  71. // Then we expect the resolved signer to be either for the non signer account.
  72. expect(result[1]).toEqual({
  73. ...node.accounts[0],
  74. dependsOn: [accountValueNode('authority')],
  75. isPda: false,
  76. resolvedIsOptional: false,
  77. resolvedIsSigner: 'either',
  78. });
  79. });
  80. test('it sets the resolved signer to either when a signer defaults to a non signer account', () => {
  81. // Given the following instruction node such that a signer account defaults to a non signer account.
  82. const node = instructionNode({
  83. accounts: [
  84. instructionAccountNode({
  85. defaultValue: accountValueNode('authority'),
  86. isSigner: true,
  87. isWritable: false,
  88. name: 'owner',
  89. }),
  90. instructionAccountNode({
  91. isSigner: false,
  92. isWritable: false,
  93. name: 'authority',
  94. }),
  95. ],
  96. name: 'myInstruction',
  97. });
  98. // When we get its resolved inputs.
  99. const result = visit(node, getResolvedInstructionInputsVisitor());
  100. // Then we expect the resolved signer to be either for the signer account.
  101. expect(result[1]).toEqual({
  102. ...node.accounts[0],
  103. dependsOn: [accountValueNode('authority')],
  104. isPda: false,
  105. resolvedIsOptional: false,
  106. resolvedIsSigner: 'either',
  107. });
  108. });
  109. test('it includes instruction data arguments with default values', () => {
  110. // Given the following instruction node with two arguments such that:
  111. // - The first argument defaults to an account.
  112. // - The second argument has no default value.
  113. const node = instructionNode({
  114. accounts: [
  115. instructionAccountNode({
  116. isSigner: true,
  117. isWritable: false,
  118. name: 'owner',
  119. }),
  120. ],
  121. arguments: [
  122. instructionArgumentNode({
  123. defaultValue: accountValueNode('owner'),
  124. name: 'ownerArg',
  125. type: publicKeyTypeNode(),
  126. }),
  127. instructionArgumentNode({
  128. name: 'argWithoutDefaults',
  129. type: numberTypeNode('u8'),
  130. }),
  131. ],
  132. name: 'myInstruction',
  133. });
  134. // When we get its resolved inputs.
  135. const result = visit(node, getResolvedInstructionInputsVisitor());
  136. // Then we expect the following inputs.
  137. expect(result).toEqual([
  138. {
  139. ...node.accounts[0],
  140. dependsOn: [],
  141. isPda: false,
  142. resolvedIsOptional: false,
  143. resolvedIsSigner: true,
  144. },
  145. {
  146. ...node.arguments[0],
  147. dependsOn: [accountValueNode('owner')],
  148. },
  149. ]);
  150. // And the argument without default value is not included.
  151. expect(result.some(input => input.name === 'argWithoutDefaults')).toBe(false);
  152. });
  153. test('it includes instruction extra arguments with default values', () => {
  154. // Given the following instruction node with two extra arguments such that:
  155. // - The first argument defaults to an account.
  156. // - The second argument has no default value.
  157. const node = instructionNode({
  158. accounts: [
  159. instructionAccountNode({
  160. isSigner: true,
  161. isWritable: false,
  162. name: 'owner',
  163. }),
  164. ],
  165. extraArguments: [
  166. instructionArgumentNode({
  167. defaultValue: accountValueNode('owner'),
  168. name: 'ownerArg',
  169. type: publicKeyTypeNode(),
  170. }),
  171. instructionArgumentNode({
  172. name: 'argWithoutDefaults',
  173. type: numberTypeNode('u8'),
  174. }),
  175. ],
  176. name: 'myInstruction',
  177. });
  178. // When we get its resolved inputs.
  179. const result = visit(node, getResolvedInstructionInputsVisitor());
  180. // Then we expect the following inputs.
  181. expect(result).toEqual([
  182. {
  183. ...node.accounts[0],
  184. dependsOn: [],
  185. isPda: false,
  186. resolvedIsOptional: false,
  187. resolvedIsSigner: true,
  188. },
  189. {
  190. ...node.extraArguments![0],
  191. dependsOn: [accountValueNode('owner')],
  192. },
  193. ]);
  194. // And the argument without default value is not included.
  195. expect(result.some(input => input.name === 'argWithoutDefaults')).toBe(false);
  196. });
  197. test('it returns an empty array for empty instructions', () => {
  198. // Given the following empty instruction node.
  199. const node = instructionNode({ name: 'myInstruction' });
  200. // When we get its resolved inputs.
  201. const result = visit(node, getResolvedInstructionInputsVisitor());
  202. // Then we expect an empty array.
  203. expect(result).toEqual([]);
  204. });
  205. test('it resolves the seeds of a PdaValueNode first', () => {
  206. // Given the following instruction node with an account that defaults to another account.
  207. const node = instructionNode({
  208. accounts: [
  209. instructionAccountNode({
  210. defaultValue: pdaValueNode('counter', [pdaSeedValueNode('authority', accountValueNode('payer'))]),
  211. isSigner: false,
  212. isWritable: false,
  213. name: 'counter',
  214. }),
  215. instructionAccountNode({
  216. isSigner: true,
  217. isWritable: false,
  218. name: 'payer',
  219. }),
  220. ],
  221. name: 'myInstruction',
  222. });
  223. // When we get its resolved inputs.
  224. const result = visit(node, getResolvedInstructionInputsVisitor());
  225. // Then we expect the accounts to be in order of resolution.
  226. expect(result).toEqual([
  227. {
  228. ...node.accounts[1],
  229. dependsOn: [],
  230. isPda: false,
  231. resolvedIsOptional: false,
  232. resolvedIsSigner: true,
  233. },
  234. {
  235. ...node.accounts[0],
  236. dependsOn: [accountValueNode('payer')],
  237. isPda: false,
  238. resolvedIsOptional: false,
  239. resolvedIsSigner: false,
  240. },
  241. ]);
  242. });
  243. test('it resolves the program id of a PdaValueNode first', () => {
  244. // Given the following instruction node with an account that defaults to another account.
  245. const node = instructionNode({
  246. accounts: [
  247. instructionAccountNode({
  248. defaultValue: pdaValueNode('counter', [], accountValueNode('counterProgram')),
  249. isSigner: false,
  250. isWritable: false,
  251. name: 'counter',
  252. }),
  253. instructionAccountNode({
  254. isSigner: false,
  255. isWritable: false,
  256. name: 'counterProgram',
  257. }),
  258. ],
  259. name: 'myInstruction',
  260. });
  261. // When we get its resolved inputs.
  262. const result = visit(node, getResolvedInstructionInputsVisitor());
  263. // Then we expect the accounts to be in order of resolution.
  264. expect(result).toEqual([
  265. {
  266. ...node.accounts[1],
  267. dependsOn: [],
  268. isPda: false,
  269. resolvedIsOptional: false,
  270. resolvedIsSigner: false,
  271. },
  272. {
  273. ...node.accounts[0],
  274. dependsOn: [accountValueNode('counterProgram')],
  275. isPda: false,
  276. resolvedIsOptional: false,
  277. resolvedIsSigner: false,
  278. },
  279. ]);
  280. });