generate-ix.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * Generate instructions with repetitive accounts and add them to the bench program.
  3. */
  4. import * as fs from "fs/promises";
  5. import path from "path";
  6. type Instruction = {
  7. /** Instruction name */
  8. name: string;
  9. /** Each account type in accounts struct */
  10. accountType: string;
  11. /** Account macro(`#[account(..)]`) */
  12. accountMacro?: {
  13. init: true;
  14. space?: number | string;
  15. };
  16. /** Number of accounts to create per instruction */
  17. accountCounts?: number[];
  18. };
  19. /**
  20. * The following instructions will be added to the program.
  21. *
  22. * If an instruction already exists, it will be skipped.
  23. */
  24. const INSTRUCTIONS: Instruction[] = [
  25. {
  26. name: "account_info",
  27. accountType: "AccountInfo<'info>",
  28. },
  29. {
  30. name: "account_empty_init",
  31. accountType: "Account<'info, Empty>",
  32. accountMacro: {
  33. init: true,
  34. },
  35. },
  36. {
  37. name: "account_empty",
  38. accountType: "Account<'info, Empty>",
  39. },
  40. {
  41. name: "account_sized_init",
  42. accountType: "Account<'info, Sized>",
  43. accountMacro: {
  44. init: true,
  45. space: "8 + std::mem::size_of::<Sized>()",
  46. },
  47. },
  48. {
  49. name: "account_sized",
  50. accountType: "Account<'info, Sized>",
  51. },
  52. {
  53. name: "account_unsized_init",
  54. accountType: "Account<'info, Unsized>",
  55. accountMacro: {
  56. init: true,
  57. space: "8 + std::mem::size_of::<Unsized>()",
  58. },
  59. },
  60. {
  61. name: "account_unsized",
  62. accountType: "Account<'info, Unsized>",
  63. },
  64. {
  65. name: "boxed_account_empty_init",
  66. accountType: "Box<Account<'info, Empty>>",
  67. accountMacro: {
  68. init: true,
  69. },
  70. },
  71. {
  72. name: "boxed_account_empty",
  73. accountType: "Box<Account<'info, Empty>>",
  74. },
  75. {
  76. name: "boxed_account_sized_init",
  77. accountType: "Box<Account<'info, Sized>>",
  78. accountMacro: {
  79. init: true,
  80. space: "8 + std::mem::size_of::<Sized>()",
  81. },
  82. },
  83. {
  84. name: "boxed_account_sized",
  85. accountType: "Box<Account<'info, Sized>>",
  86. },
  87. {
  88. name: "boxed_account_unsized_init",
  89. accountType: "Box<Account<'info, Unsized>>",
  90. accountMacro: {
  91. init: true,
  92. space: "8 + std::mem::size_of::<Unsized>()",
  93. },
  94. },
  95. {
  96. name: "boxed_account_unsized",
  97. accountType: "Box<Account<'info, Unsized>>",
  98. },
  99. {
  100. name: "boxed_interface_account_mint",
  101. accountType: "Box<InterfaceAccount<'info, Mint>>",
  102. },
  103. {
  104. name: "boxed_interface_account_token",
  105. accountType: "Box<InterfaceAccount<'info, TokenAccount>>",
  106. },
  107. {
  108. name: "interface_account_mint",
  109. accountType: "InterfaceAccount<'info, Mint>",
  110. },
  111. {
  112. name: "interface_account_token",
  113. accountType: "InterfaceAccount<'info, TokenAccount>",
  114. accountCounts: [1, 2, 4],
  115. },
  116. {
  117. name: "interface",
  118. accountType: "Interface<'info, TokenInterface>",
  119. },
  120. {
  121. name: "program",
  122. accountType: "Program<'info, System>",
  123. },
  124. {
  125. name: "signer",
  126. accountType: "Signer<'info>",
  127. },
  128. {
  129. name: "system_account",
  130. accountType: "SystemAccount<'info>",
  131. },
  132. {
  133. name: "unchecked_account",
  134. accountType: "UncheckedAccount<'info>",
  135. },
  136. ];
  137. (async () => {
  138. // Get the program file
  139. const programPath = path.join("programs", "bench", "src", "lib.rs");
  140. let file = await fs.readFile(programPath, {
  141. encoding: "utf8",
  142. });
  143. const create = (
  144. ix: Omit<Instruction, "accountCounts"> & { count: number }
  145. ) => {
  146. // Get the title case of the name for the accounts struct
  147. const accountsName =
  148. ix.name[0].toUpperCase() +
  149. ix.name.slice(1).replace(/_\w/g, (match) => match[1].toUpperCase());
  150. // Generate accounts
  151. let accounts = "";
  152. let accountMacro = "";
  153. const INDENT = "\n ";
  154. if (ix.accountMacro?.init) {
  155. accounts += `${INDENT}#[account(mut)]${INDENT}pub payer: Signer<'info>,`;
  156. accounts += `${INDENT}pub system_program: Program<'info, System>,`;
  157. accountMacro += `init, payer = payer, space = ${
  158. ix.accountMacro.space ?? 8
  159. }`;
  160. }
  161. accountMacro = `${INDENT}#[account(${accountMacro})]`;
  162. for (let i = 0; i < ix.count; i++) {
  163. if (ix.accountMacro) {
  164. accounts += accountMacro;
  165. }
  166. accounts += `${INDENT}pub account${i + 1}: ${ix.accountType},`;
  167. }
  168. return {
  169. ix: `
  170. pub fn ${ix.name}(_ctx: Context<${accountsName}>) -> Result<()> {
  171. Ok(())
  172. }`,
  173. accounts: `
  174. #[derive(Accounts)]
  175. pub struct ${accountsName}<'info> {${accounts}\n}`,
  176. };
  177. };
  178. const insert = (index: number, text: string) => {
  179. file = file.slice(0, index) + "\n" + text + file.slice(index);
  180. };
  181. for (const instruction of INSTRUCTIONS) {
  182. // Default count
  183. instruction.accountCounts ??= [1, 2, 4, 8];
  184. for (const count of instruction.accountCounts) {
  185. // Append count to the end of the instruction name
  186. const ixName = instruction.name + count;
  187. // Skip existing instructions
  188. if (file.includes(`fn ${ixName}`)) {
  189. continue;
  190. }
  191. const { ix, accounts } = create({ ...instruction, name: ixName, count });
  192. // Get the ix index to start from
  193. const programIndex = file.indexOf("#[program]");
  194. const fileStartingFromProgram = file.slice(programIndex);
  195. // Add instruction
  196. const ixIndex = programIndex + fileStartingFromProgram.indexOf("\n}");
  197. insert(ixIndex, ix);
  198. // Add accounts
  199. const accountsIndex = file.length - 1;
  200. insert(accountsIndex, accounts);
  201. }
  202. }
  203. // Save
  204. await fs.writeFile(programPath, file);
  205. })();