transactions.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import {
  2. createAddEntityInstruction,
  3. createInitializeComponentInstruction,
  4. createInitializeNewWorldInstruction,
  5. FindComponentPda,
  6. FindEntityPda,
  7. FindWorldPda,
  8. FindRegistryPda,
  9. Registry,
  10. SerializeArgs,
  11. SYSVAR_INSTRUCTIONS_PUBKEY,
  12. World,
  13. } from "../index";
  14. import BN from "bn.js";
  15. import type web3 from "@solana/web3.js";
  16. import {
  17. type Connection,
  18. type PublicKey,
  19. Transaction,
  20. type TransactionInstruction,
  21. } from "@solana/web3.js";
  22. import type WorldProgram from "../generated";
  23. import { PROGRAM_ID, worldIdl } from "../generated";
  24. import { type Idl, Program } from "@coral-xyz/anchor";
  25. const MAX_COMPONENTS = 5;
  26. /**
  27. * Create the transaction to Initialize a new world
  28. * @param payer
  29. * @param connection
  30. * @constructor
  31. */
  32. export async function InitializeNewWorld({
  33. payer,
  34. connection,
  35. }: {
  36. payer: PublicKey;
  37. connection: Connection;
  38. }): Promise<{
  39. instruction: TransactionInstruction;
  40. transaction: Transaction;
  41. worldPda: PublicKey;
  42. worldId: BN;
  43. }> {
  44. const registryPda = FindRegistryPda({});
  45. const registry = await Registry.fromAccountAddress(connection, registryPda);
  46. const worldId = new BN(registry.worlds);
  47. const worldPda = FindWorldPda({ worldId });
  48. const initializeWorldIx = createInitializeNewWorldInstruction({
  49. world: worldPda,
  50. registry: registryPda,
  51. payer,
  52. });
  53. return {
  54. instruction: initializeWorldIx,
  55. transaction: new Transaction().add(initializeWorldIx),
  56. worldPda,
  57. worldId,
  58. };
  59. }
  60. /**
  61. * Create the transaction to Add a new authority
  62. * @param authority
  63. * @param newAuthority
  64. * @param world
  65. * @param connection
  66. * @constructor
  67. */
  68. export async function AddAuthority({
  69. authority,
  70. newAuthority,
  71. world,
  72. connection,
  73. }: {
  74. authority: PublicKey;
  75. newAuthority: PublicKey;
  76. world: PublicKey;
  77. connection: Connection;
  78. }): Promise<{
  79. instruction: TransactionInstruction;
  80. transaction: Transaction;
  81. }> {
  82. const program = new Program(
  83. worldIdl as Idl,
  84. ) as unknown as Program<WorldProgram>;
  85. const worldInstance = await World.fromAccountAddress(connection, world);
  86. const worldId = new BN(worldInstance.id);
  87. const addAuthorityIx = await program.methods
  88. .addAuthority(worldId)
  89. .accounts({
  90. authority,
  91. newAuthority,
  92. world,
  93. })
  94. .instruction();
  95. return {
  96. instruction: addAuthorityIx,
  97. transaction: new Transaction().add(addAuthorityIx),
  98. };
  99. }
  100. /**
  101. * Create the transaction to Remove an authority
  102. * @param authority
  103. * @param authorityToDelete
  104. * @param world
  105. * @param connection
  106. * @constructor
  107. */
  108. export async function RemoveAuthority({
  109. authority,
  110. authorityToDelete,
  111. world,
  112. connection,
  113. }: {
  114. authority: PublicKey;
  115. authorityToDelete: PublicKey;
  116. world: PublicKey;
  117. connection: Connection;
  118. }): Promise<{
  119. instruction: TransactionInstruction;
  120. transaction: Transaction;
  121. }> {
  122. const program = new Program(
  123. worldIdl as Idl,
  124. ) as unknown as Program<WorldProgram>;
  125. const worldInstance = await World.fromAccountAddress(connection, world);
  126. const worldId = new BN(worldInstance.id);
  127. const removeAuthorityIx = await program.methods
  128. .removeAuthority(worldId)
  129. .accounts({
  130. authority,
  131. authorityToDelete,
  132. world,
  133. })
  134. .instruction();
  135. return {
  136. instruction: removeAuthorityIx,
  137. transaction: new Transaction().add(removeAuthorityIx),
  138. };
  139. }
  140. /**
  141. * Create the transaction to Approve a system
  142. * @param authority
  143. * @param systemToApprove
  144. * @param world
  145. * @constructor
  146. */
  147. export async function ApproveSystem({
  148. authority,
  149. systemToApprove,
  150. world,
  151. }: {
  152. authority: PublicKey;
  153. systemToApprove: PublicKey;
  154. world: PublicKey;
  155. }): Promise<{
  156. instruction: TransactionInstruction;
  157. transaction: Transaction;
  158. }> {
  159. const program = new Program(
  160. worldIdl as Idl,
  161. ) as unknown as Program<WorldProgram>;
  162. const approveSystemIx = await program.methods
  163. .approveSystem()
  164. .accounts({
  165. authority,
  166. system: systemToApprove,
  167. world,
  168. })
  169. .instruction();
  170. return {
  171. instruction: approveSystemIx,
  172. transaction: new Transaction().add(approveSystemIx),
  173. };
  174. }
  175. /**
  176. * Create the transaction to Remove a system
  177. * @param authority
  178. * @param systemToRemove
  179. * @param world
  180. * @constructor
  181. */
  182. export async function RemoveSystem({
  183. authority,
  184. systemToRemove,
  185. world,
  186. }: {
  187. authority: PublicKey;
  188. systemToRemove: PublicKey;
  189. world: PublicKey;
  190. }): Promise<{
  191. instruction: TransactionInstruction;
  192. transaction: Transaction;
  193. }> {
  194. const program = new Program(
  195. worldIdl as Idl,
  196. ) as unknown as Program<WorldProgram>;
  197. const removeSystemIx = await program.methods
  198. .removeSystem()
  199. .accounts({
  200. authority,
  201. system: systemToRemove,
  202. world,
  203. })
  204. .instruction();
  205. return {
  206. instruction: removeSystemIx,
  207. transaction: new Transaction().add(removeSystemIx),
  208. };
  209. }
  210. /**
  211. * Create the transaction to Add a new entity
  212. * @param payer
  213. * @param worldPda
  214. * @param connection
  215. * @constructor
  216. */
  217. export async function AddEntity({
  218. payer,
  219. world,
  220. seed,
  221. connection,
  222. }: {
  223. payer: PublicKey;
  224. world: PublicKey;
  225. seed?: Uint8Array;
  226. connection: Connection;
  227. }): Promise<{
  228. instruction: TransactionInstruction;
  229. transaction: Transaction;
  230. entityPda: PublicKey;
  231. }> {
  232. const worldInstance = await World.fromAccountAddress(connection, world);
  233. const worldId = new BN(worldInstance.id);
  234. const entityPda =
  235. seed !== undefined
  236. ? FindEntityPda({ worldId, seed })
  237. : FindEntityPda({ worldId, entityId: new BN(worldInstance.entities) });
  238. const addEntityIx = createAddEntityInstruction(
  239. {
  240. world,
  241. payer,
  242. entity: entityPda,
  243. },
  244. { extraSeed: seed ?? null },
  245. );
  246. return {
  247. instruction: addEntityIx,
  248. transaction: new Transaction().add(addEntityIx),
  249. entityPda,
  250. };
  251. }
  252. /**
  253. * Create the transaction to Initialize a new component
  254. * @param payer
  255. * @param entityPda
  256. * @param componentId
  257. * @param seeds
  258. * @param authority
  259. * @param anchorRemainingAccounts
  260. * @constructor
  261. */
  262. export async function InitializeComponent({
  263. payer,
  264. entity,
  265. componentId,
  266. seed = "",
  267. authority,
  268. anchorRemainingAccounts,
  269. }: {
  270. payer: PublicKey;
  271. entity: PublicKey;
  272. componentId: PublicKey;
  273. seed?: string;
  274. authority?: web3.PublicKey;
  275. anchorRemainingAccounts?: web3.AccountMeta[];
  276. }): Promise<{
  277. instruction: TransactionInstruction;
  278. transaction: Transaction;
  279. componentPda: PublicKey;
  280. }> {
  281. const componentPda = FindComponentPda({ componentId, entity, seed });
  282. const initializeComponentIx = createInitializeComponentInstruction({
  283. payer,
  284. entity,
  285. data: componentPda,
  286. componentProgram: componentId,
  287. authority: authority ?? PROGRAM_ID,
  288. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  289. anchorRemainingAccounts,
  290. });
  291. return {
  292. instruction: initializeComponentIx,
  293. transaction: new Transaction().add(initializeComponentIx),
  294. componentPda,
  295. };
  296. }
  297. interface ApplySystemInstruction {
  298. authority: PublicKey;
  299. systemId: PublicKey;
  300. entities: ApplySystemEntity[];
  301. world: PublicKey;
  302. extraAccounts?: web3.AccountMeta[];
  303. args?: object;
  304. }
  305. function getApplyInstructionFunctionName(componentsCount: number) {
  306. return `apply${componentsCount > 1 ? componentsCount : ""}`;
  307. }
  308. function getBoltComponentName(index: number, componentsCount: number) {
  309. if (componentsCount === 1) return "boltComponent";
  310. return `boltComponent${index + 1}`;
  311. }
  312. function getBoltComponentProgramName(index: number, componentsCount: number) {
  313. if (componentsCount === 1) return "componentProgram";
  314. return `componentProgram${index + 1}`;
  315. }
  316. async function createApplySystemInstruction({
  317. authority,
  318. systemId,
  319. entities,
  320. world,
  321. extraAccounts,
  322. args,
  323. }: ApplySystemInstruction): Promise<web3.TransactionInstruction> {
  324. const program = new Program(
  325. worldIdl as Idl,
  326. ) as unknown as Program<WorldProgram>;
  327. let componentCount = 0;
  328. entities.forEach(function (entity) {
  329. componentCount += entity.components.length;
  330. });
  331. if (componentCount <= 0) {
  332. throw new Error("No components provided");
  333. }
  334. if (componentCount > MAX_COMPONENTS) {
  335. throw new Error(
  336. `Not implemented for component counts outside 1-${MAX_COMPONENTS}`,
  337. );
  338. }
  339. const applyAccounts = {
  340. authority: authority ?? PROGRAM_ID,
  341. boltSystem: systemId,
  342. instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
  343. world,
  344. };
  345. let componentIndex = 0;
  346. entities.forEach(function (entity) {
  347. entity.components.forEach(function (component) {
  348. const componentPda = FindComponentPda({
  349. componentId: component.componentId,
  350. entity: entity.entity,
  351. seed: component.seed,
  352. });
  353. applyAccounts[
  354. getBoltComponentProgramName(componentIndex, componentCount)
  355. ] = component.componentId;
  356. applyAccounts[getBoltComponentName(componentIndex, componentCount)] =
  357. componentPda;
  358. componentIndex++;
  359. });
  360. });
  361. return program.methods[getApplyInstructionFunctionName(componentCount)](
  362. SerializeArgs(args),
  363. )
  364. .accounts(applyAccounts)
  365. .remainingAccounts(extraAccounts ?? [])
  366. .instruction();
  367. }
  368. interface ApplySystemEntity {
  369. entity: PublicKey;
  370. components: ApplySystemComponent[];
  371. }
  372. interface ApplySystemComponent {
  373. componentId: PublicKey;
  374. seed?: string;
  375. }
  376. /**
  377. * Apply a system to a set of components
  378. * @param authority
  379. * @param systemId
  380. * @param entities
  381. * @param extraAccounts
  382. * @param args
  383. * @constructor
  384. */
  385. export async function ApplySystem({
  386. authority,
  387. systemId,
  388. entities,
  389. world,
  390. extraAccounts,
  391. args,
  392. }: {
  393. authority: PublicKey;
  394. systemId: PublicKey;
  395. entities: ApplySystemEntity[];
  396. world: PublicKey;
  397. extraAccounts?: web3.AccountMeta[];
  398. args?: object;
  399. }): Promise<{ instruction: TransactionInstruction; transaction: Transaction }> {
  400. const applySystemIx = await createApplySystemInstruction({
  401. authority,
  402. systemId,
  403. entities,
  404. world,
  405. extraAccounts,
  406. args,
  407. });
  408. return {
  409. instruction: applySystemIx,
  410. transaction: new Transaction().add(applySystemIx),
  411. };
  412. }