new-idl.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { assert } from "chai";
  3. import type { NewIdl } from "../target/types/new_idl";
  4. describe("New IDL", () => {
  5. anchor.setProvider(anchor.AnchorProvider.env());
  6. const program = anchor.workspace.newIdl as anchor.Program<NewIdl>;
  7. describe("Case conversion", () => {
  8. const caseConversionAccountKp = anchor.web3.Keypair.generate();
  9. const FIELD_NAME = 5;
  10. it("Works when instructions have no case conversion in IDL", async () => {
  11. const ixName = "no_case_conversion";
  12. const ix = program.rawIdl.instructions.find((ix) => ix.name === ixName);
  13. if (!ix) throw new Error(`Instruction \`${ixName}\` not found`);
  14. await program.methods
  15. .noCaseConversion(FIELD_NAME)
  16. .accounts({ caseConversionAccount: caseConversionAccountKp.publicKey })
  17. .signers([caseConversionAccountKp])
  18. .rpc();
  19. });
  20. it("Works when accounts have no case conversion in IDL", async () => {
  21. const accName = "SimpleAccount";
  22. const acc = program.rawIdl.accounts!.find((acc) => acc.name === accName);
  23. if (!acc) throw new Error(`Account \`${accName}\` not found`);
  24. const caseConversionAccount = await program.account.simpleAccount.fetch(
  25. caseConversionAccountKp.publicKey
  26. );
  27. assert.strictEqual(caseConversionAccount.fieldName, FIELD_NAME);
  28. });
  29. it("Works when events have no case conversion in IDL", async () => {
  30. const eventName = "SimpleEvent";
  31. const event = program.rawIdl.events!.find((ev) => ev.name === eventName);
  32. if (!event) throw new Error(`Event \`${eventName}\` not found`);
  33. await new Promise<void>(async (res) => {
  34. const id = program.addEventListener("simpleEvent", (ev) => {
  35. program.removeEventListener(id);
  36. assert.strictEqual(ev.fieldName, FIELD_NAME);
  37. res();
  38. });
  39. const caseConversionAccountKp = anchor.web3.Keypair.generate();
  40. await program.methods
  41. .noCaseConversion(FIELD_NAME)
  42. .accounts({
  43. caseConversionAccount: caseConversionAccountKp.publicKey,
  44. })
  45. .signers([caseConversionAccountKp])
  46. .rpc();
  47. });
  48. });
  49. });
  50. describe("Client interaction", () => {
  51. it("Can send empty ix(no arg, no account)", async () => {
  52. await program.methods.empty().rpc();
  53. });
  54. it("Can use primitive types", async () => {
  55. const kp = anchor.web3.Keypair.generate();
  56. const bool = true;
  57. const i8 = -3;
  58. const i16 = 1;
  59. const i32 = -5555551;
  60. const i64 = new anchor.BN("384535471");
  61. const i128 = new anchor.BN(-8342491);
  62. const u8 = 123;
  63. const u16 = 7888;
  64. const u32 = 5555551;
  65. const u64 = new anchor.BN("384535471");
  66. const u128 = new anchor.BN(8888888);
  67. const f32 = 1.0;
  68. const f64 = 0.618;
  69. const pubkey = anchor.web3.PublicKey.default;
  70. await program.methods
  71. .primitiveTypes(
  72. bool,
  73. i8,
  74. i16,
  75. i32,
  76. i64,
  77. i128,
  78. u8,
  79. u16,
  80. u32,
  81. u64,
  82. u128,
  83. f32,
  84. f64,
  85. pubkey
  86. )
  87. .accounts({ account: kp.publicKey })
  88. .signers([kp])
  89. .preInstructions([
  90. await program.account.primitiveAccount.createInstruction(kp),
  91. ])
  92. .rpc();
  93. const account = await program.account.primitiveAccount.fetch(
  94. kp.publicKey
  95. );
  96. assert.strictEqual(account.bool, bool);
  97. assert.strictEqual(account.i8, i8);
  98. assert.strictEqual(account.i16, i16);
  99. assert.strictEqual(account.i32, i32);
  100. assert(account.i64.eq(i64));
  101. assert(account.i128.eq(i128));
  102. assert.strictEqual(account.u8, u8);
  103. assert.strictEqual(account.u16, u16);
  104. assert.strictEqual(account.u32, u32);
  105. assert(account.u64.eq(u64));
  106. assert(account.u128.eq(u128));
  107. assert.strictEqual(account.f32, f32);
  108. assert.strictEqual(account.f64, f64);
  109. assert(account.pubkey.equals(pubkey));
  110. });
  111. it("Can use unsized types", async () => {
  112. const kp = anchor.web3.Keypair.generate();
  113. const string = "anchor";
  114. const bytes = Buffer.from([1, 2, 3, 4]);
  115. await program.methods
  116. .unsizedTypes(string, bytes)
  117. .accounts({ account: kp.publicKey })
  118. .signers([kp])
  119. .preInstructions([
  120. await program.account.primitiveAccount.createInstruction(kp),
  121. ])
  122. .rpc();
  123. const account = await program.account.unsizedAccount.fetch(kp.publicKey);
  124. assert.strictEqual(account.string, string);
  125. assert(account.bytes.equals(bytes));
  126. });
  127. it("Can use struct", async () => {
  128. const unitStructArg = {} as const;
  129. const namedStructArg = {
  130. u8: 1,
  131. u16: 11,
  132. u32: 111,
  133. u64: new anchor.BN(1111),
  134. } as const;
  135. const tupleStructArg = [new anchor.BN(23), "tuple"] as const;
  136. const kp = anchor.web3.Keypair.generate();
  137. await program.methods
  138. .strct(unitStructArg, namedStructArg, tupleStructArg)
  139. .accounts({ account: kp.publicKey })
  140. .signers([kp])
  141. .preInstructions([
  142. await program.account.structAccount.createInstruction(kp, 1024),
  143. ])
  144. .rpc();
  145. const struct = await program.account.structAccount.fetch(kp.publicKey);
  146. // Unit
  147. assert.deepEqual(struct.unit, unitStructArg);
  148. // Named
  149. assert.strictEqual(struct.named.u8, namedStructArg.u8);
  150. assert.strictEqual(struct.named.u16, namedStructArg.u16);
  151. assert.strictEqual(struct.named.u32, namedStructArg.u32);
  152. assert(struct.named.u64.eq(namedStructArg.u64));
  153. // Tuple
  154. assert(struct.tuple[0].eq(tupleStructArg[0]));
  155. assert.strictEqual(struct.tuple[1], tupleStructArg[1]);
  156. });
  157. it("Can use enum", async () => {
  158. const testAccountEnum = async (
  159. ...args: Parameters<typeof program["methods"]["enm"]>
  160. ) => {
  161. const kp = anchor.web3.Keypair.generate();
  162. await program.methods
  163. .enm(...(args as any))
  164. .accounts({ account: kp.publicKey })
  165. .signers([kp])
  166. .preInstructions([
  167. await program.account.enumAccount.createInstruction(kp),
  168. ])
  169. .rpc();
  170. return await program.account.enumAccount.fetch(kp.publicKey);
  171. };
  172. // Unit
  173. const unit = await testAccountEnum({ unit: {} });
  174. assert.deepEqual(unit.fullEnum.unit, {});
  175. // Named
  176. const pointX = new anchor.BN(1);
  177. const pointY = new anchor.BN(2);
  178. const named = await testAccountEnum({ named: { pointX, pointY } });
  179. if (!named.fullEnum.named) throw new Error("Named not crated");
  180. assert(named.fullEnum.named.pointX.eq(pointX));
  181. assert(named.fullEnum.named.pointY.eq(pointY));
  182. // Unnamed
  183. const tupleArg = [1, 2, 3, 4] as const;
  184. const unnamed = await testAccountEnum({ unnamed: tupleArg });
  185. if (!unnamed.fullEnum.unnamed) throw new Error("Unnamed not crated");
  186. assert(
  187. Object.entries(unnamed.fullEnum.unnamed).every(
  188. ([key, value]) => value === tupleArg[key as keyof typeof tupleArg]
  189. )
  190. );
  191. // Unnamed struct
  192. const tupleStructArg = [
  193. { u8: 1, u16: 11, u32: 111, u64: new anchor.BN(1111) },
  194. ] as const;
  195. const unnamedStruct = await testAccountEnum({
  196. unnamedStruct: tupleStructArg,
  197. });
  198. if (!unnamedStruct.fullEnum.unnamedStruct) {
  199. throw new Error("Unnamed struct not crated");
  200. }
  201. assert.strictEqual(
  202. unnamedStruct.fullEnum.unnamedStruct[0].u8,
  203. tupleStructArg[0].u8
  204. );
  205. assert.strictEqual(
  206. unnamedStruct.fullEnum.unnamedStruct[0].u16,
  207. tupleStructArg[0].u16
  208. );
  209. assert.strictEqual(
  210. unnamedStruct.fullEnum.unnamedStruct[0].u32,
  211. tupleStructArg[0].u32
  212. );
  213. assert(
  214. unnamedStruct.fullEnum.unnamedStruct[0].u64.eq(tupleStructArg[0].u64)
  215. );
  216. });
  217. it("Can use type aliases", async () => {
  218. const kp = anchor.web3.Keypair.generate();
  219. const aliasU8 = 42;
  220. const aliasU8Array = [1, 2, 3, 4, 5, 6, 7, 8];
  221. const aliasStruct = {
  222. u8: 1,
  223. u16: 2,
  224. u32: 3,
  225. u64: new anchor.BN(4),
  226. };
  227. const aliasVecString = ["first", "second"];
  228. const aliasOptionVecPubkey = [anchor.web3.Keypair.generate().publicKey];
  229. const aliasGenericConst = [1, 23045, 32, 4];
  230. const aliasMultipleGenericsMixed = [
  231. [true, false],
  232. [false, true],
  233. ];
  234. const aliasExternal = new anchor.BN(1708705033);
  235. await program.methods
  236. .typeAlias(
  237. aliasU8,
  238. aliasU8Array,
  239. aliasStruct,
  240. aliasVecString,
  241. aliasOptionVecPubkey,
  242. aliasGenericConst,
  243. aliasMultipleGenericsMixed,
  244. aliasExternal
  245. )
  246. .accounts({ account: kp.publicKey })
  247. .signers([kp])
  248. .preInstructions([
  249. await program.account.aliasAccount.createInstruction(kp, 1024),
  250. ])
  251. .rpc();
  252. const account = await program.account.aliasAccount.fetch(kp.publicKey);
  253. assert.strictEqual(account.aliasU8, aliasU8);
  254. assert.deepEqual(account.aliasU8Array, aliasU8Array);
  255. assert.strictEqual(account.aliasStruct.u8, aliasStruct.u8);
  256. assert.strictEqual(account.aliasStruct.u16, aliasStruct.u16);
  257. assert.strictEqual(account.aliasStruct.u32, aliasStruct.u32);
  258. assert(account.aliasStruct.u64.eq(aliasStruct.u64));
  259. assert.deepEqual(account.aliasVecString, aliasVecString);
  260. assert.deepEqual(account.aliasOptionVecPubkey, aliasOptionVecPubkey);
  261. assert.deepEqual(account.aliasGenericConst, aliasGenericConst);
  262. assert.deepEqual(
  263. account.aliasMultipleGenericsMixed,
  264. aliasMultipleGenericsMixed
  265. );
  266. assert(account.aliasExternal.eq(aliasExternal));
  267. });
  268. it("Can use accounts and events as arguments and fields", async () => {
  269. const kp = anchor.web3.Keypair.generate();
  270. const accountArg = {
  271. simpleAccount: { fieldName: 2 },
  272. simpleEvent: { fieldName: 4 },
  273. };
  274. await program.methods
  275. .accountAndEventArgAndField(accountArg)
  276. .accounts({ account: kp.publicKey })
  277. .signers([kp])
  278. .preInstructions([
  279. await program.account.accountAndEventFieldAccount.createInstruction(
  280. kp
  281. ),
  282. ])
  283. .rpc();
  284. const account = await program.account.accountAndEventFieldAccount.fetch(
  285. kp.publicKey
  286. );
  287. assert.deepEqual(account, accountArg);
  288. });
  289. it("Can use generics", async () => {
  290. const arg = {
  291. arr: [6, 123, 2, 3],
  292. subField: {
  293. subArr: new Array(8).fill(null).map((_, i) => i + 33),
  294. another: [30211, 65050, 21, 441],
  295. },
  296. };
  297. const { pubkeys } = await program.methods.generic(arg).rpcAndKeys();
  298. const myAccount = await program.account.genericAccount.fetch(
  299. pubkeys.myAccount
  300. );
  301. assert.deepEqual(myAccount.field, arg);
  302. });
  303. it("Can use generics populated with custom struct", async () => {
  304. const arg = {
  305. arr: [{ field: 1 }, { field: 2 }, { field: 3 }, { field: 4 }],
  306. subField: {
  307. subArr: new Array(8).fill(null).map((_, i) => ({ field: i })),
  308. another: [
  309. { field: 42 },
  310. { field: 420 },
  311. { field: 4_200 },
  312. { field: 42_000 },
  313. ],
  314. },
  315. };
  316. const { pubkeys } = await program.methods
  317. .genericCustomStruct(arg)
  318. .rpcAndKeys();
  319. const myAccount = await program.account.genericAccountCustomStruct.fetch(
  320. pubkeys.myAccount
  321. );
  322. assert.deepEqual(myAccount.field, arg);
  323. });
  324. it("Can use full module path types", async () => {
  325. const kp = anchor.web3.Keypair.generate();
  326. const namedStructArg = { u8: 1, u16: 2, u32: 3, u64: new anchor.BN(4) };
  327. const someModuleNamedStructArg = { data: 5 };
  328. await program.methods
  329. .fullPath(namedStructArg, someModuleNamedStructArg)
  330. .accounts({ account: kp.publicKey })
  331. .preInstructions([
  332. await program.account.fullPathAccount.createInstruction(kp),
  333. ])
  334. .signers([kp])
  335. .rpc();
  336. const fullPathAccount = await program.account.fullPathAccount.fetch(
  337. kp.publicKey
  338. );
  339. assert.strictEqual(fullPathAccount.namedStruct.u8, namedStructArg.u8);
  340. assert.strictEqual(fullPathAccount.namedStruct.u16, namedStructArg.u16);
  341. assert.strictEqual(fullPathAccount.namedStruct.u32, namedStructArg.u32);
  342. assert(fullPathAccount.namedStruct.u64.eq(namedStructArg.u64));
  343. assert.deepEqual(
  344. fullPathAccount.someModuleNamedStruct,
  345. someModuleNamedStructArg
  346. );
  347. });
  348. it("Can use external types", async () => {
  349. const externalArg = { someField: 5 };
  350. const kp = anchor.web3.Keypair.generate();
  351. await program.methods
  352. .external(externalArg)
  353. .accounts({ account: kp.publicKey })
  354. .signers([kp])
  355. .preInstructions([
  356. await program.account.accountWithExternalField.createInstruction(kp),
  357. ])
  358. .rpc();
  359. const account = await program.account.accountWithExternalField.fetch(
  360. kp.publicKey
  361. );
  362. assert.deepEqual(account.myStruct, externalArg);
  363. });
  364. it("Can use non-Anchor external types", async () => {
  365. const feature = { activatedAt: new anchor.BN(42) };
  366. const kp = anchor.web3.Keypair.generate();
  367. await program.methods
  368. .externalNonAnchor(feature)
  369. .accounts({ account: kp.publicKey })
  370. .signers([kp])
  371. .preInstructions([
  372. await program.account.accountWithNonAnchorExternalField.createInstruction(
  373. kp
  374. ),
  375. ])
  376. .rpc();
  377. const account =
  378. await program.account.accountWithNonAnchorExternalField.fetch(
  379. kp.publicKey
  380. );
  381. assert(account.feature.activatedAt?.eq(feature.activatedAt));
  382. });
  383. });
  384. describe("Format", () => {
  385. const ixCoder = new anchor.BorshInstructionCoder(program.idl);
  386. const formatEnum = async (argName: string, data: any, expected: string) => {
  387. const typeName = Object.keys(data)[0];
  388. const arg = data[typeName];
  389. const ix = await program.methods
  390. .enm(arg)
  391. .accounts({ account: anchor.web3.PublicKey.default })
  392. .instruction();
  393. const formattedIx = ixCoder.format({ name: "enm", data }, ix.keys);
  394. if (!formattedIx) throw new Error("Failed to format");
  395. assert.deepEqual(formattedIx.args[0], {
  396. name: argName,
  397. type: typeName,
  398. data: expected,
  399. });
  400. };
  401. it("Can format unit enum", async () => {
  402. await formatEnum("fullEnum", { fullEnum: { unit: {} } }, "unit");
  403. });
  404. it("Can format named enum", async () => {
  405. await formatEnum(
  406. "fullEnum",
  407. {
  408. fullEnum: {
  409. named: { pointX: new anchor.BN(1), pointY: new anchor.BN(2) },
  410. },
  411. },
  412. "named { pointX: 1, pointY: 2 }"
  413. );
  414. });
  415. it("Can format tuple enum", async () => {
  416. await formatEnum(
  417. "fullEnum",
  418. { fullEnum: { unnamed: [2, 10, 200, 49] } },
  419. "unnamed { 0: 2, 1: 10, 2: 200, 3: 49 }"
  420. );
  421. });
  422. });
  423. });