bolt.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { type Program, web3 } from "@coral-xyz/anchor";
  3. import { type PublicKey } from "@solana/web3.js";
  4. import { type Position } from "../target/types/position";
  5. import { type Velocity } from "../target/types/velocity";
  6. import { type BoltComponent } from "../target/types/bolt_component";
  7. import { type SystemSimpleMovement } from "../target/types/system_simple_movement";
  8. import { type SystemFly } from "../target/types/system_fly";
  9. import { type SystemApplyVelocity } from "../target/types/system_apply_velocity";
  10. import { type World } from "../target/types/world";
  11. import { expect } from "chai";
  12. import BN from "bn.js";
  13. import {
  14. createInitializeRegistryInstruction,
  15. FindComponentPda,
  16. FindEntityPda,
  17. FindWorldPda,
  18. FindWorldRegistryPda,
  19. SYSVAR_INSTRUCTIONS_PUBKEY,
  20. } from "../clients/bolt-sdk";
  21. enum Direction {
  22. Left = "Left",
  23. Right = "Right",
  24. Up = "Up",
  25. Down = "Down",
  26. }
  27. function serializeArgs(args: any = {}) {
  28. const jsonString = JSON.stringify(args);
  29. const encoder = new TextEncoder();
  30. const binaryData = encoder.encode(jsonString);
  31. return Buffer.from(
  32. binaryData.buffer,
  33. binaryData.byteOffset,
  34. binaryData.byteLength
  35. );
  36. }
  37. describe("bolt", () => {
  38. const provider = anchor.AnchorProvider.env();
  39. anchor.setProvider(provider);
  40. const worldProgram = anchor.workspace.World as Program<World>;
  41. const boltComponentPositionProgram = anchor.workspace
  42. .Position as Program<Position>;
  43. const boltComponentVelocityProgram = anchor.workspace
  44. .Velocity as Program<Velocity>;
  45. const boltComponentProgramOrigin = anchor.workspace
  46. .BoltComponent as Program<BoltComponent>;
  47. const systemSimpleMovement = (
  48. anchor.workspace.SystemSimpleMovement as Program<SystemSimpleMovement>
  49. ).programId;
  50. const systemFly = (anchor.workspace.SystemFly as Program<SystemFly>)
  51. .programId;
  52. const applyVelocity = (
  53. anchor.workspace.SystemApplyVelocity as Program<SystemApplyVelocity>
  54. ).programId;
  55. let entity1: PublicKey;
  56. let entity2: PublicKey;
  57. let entity5: PublicKey;
  58. let componentPositionEntity1: PublicKey;
  59. let componentPositionEntity2: PublicKey;
  60. let componentPositionEntity5: PublicKey;
  61. let componentVelocityEntity1: PublicKey;
  62. it("InitializeWorldsRegistry", async () => {
  63. const registryPda = FindWorldRegistryPda(worldProgram.programId);
  64. const initializeRegistryIx = createInitializeRegistryInstruction({
  65. registry: registryPda,
  66. payer: provider.wallet.publicKey,
  67. });
  68. const tx = new anchor.web3.Transaction().add(initializeRegistryIx);
  69. await provider.sendAndConfirm(tx);
  70. });
  71. it("InitializeNewWorld", async () => {
  72. const registryPda = FindWorldRegistryPda(worldProgram.programId);
  73. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  74. await worldProgram.methods
  75. .initializeNewWorld()
  76. .accounts({
  77. world: worldPda,
  78. registry: registryPda,
  79. payer: provider.wallet.publicKey,
  80. })
  81. .rpc();
  82. });
  83. it("InitializeNewWorld 2", async () => {
  84. const registryPda = FindWorldRegistryPda(worldProgram.programId);
  85. const worldPda = FindWorldPda(new BN(1), worldProgram.programId);
  86. await worldProgram.methods
  87. .initializeNewWorld()
  88. .accounts({
  89. world: worldPda,
  90. registry: registryPda,
  91. payer: provider.wallet.publicKey,
  92. })
  93. .rpc();
  94. });
  95. it("Add entity 1", async () => {
  96. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  97. entity1 = FindEntityPda(new BN(0), new BN(0), null, worldProgram.programId);
  98. await worldProgram.methods
  99. .addEntity(null)
  100. .accounts({
  101. world: worldPda,
  102. entity: entity1,
  103. payer: provider.wallet.publicKey,
  104. })
  105. .rpc();
  106. });
  107. it("Add entity 2", async () => {
  108. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  109. entity2 = FindEntityPda(new BN(0), new BN(1), null, worldProgram.programId);
  110. await worldProgram.methods
  111. .addEntity(null)
  112. .accounts({
  113. world: worldPda,
  114. entity: entity2,
  115. payer: provider.wallet.publicKey,
  116. })
  117. .rpc();
  118. });
  119. it("Add entity 3", async () => {
  120. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  121. const entityPda = FindEntityPda(
  122. new BN(0),
  123. new BN(2),
  124. null,
  125. worldProgram.programId
  126. );
  127. await worldProgram.methods
  128. .addEntity(null)
  129. .accounts({
  130. world: worldPda,
  131. entity: entityPda,
  132. payer: provider.wallet.publicKey,
  133. })
  134. .rpc();
  135. });
  136. it("Add entity 4 with extra seeds", async () => {
  137. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  138. const seed = "extra-seed";
  139. const entity4 = FindEntityPda(
  140. new BN(0),
  141. new BN(3),
  142. seed,
  143. worldProgram.programId
  144. );
  145. await worldProgram.methods
  146. .addEntity(seed)
  147. .accounts({
  148. world: worldPda,
  149. entity: entity4,
  150. payer: provider.wallet.publicKey,
  151. })
  152. .rpc();
  153. });
  154. it("Add entity 5", async () => {
  155. const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
  156. entity5 = FindEntityPda(new BN(0), new BN(4), null, worldProgram.programId);
  157. await worldProgram.methods
  158. .addEntity(null)
  159. .accounts({
  160. world: worldPda,
  161. entity: entity5,
  162. payer: provider.wallet.publicKey,
  163. })
  164. .rpc();
  165. });
  166. it("Initialize Original Component on Entity 1, trough the world instance", async () => {
  167. const componentEntity1 = FindComponentPda(
  168. boltComponentProgramOrigin.programId,
  169. entity1,
  170. "origin-component"
  171. );
  172. await worldProgram.methods
  173. .initializeComponent()
  174. .accounts({
  175. payer: provider.wallet.publicKey,
  176. data: componentEntity1,
  177. componentProgram: boltComponentProgramOrigin.programId,
  178. entity: entity1,
  179. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  180. authority: provider.wallet.publicKey,
  181. })
  182. .rpc();
  183. });
  184. it("Initialize Original Component on Entity 2, trough the world instance", async () => {
  185. const componentEntity2 = FindComponentPda(
  186. boltComponentProgramOrigin.programId,
  187. entity2,
  188. "origin-component"
  189. );
  190. await worldProgram.methods
  191. .initializeComponent()
  192. .accounts({
  193. payer: provider.wallet.publicKey,
  194. data: componentEntity2,
  195. componentProgram: boltComponentProgramOrigin.programId,
  196. entity: entity2,
  197. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  198. authority: provider.wallet.publicKey,
  199. })
  200. .rpc();
  201. });
  202. it("Initialize Position Component on Entity 1", async () => {
  203. componentPositionEntity1 = FindComponentPda(
  204. boltComponentPositionProgram.programId,
  205. entity1
  206. );
  207. await worldProgram.methods
  208. .initializeComponent()
  209. .accounts({
  210. payer: provider.wallet.publicKey,
  211. data: componentPositionEntity1,
  212. componentProgram: boltComponentPositionProgram.programId,
  213. entity: entity1,
  214. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  215. authority: worldProgram.programId,
  216. })
  217. .rpc();
  218. });
  219. it("Initialize Velocity Component on Entity 1", async () => {
  220. componentVelocityEntity1 = FindComponentPda(
  221. boltComponentVelocityProgram.programId,
  222. entity1,
  223. "component-velocity"
  224. );
  225. await worldProgram.methods
  226. .initializeComponent()
  227. .accounts({
  228. payer: provider.wallet.publicKey,
  229. data: componentVelocityEntity1,
  230. componentProgram: boltComponentVelocityProgram.programId,
  231. entity: entity1,
  232. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  233. authority: worldProgram.programId,
  234. })
  235. .rpc();
  236. });
  237. it("Initialize Position Component on Entity 2", async () => {
  238. componentPositionEntity2 = FindComponentPda(
  239. boltComponentPositionProgram.programId,
  240. entity2
  241. );
  242. await worldProgram.methods
  243. .initializeComponent()
  244. .accounts({
  245. payer: provider.wallet.publicKey,
  246. data: componentPositionEntity2,
  247. componentProgram: boltComponentPositionProgram.programId,
  248. entity: entity2,
  249. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  250. authority: worldProgram.programId,
  251. })
  252. .rpc();
  253. });
  254. it("Initialize Position Component on Entity 5", async () => {
  255. componentPositionEntity5 = FindComponentPda(
  256. boltComponentPositionProgram.programId,
  257. entity5
  258. );
  259. await worldProgram.methods
  260. .initializeComponent()
  261. .accounts({
  262. payer: provider.wallet.publicKey,
  263. data: componentPositionEntity5,
  264. componentProgram: boltComponentPositionProgram.programId,
  265. entity: entity5,
  266. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  267. authority: provider.wallet.publicKey,
  268. })
  269. .rpc();
  270. });
  271. it("Check Position on Entity 1 is default", async () => {
  272. expect(
  273. (
  274. await boltComponentPositionProgram.account.position.fetch(
  275. componentPositionEntity1
  276. )
  277. ).x.toNumber()
  278. ).to.equal(0);
  279. expect(
  280. (
  281. await boltComponentPositionProgram.account.position.fetch(
  282. componentPositionEntity1
  283. )
  284. ).y.toNumber()
  285. ).to.equal(0);
  286. expect(
  287. (
  288. await boltComponentPositionProgram.account.position.fetch(
  289. componentPositionEntity1
  290. )
  291. ).z.toNumber()
  292. ).to.equal(0);
  293. });
  294. it("Simple Movement System and Up direction on Entity 1", async () => {
  295. const args = {
  296. direction: Direction.Up,
  297. };
  298. await worldProgram.methods
  299. .apply(serializeArgs(args)) // Move Up
  300. .accounts({
  301. componentProgram: boltComponentPositionProgram.programId,
  302. boltSystem: systemSimpleMovement,
  303. boltComponent: componentPositionEntity1,
  304. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  305. authority: worldProgram.programId,
  306. })
  307. .rpc({ skipPreflight: true });
  308. expect(
  309. (
  310. await boltComponentPositionProgram.account.position.fetch(
  311. componentPositionEntity1
  312. )
  313. ).y.toNumber()
  314. ).to.equal(1);
  315. const componentData =
  316. await boltComponentPositionProgram.account.position.fetch(
  317. componentPositionEntity1
  318. );
  319. const x = componentData.x.toNumber();
  320. const y = componentData.y.toNumber();
  321. const z = componentData.z.toNumber();
  322. console.log("+-----------------------------+");
  323. console.log("| Movement System: Entity 1 |");
  324. console.log("+----------------+------------+");
  325. console.log("| Coordinate | Value |");
  326. console.log("+----------------+------------+");
  327. console.log(`| X Position | ${String(x).padEnd(10, " ")} |`);
  328. console.log("| | |");
  329. console.log(`| Y Position | ${String(y).padEnd(10, " ")} |`);
  330. console.log("| | |");
  331. console.log(`| Z Position | ${String(z).padEnd(10, " ")} |`);
  332. console.log("+----------------+------------+");
  333. console.log("| |");
  334. console.log("+-----------------------------+");
  335. console.log("Component Position: ", componentPositionEntity1.toString());
  336. });
  337. it("Simple Movement System and Right direction on Entity 1", async () => {
  338. const args = {
  339. direction: Direction.Right,
  340. };
  341. await worldProgram.methods
  342. .apply(serializeArgs(args)) // Move Right
  343. .accounts({
  344. componentProgram: boltComponentPositionProgram.programId,
  345. boltSystem: systemSimpleMovement,
  346. boltComponent: componentPositionEntity1,
  347. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  348. authority: worldProgram.programId,
  349. })
  350. .rpc();
  351. expect(
  352. (
  353. await boltComponentPositionProgram.account.position.fetch(
  354. componentPositionEntity1
  355. )
  356. ).y.toNumber()
  357. ).to.equal(1);
  358. expect(
  359. (
  360. await boltComponentPositionProgram.account.position.fetch(
  361. componentPositionEntity1
  362. )
  363. ).y.toNumber()
  364. ).to.equal(1);
  365. const componentData =
  366. await boltComponentPositionProgram.account.position.fetch(
  367. componentPositionEntity1
  368. );
  369. const x = componentData.x.toNumber();
  370. const y = componentData.y.toNumber();
  371. const z = componentData.z.toNumber();
  372. console.log("+-----------------------------+");
  373. console.log("| Movement System: Entity 1 |");
  374. console.log("+----------------+------------+");
  375. console.log("| Coordinate | Value |");
  376. console.log("+----------------+------------+");
  377. console.log(`| X Position | ${String(x).padEnd(10, " ")} |`);
  378. console.log("| | |");
  379. console.log(`| Y Position | ${String(y).padEnd(10, " ")} |`);
  380. console.log("| | |");
  381. console.log(`| Z Position | ${String(z).padEnd(10, " ")} |`);
  382. console.log("+----------------+------------+");
  383. console.log("| |");
  384. console.log("+-----------------------------+");
  385. });
  386. it("Fly System on Entity 1", async () => {
  387. await worldProgram.methods
  388. .apply(Buffer.alloc(0)) // Move Up
  389. .accounts({
  390. componentProgram: boltComponentPositionProgram.programId,
  391. boltSystem: systemFly,
  392. boltComponent: componentPositionEntity1,
  393. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  394. authority: worldProgram.programId,
  395. })
  396. .rpc();
  397. expect(
  398. (
  399. await boltComponentPositionProgram.account.position.fetch(
  400. componentPositionEntity1
  401. )
  402. ).z.toNumber()
  403. ).to.equal(1);
  404. const componentData =
  405. await boltComponentPositionProgram.account.position.fetch(
  406. componentPositionEntity1
  407. );
  408. const x = componentData.x.toNumber();
  409. const y = componentData.y.toNumber();
  410. const z = componentData.z.toNumber();
  411. console.log("+-----------------------------+");
  412. console.log("| Fly: Position Entity 1 |");
  413. console.log("+----------------+------------+");
  414. console.log("| Coordinate | Value |");
  415. console.log("+----------------+------------+");
  416. console.log(`| X Position | ${String(x).padEnd(10, " ")} |`);
  417. console.log("| | |");
  418. console.log(`| Y Position | ${String(y).padEnd(10, " ")} |`);
  419. console.log("| | |");
  420. console.log(`| Z Position | ${String(z).padEnd(10, " ")} |`);
  421. console.log("+----------------+------------+");
  422. console.log("| |");
  423. console.log("+-----------------------------+");
  424. });
  425. it("Apply Velocity on Entity 1", async () => {
  426. await worldProgram.methods
  427. .apply2(Buffer.alloc(0))
  428. .accounts({
  429. componentProgram1: boltComponentVelocityProgram.programId,
  430. componentProgram2: boltComponentPositionProgram.programId,
  431. boltSystem: applyVelocity,
  432. boltComponent1: componentVelocityEntity1,
  433. boltComponent2: componentPositionEntity1,
  434. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  435. authority: worldProgram.programId,
  436. })
  437. .rpc();
  438. const componentData =
  439. await boltComponentVelocityProgram.account.velocity.fetch(
  440. componentVelocityEntity1
  441. );
  442. let x = componentData.x.toNumber();
  443. let y = componentData.y.toNumber();
  444. let z = componentData.z.toNumber();
  445. const tmp = componentData.lastApplied.toNumber();
  446. console.log("+-----------------------------+");
  447. console.log("| Apply Velocity: Velocity Entity 1 |");
  448. console.log("+----------------+------------+");
  449. console.log("| Coordinate | Value |");
  450. console.log("+----------------+------------+");
  451. console.log(`| X Position | ${String(x).padEnd(10, " ")} |`);
  452. console.log("| | |");
  453. console.log(`| Y Position | ${String(y).padEnd(10, " ")} |`);
  454. console.log("| | |");
  455. console.log(`| Z Position | ${String(z).padEnd(10, " ")} |`);
  456. console.log("| | |");
  457. console.log(`| Timestamp | ${String(tmp).padEnd(10, " ")} |`);
  458. console.log("+----------------+------------+");
  459. console.log("| |");
  460. console.log("+-----------------------------+");
  461. const positionData =
  462. await boltComponentPositionProgram.account.position.fetch(
  463. componentPositionEntity1
  464. );
  465. x = positionData.x.toNumber();
  466. y = positionData.y.toNumber();
  467. z = positionData.z.toNumber();
  468. console.log("+-----------------------------+");
  469. console.log("| Apply Velocity: Position Entity 1 |");
  470. console.log("+----------------+------------+");
  471. console.log("| Coordinate | Value |");
  472. console.log("+----------------+------------+");
  473. console.log(`| X Position | ${String(x).padEnd(10, " ")} |`);
  474. console.log("| | |");
  475. console.log(`| Y Position | ${String(y).padEnd(10, " ")} |`);
  476. console.log("| | |");
  477. console.log(`| Z Position | ${String(z).padEnd(10, " ")} |`);
  478. console.log("+----------------+------------+");
  479. console.log("| |");
  480. console.log("+-----------------------------+");
  481. expect(positionData.z.toNumber()).to.not.equal(300);
  482. });
  483. it("Apply Velocity on Entity 1, with Clock external account", async () => {
  484. await worldProgram.methods
  485. .apply2(Buffer.alloc(0))
  486. .accounts({
  487. componentProgram1: boltComponentVelocityProgram.programId,
  488. componentProgram2: boltComponentPositionProgram.programId,
  489. boltSystem: applyVelocity,
  490. boltComponent1: componentVelocityEntity1,
  491. boltComponent2: componentPositionEntity1,
  492. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  493. authority: worldProgram.programId,
  494. })
  495. .remainingAccounts([
  496. {
  497. pubkey: new web3.PublicKey(
  498. "SysvarC1ock11111111111111111111111111111111"
  499. ),
  500. isWritable: false,
  501. isSigner: false,
  502. },
  503. ])
  504. .rpc();
  505. const positionData =
  506. await boltComponentPositionProgram.account.position.fetch(
  507. componentPositionEntity1
  508. );
  509. // Check if the position has changed to 300 (which means the account clock was used)
  510. expect(positionData.z.toNumber()).to.equal(300);
  511. });
  512. // Check illegal authority usage
  513. it("Check invalid component update", async () => {
  514. const componentDataPrev =
  515. await boltComponentPositionProgram.account.position.fetch(
  516. componentPositionEntity5
  517. );
  518. try {
  519. await worldProgram.methods
  520. .apply(Buffer.alloc(0)) // Move Up
  521. .accounts({
  522. componentProgram: boltComponentPositionProgram.programId,
  523. boltSystem: systemFly,
  524. boltComponent: componentPositionEntity5,
  525. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  526. authority: worldProgram.programId,
  527. })
  528. .rpc();
  529. } catch (e) {
  530. expect(e.message).to.contain("Invalid authority");
  531. }
  532. const componentData =
  533. await boltComponentPositionProgram.account.position.fetch(
  534. componentPositionEntity5
  535. );
  536. expect(
  537. componentDataPrev.x.toNumber() === componentData.x.toNumber() &&
  538. componentDataPrev.y.toNumber() === componentData.y.toNumber() &&
  539. componentDataPrev.z.toNumber() === componentData.z.toNumber()
  540. ).to.equal(true);
  541. });
  542. // Check illegal call, without CPI
  543. it("Check invalid init without CPI", async () => {
  544. let invalid = false;
  545. const componentVelocityEntity5 = FindComponentPda(
  546. boltComponentVelocityProgram.programId,
  547. entity5
  548. );
  549. try {
  550. await boltComponentProgramOrigin.methods
  551. .initialize()
  552. .accounts({
  553. payer: provider.wallet.publicKey,
  554. data: componentVelocityEntity5,
  555. entity: entity5,
  556. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  557. systemProgram: anchor.web3.SystemProgram.programId,
  558. authority: provider.wallet.publicKey,
  559. })
  560. .rpc();
  561. } catch (e) {
  562. invalid = true;
  563. }
  564. expect(invalid).to.equal(true);
  565. });
  566. // Check illegal call, without CPI
  567. it("Check invalid update without CPI", async () => {
  568. let invalid = false;
  569. const componentVelocityEntity5 = FindComponentPda(
  570. boltComponentVelocityProgram.programId,
  571. entity5
  572. );
  573. try {
  574. await boltComponentProgramOrigin.methods
  575. .update(null)
  576. .accounts({
  577. boltComponent: componentVelocityEntity5,
  578. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  579. authority: provider.wallet.publicKey,
  580. })
  581. .rpc();
  582. } catch (e) {
  583. invalid = true;
  584. }
  585. expect(invalid).to.equal(true);
  586. });
  587. });