瀏覽代碼

feat: Allow passing multiple entities in the ApplySystem function (#51)

## Problem

It was previously impossible to pass components from multiple entities
in the same system from the typescript SDK.

## Solution

Changing the `ApplySystem` function to accommodate for multiple
entities.

## Other changes (e.g. bug fixes, small refactors)

Refactored the integration tests to use the highest level API to
increase testing surface.

Also improved some of the error tests:
- verifying that exactly what we want is failing by checking error
messages
 - verify the actual content of the pdas after the calls
 
Removed the unused `bolt.js`

## Notes

Matching PR for docs update:
https://github.com/magicblock-labs/docs/pull/6
crypto-vincent 1 年之前
父節點
當前提交
eef1e0e05f
共有 5 個文件被更改,包括 542 次插入1191 次删除
  1. 39 35
      cli/src/rust_template.rs
  2. 43 40
      clients/bolt-sdk/src/index.ts
  3. 87 85
      clients/bolt-sdk/src/world/transactions.ts
  4. 0 515
      tests/bolt.js
  5. 373 516
      tests/bolt.ts

+ 39 - 35
cli/src/rust_template.rs

@@ -410,7 +410,6 @@ import {{
     AddEntity,
     InitializeComponent,
     ApplySystem,
-    FindComponentPda,
 }} from "@magicblock-labs/bolt-sdk"
 import {{expect}} from "chai";
 
@@ -422,6 +421,7 @@ describe("{}", () => {{
   // Constants used to test the program.
   let worldPda: PublicKey;
   let entityPda: PublicKey;
+  let componentPda: PublicKey;
 
   const positionComponent = anchor.workspace.Position as Program<Position>;
   const systemMovement = anchor.workspace.Movement as Program<Movement>;
@@ -437,47 +437,51 @@ describe("{}", () => {{
   }});
 
   it("Add an entity", async () => {{
-      const addEntity = await AddEntity({{
-        payer: provider.wallet.publicKey,
-        world: worldPda,
-        connection: provider.connection,
-      }});
-      const txSign = await provider.sendAndConfirm(addEntity.transaction);
-      entityPda = addEntity.entityPda;
-      console.log(`Initialized a new Entity (ID=${{addEntity.entityId}}). Initialization signature: ${{txSign}}`);
+    const addEntity = await AddEntity({{
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      connection: provider.connection,
+    }});
+    const txSign = await provider.sendAndConfirm(addEntity.transaction);
+    entityPda = addEntity.entityPda;
+    console.log(`Initialized a new Entity (ID=${{addEntity.entityId}}). Initialization signature: ${{txSign}}`);
   }});
 
   it("Add a component", async () => {{
-      const initComponent = await InitializeComponent({{
-          payer: provider.wallet.publicKey,
-          entity: entityPda,
-          componentId: positionComponent.programId,
-      }});
-      const txSign = await provider.sendAndConfirm(initComponent.transaction);
-      console.log(`Initialized the grid component. Initialization signature: ${{txSign}}`);
+    const initializeComponent = await InitializeComponent({{
+      payer: provider.wallet.publicKey,
+      entity: entityPda,
+      componentId: positionComponent.programId,
+    }});
+    const txSign = await provider.sendAndConfirm(initializeComponent.transaction);
+    componentPda = initializeComponent.componentPda;
+    console.log(`Initialized the grid component. Initialization signature: ${{txSign}}`);
   }});
 
   it("Apply a system", async () => {{
-      const positionComponentPda = FindComponentPda(positionComponent.programId, entityPda);
-      // Check that the component has been initialized and x is 0
-      let positionData = await positionComponent.account.position.fetch(
-          positionComponentPda
-      );
-
-      const applySystem = await ApplySystem({{
-        authority: provider.wallet.publicKey,
-        system: systemMovement.programId,
+    // Check that the component has been initialized and x is 0
+    const positionBefore = await positionComponent.account.position.fetch(
+      componentPda
+    );
+    expect(positionBefore.x.toNumber()).to.equal(0);
+
+    // Run the movement system
+    const applySystem = await ApplySystem({{
+      authority: provider.wallet.publicKey,
+      systemId: systemMovement.programId,
+      entities: [{{
         entity: entityPda,
-        components: [positionComponent.programId],
-      }});
-      const txSign = await provider.sendAndConfirm(applySystem.transaction);
-      console.log(`Applied a system. Signature: ${{txSign}}`);
-
-      // Check that the system has been applied and x is > 0
-      positionData = await positionComponent.account.position.fetch(
-          positionComponentPda
-      );
-      expect(positionData.x.toNumber()).to.gt(0);
+        components: [{{ componentId: positionComponent.programId }}],
+      }}]
+    }});
+    const txSign = await provider.sendAndConfirm(applySystem.transaction);
+    console.log(`Applied a system. Signature: ${{txSign}}`);
+
+    // Check that the system has been applied and x is > 0
+    const positionAfter = await positionComponent.account.position.fetch(
+      componentPda
+    );
+    expect(positionAfter.x.toNumber()).to.gt(0);
   }});
 
 }});

+ 43 - 40
clients/bolt-sdk/src/index.ts

@@ -1,75 +1,78 @@
 import { PublicKey } from "@solana/web3.js";
-import BN from "bn.js";
+import type BN from "bn.js";
 import { PROGRAM_ID } from "./generated";
 export * from "./generated/accounts";
 export * from "./generated/instructions";
-export * from "./transactions/transactions";
+export * from "./world/transactions";
 export * from "./delegation/accounts";
 export * from "./delegation/delegate";
+export * from "./delegation/undelegate";
 
 export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
   "Sysvar1nstructions1111111111111111111111111"
 );
 
-export function FindWorldRegistryPda(
-  programId: PublicKey = new PublicKey(PROGRAM_ID)
-) {
+export function FindRegistryPda({ programId }: { programId?: PublicKey }) {
   return PublicKey.findProgramAddressSync(
     [Buffer.from("registry")],
-    programId
+    programId ?? PROGRAM_ID
   )[0];
 }
 
-export function FindWorldPda(
-  id: BN | string | number | Uint8Array,
-  programId: PublicKey = new PublicKey(PROGRAM_ID)
-) {
-  id = CastToBN(id);
-  const idBuffer = Buffer.from(id.toArrayLike(Buffer, "be", 8));
+export function FindWorldPda({
+  worldId,
+  programId,
+}: {
+  worldId: BN;
+  programId?: PublicKey;
+}) {
+  const idBuffer = Buffer.from(worldId.toArrayLike(Buffer, "be", 8));
   return PublicKey.findProgramAddressSync(
     [Buffer.from("world"), idBuffer],
-    programId
+    programId ?? PROGRAM_ID
   )[0];
 }
 
-export function FindEntityPda(
-  worldId: BN | string | number | Uint8Array,
-  entityId: BN | string | number | Uint8Array,
-  extraSeed?: string,
-  programId: PublicKey = new PublicKey(PROGRAM_ID)
-) {
-  worldId = CastToBN(worldId);
-  entityId = CastToBN(entityId);
+export function FindEntityPda({
+  worldId,
+  entityId,
+  seed,
+  programId,
+}: {
+  worldId: BN;
+  entityId?: BN;
+  seed?: string;
+  programId?: PublicKey;
+}) {
   const worldIdBuffer = Buffer.from(worldId.toArrayLike(Buffer, "be", 8));
-  const entityIdBuffer = Buffer.from(entityId.toArrayLike(Buffer, "be", 8));
   const seeds = [Buffer.from("entity"), worldIdBuffer];
-  if (extraSeed != null) {
+  if (seed !== undefined) {
     seeds.push(Buffer.from(new Uint8Array(8)));
-    seeds.push(Buffer.from(extraSeed));
-  } else {
+    seeds.push(Buffer.from(seed));
+  } else if (entityId !== undefined) {
+    const entityIdBuffer = Buffer.from(entityId.toArrayLike(Buffer, "be", 8));
     seeds.push(entityIdBuffer);
+  } else {
+    throw new Error("An entity must have either an Id or a Seed");
   }
-  return PublicKey.findProgramAddressSync(seeds, programId)[0];
+  return PublicKey.findProgramAddressSync(seeds, programId ?? PROGRAM_ID)[0];
 }
 
-export function FindComponentPda(
-  componentProgramId: PublicKey,
-  entity: PublicKey,
-  componentId: string = ""
-) {
+export function FindComponentPda({
+  componentId,
+  entity,
+  seed,
+}: {
+  componentId: PublicKey;
+  entity: PublicKey;
+  seed?: string;
+}) {
   return PublicKey.findProgramAddressSync(
-    [Buffer.from(componentId), entity.toBytes()],
-    componentProgramId
+    [Buffer.from(seed ?? ""), entity.toBytes()],
+    componentId
   )[0];
 }
 
-function CastToBN(id: BN | string | number | Uint8Array) {
-  if (!(id instanceof BN)) {
-    id = new BN(id);
-  }
-  return id;
-}
-
 /**
  * Serialize arguments to a buffer
  * @param args

+ 87 - 85
clients/bolt-sdk/src/transactions/transactions.ts → clients/bolt-sdk/src/world/transactions.ts

@@ -10,7 +10,7 @@ import {
   FindComponentPda,
   FindEntityPda,
   FindWorldPda,
-  FindWorldRegistryPda,
+  FindRegistryPda,
   Registry,
   SerializeArgs,
   SYSVAR_INSTRUCTIONS_PUBKEY,
@@ -36,10 +36,10 @@ export async function InitializeNewWorld({
   payer: PublicKey;
   connection: Connection;
 }): Promise<{ transaction: Transaction; worldPda: PublicKey; worldId: BN }> {
-  const registryPda = FindWorldRegistryPda();
+  const registryPda = FindRegistryPda({});
   const registry = await Registry.fromAccountAddress(connection, registryPda);
   const worldId = new BN(registry.worlds);
-  const worldPda = FindWorldPda(new BN(worldId));
+  const worldPda = FindWorldPda({ worldId });
   const initializeWorldIx = createInitializeNewWorldInstruction({
     world: worldPda,
     registry: registryPda,
@@ -62,28 +62,31 @@ export async function InitializeNewWorld({
 export async function AddEntity({
   payer,
   world,
+  seed,
   connection,
 }: {
   payer: PublicKey;
   world: PublicKey;
+  seed?: string;
   connection: Connection;
-}): Promise<{ transaction: Transaction; entityPda: PublicKey; entityId: BN }> {
+}): Promise<{ transaction: Transaction; entityPda: PublicKey }> {
   const worldInstance = await World.fromAccountAddress(connection, world);
-  const entityId = new BN(worldInstance.entities);
-  const entityPda = FindEntityPda(new BN(worldInstance.id), entityId);
-
-  const createEntityIx = createAddEntityInstruction(
+  const worldId = new BN(worldInstance.id);
+  const entityPda =
+    seed !== undefined
+      ? FindEntityPda({ worldId, seed })
+      : FindEntityPda({ worldId, entityId: new BN(worldInstance.entities) });
+  const addEntityIx = createAddEntityInstruction(
     {
       world,
       payer,
       entity: entityPda,
     },
-    { extraSeed: null }
+    { extraSeed: seed ?? null }
   );
   return {
-    transaction: new Transaction().add(createEntityIx),
+    transaction: new Transaction().add(addEntityIx),
     entityPda,
-    entityId,
   };
 }
 
@@ -112,60 +115,56 @@ export async function InitializeComponent({
   authority?: web3.PublicKey;
   anchorRemainingAccounts?: web3.AccountMeta[];
 }): Promise<{ transaction: Transaction; componentPda: PublicKey }> {
-  const componentPda = FindComponentPda(componentId, entity, seed);
-  const initComponentIx = createInitializeComponentInstruction({
+  const componentPda = FindComponentPda({ componentId, entity, seed });
+  const initializeComponentIx = createInitializeComponentInstruction({
     payer,
     entity,
     data: componentPda,
     componentProgram: componentId,
     authority: authority ?? PROGRAM_ID,
-    anchorRemainingAccounts,
     instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
+    anchorRemainingAccounts,
   });
-
   return {
-    transaction: new Transaction().add(initComponentIx),
+    transaction: new Transaction().add(initializeComponentIx),
     componentPda,
   };
 }
 
 interface ApplySystemInstruction {
-  entity: PublicKey;
-  components: PublicKey[];
-  system: PublicKey;
   authority: PublicKey;
-  seeds?: string[];
+  systemId: PublicKey;
+  entities: ApplySystemEntity[];
   extraAccounts?: web3.AccountMeta[];
   args?: object;
 }
-
-export function createApplySystemInstruction({
-  entity,
-  components,
-  system,
-  seeds,
+function getApplyInstructionFunctionName(componentsCount: number) {
+  if (componentsCount === 1) return "createApplyInstruction";
+  return `createApply${componentsCount}Instruction`;
+}
+function getBoltComponentName(index: number, componentsCount: number) {
+  if (componentsCount === 1) return "boltComponent";
+  return `boltComponent${index + 1}`;
+}
+function getBoltComponentProgramName(index: number, componentsCount: number) {
+  if (componentsCount === 1) return "componentProgram";
+  return `componentProgram${index + 1}`;
+}
+function createApplySystemInstruction({
   authority,
+  systemId,
+  entities,
   extraAccounts,
   args,
 }: ApplySystemInstruction): web3.TransactionInstruction {
-  const instructionFunctions = {
-    createApplyInstruction,
-    createApply2Instruction,
-    createApply3Instruction,
-    createApply4Instruction,
-    createApply5Instruction,
-  };
-  if (components.length === 0) throw new Error("No components provided");
-  if (seeds == null) seeds = new Array(components.length).fill("");
-  if (seeds.length !== components.length)
-    throw new Error("Seed length does not match components length");
-  const componentPdas: PublicKey[] = [];
-
-  for (let i = 0; i < components.length; i++) {
-    const componentPda = FindComponentPda(components[i], entity, seeds[i]);
-    componentPdas.push(componentPda);
+  let componentCount = 0;
+  entities.forEach(function (entity) {
+    componentCount += entity.components.length;
+  });
+  if (componentCount <= 0) {
+    throw new Error("No components provided");
   }
-  if (components.length < 1 || components.length > MAX_COMPONENTS) {
+  if (componentCount > MAX_COMPONENTS) {
     throw new Error(
       `Not implemented for component counts outside 1-${MAX_COMPONENTS}`
     );
@@ -173,58 +172,76 @@ export function createApplySystemInstruction({
 
   const instructionArgs = {
     authority: authority ?? PROGRAM_ID,
-    boltSystem: system,
+    boltSystem: systemId,
     instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
     anchorRemainingAccounts: extraAccounts,
   };
 
-  components.forEach((component, index) => {
-    instructionArgs[getBoltComponentProgramName(index, components.length)] =
-      component;
-    instructionArgs[getBoltComponentName(index, components.length)] =
-      componentPdas[index];
+  let componentIndex = 0;
+  entities.forEach(function (entity) {
+    entity.components.forEach(function (component) {
+      const componentPda = FindComponentPda({
+        componentId: component.componentId,
+        entity: entity.entity,
+        seed: component.seed,
+      });
+      instructionArgs[
+        getBoltComponentProgramName(componentIndex, componentCount)
+      ] = component.componentId;
+      instructionArgs[getBoltComponentName(componentIndex, componentCount)] =
+        componentPda;
+      componentIndex++;
+    });
   });
 
-  const functionName = getApplyInstructionFunctionName(components.length);
+  const instructionFunctions = {
+    createApplyInstruction,
+    createApply2Instruction,
+    createApply3Instruction,
+    createApply4Instruction,
+    createApply5Instruction,
+  };
+  const functionName = getApplyInstructionFunctionName(componentCount);
   return instructionFunctions[functionName](instructionArgs, {
     args: SerializeArgs(args),
   });
 }
 
+interface ApplySystemEntity {
+  entity: PublicKey;
+  components: ApplySystemComponent[];
+}
+interface ApplySystemComponent {
+  componentId: PublicKey;
+  seed?: string;
+}
+
 /**
- * Apply a system to an entity and its components
+ * Apply a system to a set of components
  * @param authority
- * @param system
- * @param entity
- * @param components
- * @param args
+ * @param systemId
+ * @param entities
  * @param extraAccounts
- * @param seeds
+ * @param args
  * @constructor
  */
 export async function ApplySystem({
   authority,
-  system,
-  entity,
-  components,
-  args = {},
+  systemId,
+  entities,
   extraAccounts,
-  seeds,
+  args,
 }: {
   authority: PublicKey;
-  system: PublicKey;
-  entity: PublicKey;
-  components: PublicKey[];
-  args?: object;
+  systemId: PublicKey;
+  entities: ApplySystemEntity[];
   extraAccounts?: web3.AccountMeta[];
-  seeds?: string[];
+  args?: object;
 }): Promise<{ transaction: Transaction }> {
   const applySystemIx = createApplySystemInstruction({
-    entity,
-    components,
-    system,
     authority,
-    seeds,
+    systemId,
+    entities,
     extraAccounts,
     args,
   });
@@ -232,18 +249,3 @@ export async function ApplySystem({
     transaction: new Transaction().add(applySystemIx),
   };
 }
-
-function getApplyInstructionFunctionName(componentsLength: number) {
-  if (componentsLength === 1) return "createApplyInstruction";
-  return `createApply${componentsLength}Instruction`;
-}
-
-function getBoltComponentName(index: number, componentsLength: number) {
-  if (componentsLength === 1) return "boltComponent";
-  return `boltComponent${index + 1}`;
-}
-
-function getBoltComponentProgramName(index: number, componentsLength: number) {
-  if (componentsLength === 1) return "componentProgram";
-  return `componentProgram${index + 1}`;
-}

+ 0 - 515
tests/bolt.js

@@ -1,515 +0,0 @@
-"use strict";
-var __createBinding =
-  (this && this.__createBinding) ||
-  (Object.create
-    ? function (o, m, k, k2) {
-        if (k2 === undefined) k2 = k;
-        var desc = Object.getOwnPropertyDescriptor(m, k);
-        if (
-          !desc ||
-          ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)
-        ) {
-          desc = {
-            enumerable: true,
-            get: function () {
-              return m[k];
-            },
-          };
-        }
-        Object.defineProperty(o, k2, desc);
-      }
-    : function (o, m, k, k2) {
-        if (k2 === undefined) k2 = k;
-        o[k2] = m[k];
-      });
-var __setModuleDefault =
-  (this && this.__setModuleDefault) ||
-  (Object.create
-    ? function (o, v) {
-        Object.defineProperty(o, "default", { enumerable: true, value: v });
-      }
-    : function (o, v) {
-        o["default"] = v;
-      });
-var __importStar =
-  (this && this.__importStar) ||
-  function (mod) {
-    if (mod && mod.__esModule) return mod;
-    var result = {};
-    if (mod != null)
-      for (var k in mod)
-        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
-          __createBinding(result, mod, k);
-    __setModuleDefault(result, mod);
-    return result;
-  };
-var __awaiter =
-  (this && this.__awaiter) ||
-  function (thisArg, _arguments, P, generator) {
-    function adopt(value) {
-      return value instanceof P
-        ? value
-        : new P(function (resolve) {
-            resolve(value);
-          });
-    }
-    return new (P || (P = Promise))(function (resolve, reject) {
-      function fulfilled(value) {
-        try {
-          step(generator.next(value));
-        } catch (e) {
-          reject(e);
-        }
-      }
-      function rejected(value) {
-        try {
-          step(generator["throw"](value));
-        } catch (e) {
-          reject(e);
-        }
-      }
-      function step(result) {
-        result.done
-          ? resolve(result.value)
-          : adopt(result.value).then(fulfilled, rejected);
-      }
-      step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-  };
-var __importDefault =
-  (this && this.__importDefault) ||
-  function (mod) {
-    return mod && mod.__esModule ? mod : { default: mod };
-  };
-Object.defineProperty(exports, "__esModule", { value: true });
-const anchor = __importStar(require("@coral-xyz/anchor"));
-const web3_js_1 = require("@solana/web3.js");
-const chai_1 = require("chai");
-const bn_js_1 = __importDefault(require("bn.js"));
-var Direction;
-(function (Direction) {
-  Direction["Left"] = "Left";
-  Direction["Right"] = "Right";
-  Direction["Up"] = "Up";
-  Direction["Down"] = "Down";
-})(Direction || (Direction = {}));
-function serializeArgs(args = {}) {
-  const jsonString = JSON.stringify(args);
-  const encoder = new TextEncoder();
-  const binaryData = encoder.encode(jsonString);
-  return Buffer.from(
-    binaryData.buffer,
-    binaryData.byteOffset,
-    binaryData.byteLength
-  );
-}
-describe("bolt", () => {
-  const provider = anchor.AnchorProvider.env();
-  anchor.setProvider(provider);
-  const worldProgram = anchor.workspace.World;
-  const boltComponentPositionProgram = anchor.workspace.ComponentPosition;
-  const boltComponentVelocityProgram = anchor.workspace.ComponentVelocity;
-  const boltComponentProgramOrigin = anchor.workspace.BoltComponent;
-  const systemSimpleMovement = anchor.workspace.SystemSimpleMovement.programId;
-  const systemFly = anchor.workspace.SystemFly.programId;
-  const applyVelocity = anchor.workspace.SystemApplyVelocity.programId;
-  let entity1;
-  let entity2;
-  let componentPositionEntity1;
-  let componentPositionEntity2;
-  let componentVelocityEntity1;
-  it("InitializeWorldsRegistry", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const registryPda = FindWorldRegistryPda(worldProgram);
-      yield worldProgram.methods
-        .initializeRegistry()
-        .accounts({
-          registry: registryPda,
-          payer: provider.wallet.publicKey,
-        })
-        .rpc();
-    }));
-  it("InitializeNewWorld", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const registryPda = FindWorldRegistryPda(worldProgram);
-      const worldPda = FindWorldPda(worldProgram, new bn_js_1.default(0));
-      yield worldProgram.methods
-        .initializeNewWorld()
-        .accounts({
-          world: worldPda,
-          registry: registryPda,
-          payer: provider.wallet.publicKey,
-        })
-        .rpc();
-    }));
-  it("Add entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const worldPda = FindWorldPda(worldProgram, new bn_js_1.default(0));
-      entity1 = FindEntityPda(
-        worldProgram,
-        new bn_js_1.default(0),
-        new bn_js_1.default(0)
-      );
-      yield worldProgram.methods
-        .addEntity()
-        .accounts({
-          world: worldPda,
-          entity: entity1,
-          payer: provider.wallet.publicKey,
-        })
-        .rpc();
-    }));
-  it("Add entity 2", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const worldPda = FindWorldPda(worldProgram, new bn_js_1.default(0));
-      entity2 = FindEntityPda(
-        worldProgram,
-        new bn_js_1.default(0),
-        new bn_js_1.default(1)
-      );
-      yield worldProgram.methods
-        .addEntity()
-        .accounts({
-          world: worldPda,
-          entity: entity2,
-          payer: provider.wallet.publicKey,
-        })
-        .rpc();
-    }));
-  it("Add entity 3", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const worldPda = FindWorldPda(worldProgram, new bn_js_1.default(0));
-      const entityPda = FindEntityPda(
-        worldProgram,
-        new bn_js_1.default(0),
-        new bn_js_1.default(2)
-      );
-      yield worldProgram.methods
-        .addEntity()
-        .accounts({
-          world: worldPda,
-          entity: entityPda,
-          payer: provider.wallet.publicKey,
-        })
-        .rpc();
-    }));
-  it("Initialize Original Component on Entity 1, trough the world instance", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      let componentEntity1 = FindComponentPda(
-        boltComponentProgramOrigin.programId,
-        entity1,
-        "origin-component"
-      );
-      yield worldProgram.methods
-        .initializeComponent()
-        .accounts({
-          payer: provider.wallet.publicKey,
-          data: componentEntity1,
-          componentProgram: boltComponentProgramOrigin.programId,
-          entity: entity1,
-        })
-        .rpc();
-    }));
-  it("Initialize Original Component on Entity 2, trough the world instance", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      let componentEntity2 = FindComponentPda(
-        boltComponentProgramOrigin.programId,
-        entity2,
-        "origin-component"
-      );
-      yield worldProgram.methods
-        .initializeComponent()
-        .accounts({
-          payer: provider.wallet.publicKey,
-          data: componentEntity2,
-          componentProgram: boltComponentProgramOrigin.programId,
-          entity: entity2,
-        })
-        .rpc();
-    }));
-  it("Initialize Position Component on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      componentPositionEntity1 = FindComponentPda(
-        boltComponentPositionProgram.programId,
-        entity1,
-        "component-position"
-      );
-      console.log(
-        "Component Position E1: ",
-        componentPositionEntity1.toBase58()
-      );
-      yield worldProgram.methods
-        .initializeComponent()
-        .accounts({
-          payer: provider.wallet.publicKey,
-          data: componentPositionEntity1,
-          componentProgram: boltComponentPositionProgram.programId,
-          entity: entity1,
-        })
-        .rpc();
-    }));
-  it("Initialize Velocity Component on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      componentVelocityEntity1 = FindComponentPda(
-        boltComponentVelocityProgram.programId,
-        entity1,
-        "component-velocity"
-      );
-      yield worldProgram.methods
-        .initializeComponent()
-        .accounts({
-          payer: provider.wallet.publicKey,
-          data: componentVelocityEntity1,
-          componentProgram: boltComponentVelocityProgram.programId,
-          entity: entity1,
-        })
-        .rpc();
-    }));
-  it("Initialize Position Component on Entity 2", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      componentPositionEntity2 = FindComponentPda(
-        boltComponentPositionProgram.programId,
-        entity2,
-        "component-position"
-      );
-      yield worldProgram.methods
-        .initializeComponent()
-        .accounts({
-          payer: provider.wallet.publicKey,
-          data: componentPositionEntity2,
-          componentProgram: boltComponentPositionProgram.programId,
-          entity: entity2,
-        })
-        .rpc();
-    }));
-  it("Check Position on Entity 1 is default", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).x.toNumber()
-      ).to.equal(0);
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).y.toNumber()
-      ).to.equal(0);
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).z.toNumber()
-      ).to.equal(0);
-    }));
-  it("Simple Movement System and Up direction on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const args = {
-        direction: Direction.Up,
-      };
-      yield worldProgram.methods
-        .apply(serializeArgs(args)) // Move Up
-        .accounts({
-          componentProgram: boltComponentPositionProgram.programId,
-          boltSystem: systemSimpleMovement,
-          boltComponent: componentPositionEntity1,
-        })
-        .rpc({ skipPreflight: true });
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).y.toNumber()
-      ).to.equal(1);
-      const componentData =
-        yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        );
-      const x = componentData.x.toNumber();
-      const y = componentData.y.toNumber();
-      const z = componentData.z.toNumber();
-      console.log("+-----------------------------+");
-      console.log("| Movement System:   Entity 1 |");
-      console.log("+----------------+------------+");
-      console.log("| Coordinate    | Value      |");
-      console.log("+----------------+------------+");
-      console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-      console.log("+----------------+------------+");
-      console.log("|                             |");
-      console.log("+-----------------------------+");
-    }));
-  it("Simple Movement System and Right direction on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      const args = {
-        direction: Direction.Right,
-      };
-      yield worldProgram.methods
-        .apply(serializeArgs(args)) // Move Right
-        .accounts({
-          componentProgram: boltComponentPositionProgram.programId,
-          boltSystem: systemSimpleMovement,
-          boltComponent: componentPositionEntity1,
-        })
-        .rpc({ skipPreflight: true });
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).y.toNumber()
-      ).to.equal(1);
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).y.toNumber()
-      ).to.equal(1);
-      const componentData =
-        yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        );
-      const x = componentData.x.toNumber();
-      const y = componentData.y.toNumber();
-      const z = componentData.z.toNumber();
-      console.log("+-----------------------------+");
-      console.log("| Movement System:   Entity 1 |");
-      console.log("+----------------+------------+");
-      console.log("| Coordinate    | Value      |");
-      console.log("+----------------+------------+");
-      console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-      console.log("+----------------+------------+");
-      console.log("|                             |");
-      console.log("+-----------------------------+");
-    }));
-  it("Fly System on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      yield worldProgram.methods
-        .apply(Buffer.alloc(0)) // Move Up
-        .accounts({
-          componentProgram: boltComponentPositionProgram.programId,
-          boltSystem: systemFly,
-          boltComponent: componentPositionEntity1,
-        })
-        .rpc();
-      (0, chai_1.expect)(
-        (yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )).z.toNumber()
-      ).to.equal(1);
-      const componentData =
-        yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        );
-      const x = componentData.x.toNumber();
-      const y = componentData.y.toNumber();
-      const z = componentData.z.toNumber();
-      console.log("+-----------------------------+");
-      console.log("| Fly: Position Entity 1      |");
-      console.log("+----------------+------------+");
-      console.log("| Coordinate    | Value      |");
-      console.log("+----------------+------------+");
-      console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-      console.log("+----------------+------------+");
-      console.log("|                             |");
-      console.log("+-----------------------------+");
-    }));
-  it("Apply Velocity on Entity 1", () =>
-    __awaiter(void 0, void 0, void 0, function* () {
-      yield worldProgram.methods
-        .apply2(Buffer.alloc(0))
-        .accounts({
-          componentProgram1: boltComponentVelocityProgram.programId,
-          componentProgram2: boltComponentPositionProgram.programId,
-          boltSystem: applyVelocity,
-          boltComponent1: componentVelocityEntity1,
-          boltComponent2: componentPositionEntity1,
-        })
-        .remainingAccounts([
-          {
-            pubkey: componentPositionEntity1,
-            isWritable: false,
-            isSigner: false,
-          },
-        ])
-        .rpc();
-      console.log("Component Velocity: ", componentVelocityEntity1.toBase58());
-      let componentData =
-        yield boltComponentVelocityProgram.account.velocity.fetch(
-          componentVelocityEntity1
-        );
-      let x = componentData.x.toNumber();
-      let y = componentData.y.toNumber();
-      let z = componentData.z.toNumber();
-      const tmp = componentData.lastApplied.toNumber();
-      console.log("+-----------------------------+");
-      console.log("| Apply Velocity: Velocity Entity 1      |");
-      console.log("+----------------+------------+");
-      console.log("| Coordinate    | Value      |");
-      console.log("+----------------+------------+");
-      console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Timestamp    | ${String(tmp).padEnd(10, " ")} |`);
-      console.log("+----------------+------------+");
-      console.log("|                             |");
-      console.log("+-----------------------------+");
-      let positionData =
-        yield boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        );
-      x = positionData.x.toNumber();
-      y = positionData.y.toNumber();
-      z = positionData.z.toNumber();
-      console.log("+-----------------------------+");
-      console.log("| Apply Velocity: Position Entity 1      |");
-      console.log("+----------------+------------+");
-      console.log("| Coordinate    | Value      |");
-      console.log("+----------------+------------+");
-      console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-      console.log("|               |            |");
-      console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-      console.log("+----------------+------------+");
-      console.log("|                             |");
-      console.log("+-----------------------------+");
-    }));
-  // Utils
-  function FindWorldRegistryPda(program) {
-    return web3_js_1.PublicKey.findProgramAddressSync(
-      [Buffer.from("registry")],
-      program.programId
-    )[0];
-  }
-  function FindWorldPda(program, id) {
-    return web3_js_1.PublicKey.findProgramAddressSync(
-      [Buffer.from("world"), id.toBuffer("le", 8)],
-      program.programId
-    )[0];
-  }
-  function FindEntityPda(program, worldId, entityId) {
-    return web3_js_1.PublicKey.findProgramAddressSync(
-      [
-        Buffer.from("entity"),
-        worldId.toBuffer("be", 8),
-        entityId.toBuffer("be", 8),
-      ],
-      program.programId
-    )[0];
-  }
-  function FindComponentPda(program, entity, seed = "component") {
-    return web3_js_1.PublicKey.findProgramAddressSync(
-      [Buffer.from(seed), entity.toBytes()],
-      program
-    )[0];
-  }
-});

+ 373 - 516
tests/bolt.ts

@@ -11,16 +11,16 @@ import { type World } from "../target/types/world";
 import { expect } from "chai";
 import BN from "bn.js";
 import {
+  AddEntity,
   createDelegateInstruction,
+  createUndelegateInstruction,
   createInitializeRegistryInstruction,
   DELEGATION_PROGRAM_ID,
-  FindComponentPda,
-  FindEntityPda,
-  FindWorldPda,
-  FindWorldRegistryPda,
-  SYSVAR_INSTRUCTIONS_PUBKEY,
+  FindRegistryPda,
+  InitializeComponent,
+  InitializeNewWorld,
+  ApplySystem,
 } from "../clients/bolt-sdk";
-import { createUndelegateInstruction } from "../clients/bolt-sdk/lib/delegation/undelegate";
 
 enum Direction {
   Left = "Left",
@@ -29,519 +29,359 @@ enum Direction {
   Down = "Down",
 }
 
-function serializeArgs(args: any = {}) {
-  const jsonString = JSON.stringify(args);
-  const encoder = new TextEncoder();
-  const binaryData = encoder.encode(jsonString);
-  return Buffer.from(
-    binaryData.buffer,
-    binaryData.byteOffset,
-    binaryData.byteLength
-  );
+function padCenter(value: string, width: number) {
+  const length = value.length;
+  if (width <= length) {
+    return value;
+  }
+  const padding = (width - length) / 2;
+  const align = width - padding;
+  return value.padStart(align, " ").padEnd(width, " ");
+}
+
+function logPosition(title: string, { x, y, z }: { x: BN; y: BN; z: BN }) {
+  console.log(" +----------------------------------+");
+  console.log(` | ${padCenter(title, 32)} |`);
+  console.log(" +-----------------+----------------+");
+  console.log(` | X Position      | ${String(x).padEnd(14, " ")} |`);
+  console.log(` | Y Position      | ${String(y).padEnd(14, " ")} |`);
+  console.log(` | Z Position      | ${String(z).padEnd(14, " ")} |`);
+  console.log(" +-----------------+----------------+");
+}
+
+function logVelocity(
+  title: string,
+  { x, y, z, lastApplied }: { x: BN; y: BN; z: BN; lastApplied: BN }
+) {
+  console.log(" +----------------------------------+");
+  console.log(` | ${padCenter(title, 32)} |`);
+  console.log(" +-----------------+----------------+");
+  console.log(` | X Velocity      | ${String(x).padEnd(14, " ")} |`);
+  console.log(` | Y Velocity      | ${String(y).padEnd(14, " ")} |`);
+  console.log(` | Z Velocity      | ${String(z).padEnd(14, " ")} |`);
+  console.log(` | Last Applied    | ${String(lastApplied).padEnd(14, " ")} |`);
+  console.log(" +-----------------+----------------+");
 }
 
 describe("bolt", () => {
   const provider = anchor.AnchorProvider.env();
   anchor.setProvider(provider);
 
-  const worldProgram = anchor.workspace.World as Program<World>;
-  const boltComponentPositionProgram = anchor.workspace
+  const boltWorld = anchor.workspace.World as Program<World>;
+  const boltComponentProgram = anchor.workspace
+    .BoltComponent as Program<BoltComponent>;
+
+  const exampleComponentPosition = anchor.workspace
     .Position as Program<Position>;
-  const boltComponentVelocityProgram = anchor.workspace
+  const exampleComponentVelocity = anchor.workspace
     .Velocity as Program<Velocity>;
-  const boltComponentProgramOrigin = anchor.workspace
-    .BoltComponent as Program<BoltComponent>;
 
-  const systemSimpleMovement = (
+  const exampleSystemSimpleMovement = (
     anchor.workspace.SystemSimpleMovement as Program<SystemSimpleMovement>
   ).programId;
-  const systemFly = (anchor.workspace.SystemFly as Program<SystemFly>)
+  const exampleSystemFly = (anchor.workspace.SystemFly as Program<SystemFly>)
     .programId;
-  const applyVelocity = (
+  const exampleSystemApplyVelocity = (
     anchor.workspace.SystemApplyVelocity as Program<SystemApplyVelocity>
   ).programId;
 
-  let entity1: PublicKey;
-  let entity2: PublicKey;
-  let entity5: PublicKey;
-  let componentPositionEntity1: PublicKey;
-  let componentPositionEntity2: PublicKey;
-  let componentPositionEntity5: PublicKey;
-  let componentVelocityEntity1: PublicKey;
+  let worldPda: PublicKey;
 
-  it("InitializeWorldsRegistry", async () => {
-    const registryPda = FindWorldRegistryPda(worldProgram.programId);
+  let entity1Pda: PublicKey;
+  let entity2Pda: PublicKey;
+  let entity4Pda: PublicKey;
+  let entity5Pda: PublicKey;
+
+  let componentPositionEntity1Pda: PublicKey;
+  let componentVelocityEntity1Pda: PublicKey;
+
+  let componentPositionEntity4Pda: PublicKey;
+  let componentPositionEntity5Pda: PublicKey;
+
+  it("InitializeRegistry", async () => {
+    const registryPda = FindRegistryPda({});
     const initializeRegistryIx = createInitializeRegistryInstruction({
       registry: registryPda,
       payer: provider.wallet.publicKey,
     });
-
     const tx = new anchor.web3.Transaction().add(initializeRegistryIx);
     await provider.sendAndConfirm(tx);
   });
 
   it("InitializeNewWorld", async () => {
-    const registryPda = FindWorldRegistryPda(worldProgram.programId);
-
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-    const res = await worldProgram.methods
-      .initializeNewWorld()
-      .accounts({
-        world: worldPda,
-        registry: registryPda,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
-    console.log(res);
+    const initializeNewWorld = await InitializeNewWorld({
+      payer: provider.wallet.publicKey,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(initializeNewWorld.transaction);
+    worldPda = initializeNewWorld.worldPda; // Saved for later
   });
 
   it("InitializeNewWorld 2", async () => {
-    const registryPda = FindWorldRegistryPda(worldProgram.programId);
-
-    const worldPda = FindWorldPda(new BN(1), worldProgram.programId);
-    await worldProgram.methods
-      .initializeNewWorld()
-      .accounts({
-        world: worldPda,
-        registry: registryPda,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+    const initializeNewWorld = await InitializeNewWorld({
+      payer: provider.wallet.publicKey,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(initializeNewWorld.transaction);
   });
 
   it("Add entity 1", async () => {
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-    entity1 = FindEntityPda(new BN(0), new BN(0), null, worldProgram.programId);
-    await worldProgram.methods
-      .addEntity(null)
-      .accounts({
-        world: worldPda,
-        entity: entity1,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+    const addEntity = await AddEntity({
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(addEntity.transaction);
+    entity1Pda = addEntity.entityPda; // Saved for later
   });
 
   it("Add entity 2", async () => {
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-
-    entity2 = FindEntityPda(new BN(0), new BN(1), null, worldProgram.programId);
-    await worldProgram.methods
-      .addEntity(null)
-      .accounts({
-        world: worldPda,
-        entity: entity2,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+    const addEntity = await AddEntity({
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(addEntity.transaction);
+    entity2Pda = addEntity.entityPda; // Saved for later
   });
 
   it("Add entity 3", async () => {
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-
-    const entityPda = FindEntityPda(
-      new BN(0),
-      new BN(2),
-      null,
-      worldProgram.programId
-    );
-    await worldProgram.methods
-      .addEntity(null)
-      .accounts({
-        world: worldPda,
-        entity: entityPda,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+    const addEntity = await AddEntity({
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(addEntity.transaction);
   });
 
-  it("Add entity 4 with extra seeds", async () => {
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-    const seed = "extra-seed";
-    const entity4 = FindEntityPda(
-      new BN(0),
-      new BN(3),
-      seed,
-      worldProgram.programId
-    );
-
-    await worldProgram.methods
-      .addEntity(seed)
-      .accounts({
-        world: worldPda,
-        entity: entity4,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+  it("Add entity 4 (with seed)", async () => {
+    const addEntity = await AddEntity({
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      seed: "extra-seed",
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(addEntity.transaction);
+    entity4Pda = addEntity.entityPda;
   });
 
   it("Add entity 5", async () => {
-    const worldPda = FindWorldPda(new BN(0), worldProgram.programId);
-    entity5 = FindEntityPda(new BN(0), new BN(4), null, worldProgram.programId);
-
-    await worldProgram.methods
-      .addEntity(null)
-      .accounts({
-        world: worldPda,
-        entity: entity5,
-        payer: provider.wallet.publicKey,
-      })
-      .rpc();
+    const addEntity = await AddEntity({
+      payer: provider.wallet.publicKey,
+      world: worldPda,
+      connection: provider.connection,
+    });
+    await provider.sendAndConfirm(addEntity.transaction);
+    entity5Pda = addEntity.entityPda; // Saved for later
   });
 
   it("Initialize Original Component on Entity 1, trough the world instance", async () => {
-    const componentEntity1 = FindComponentPda(
-      boltComponentProgramOrigin.programId,
-      entity1,
-      "origin-component"
-    );
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentEntity1,
-        componentProgram: boltComponentProgramOrigin.programId,
-        entity: entity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity1Pda,
+      seed: "origin-component",
+      componentId: boltComponentProgram.programId,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
   });
 
   it("Initialize Original Component on Entity 2, trough the world instance", async () => {
-    const componentEntity2 = FindComponentPda(
-      boltComponentProgramOrigin.programId,
-      entity2,
-      "origin-component"
-    );
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentEntity2,
-        componentProgram: boltComponentProgramOrigin.programId,
-        entity: entity2,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity2Pda,
+      seed: "origin-component",
+      componentId: boltComponentProgram.programId,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
   });
 
   it("Initialize Position Component on Entity 1", async () => {
-    componentPositionEntity1 = FindComponentPda(
-      boltComponentPositionProgram.programId,
-      entity1
-    );
-
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentPositionEntity1,
-        componentProgram: boltComponentPositionProgram.programId,
-        entity: entity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: worldProgram.programId,
-      })
-      .rpc();
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity1Pda,
+      componentId: exampleComponentPosition.programId,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
+    componentPositionEntity1Pda = initializeComponent.componentPda; // Saved for later
   });
 
-  it("Initialize Velocity Component on Entity 1", async () => {
-    componentVelocityEntity1 = FindComponentPda(
-      boltComponentVelocityProgram.programId,
-      entity1,
-      "component-velocity"
-    );
-
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentVelocityEntity1,
-        componentProgram: boltComponentVelocityProgram.programId,
-        entity: entity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: worldProgram.programId,
-      })
-      .rpc();
+  it("Initialize Velocity Component on Entity 1 (with seed)", async () => {
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity1Pda,
+      componentId: exampleComponentVelocity.programId,
+      seed: "component-velocity",
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
+    componentVelocityEntity1Pda = initializeComponent.componentPda; // Saved for later
   });
 
   it("Initialize Position Component on Entity 2", async () => {
-    componentPositionEntity2 = FindComponentPda(
-      boltComponentPositionProgram.programId,
-      entity2
-    );
-
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentPositionEntity2,
-        componentProgram: boltComponentPositionProgram.programId,
-        entity: entity2,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: worldProgram.programId,
-      })
-      .rpc();
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity2Pda,
+      componentId: exampleComponentPosition.programId,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
   });
 
-  it("Initialize Position Component on Entity 5", async () => {
-    componentPositionEntity5 = FindComponentPda(
-      boltComponentPositionProgram.programId,
-      entity5
-    );
+  it("Initialize Position Component on Entity 4", async () => {
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity4Pda,
+      componentId: exampleComponentPosition.programId,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
+    componentPositionEntity4Pda = initializeComponent.componentPda; // Saved for later
+  });
 
-    await worldProgram.methods
-      .initializeComponent()
-      .accounts({
-        payer: provider.wallet.publicKey,
-        data: componentPositionEntity5,
-        componentProgram: boltComponentPositionProgram.programId,
-        entity: entity5,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
+  it("Initialize Position Component on Entity 5 (with authority)", async () => {
+    const initializeComponent = await InitializeComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity5Pda,
+      componentId: exampleComponentPosition.programId,
+      authority: provider.wallet.publicKey,
+    });
+    await provider.sendAndConfirm(initializeComponent.transaction);
+    componentPositionEntity5Pda = initializeComponent.componentPda; // Saved for later
   });
 
   it("Check Position on Entity 1 is default", async () => {
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).x.toNumber()
-    ).to.equal(0);
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).y.toNumber()
-    ).to.equal(0);
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).z.toNumber()
-    ).to.equal(0);
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Default State: Entity 1", position);
+    expect(position.x.toNumber()).to.equal(0);
+    expect(position.y.toNumber()).to.equal(0);
+    expect(position.z.toNumber()).to.equal(0);
   });
 
-  it("Simple Movement System and Up direction on Entity 1", async () => {
-    const args = {
-      direction: Direction.Up,
-    };
-    await worldProgram.methods
-      .apply(serializeArgs(args)) // Move Up
-      .accounts({
-        componentProgram: boltComponentPositionProgram.programId,
-        boltSystem: systemSimpleMovement,
-        boltComponent: componentPositionEntity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
-
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).y.toNumber()
-    ).to.equal(1);
-
-    const componentData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity1
-      );
-    const x = componentData.x.toNumber();
-    const y = componentData.y.toNumber();
-    const z = componentData.z.toNumber();
-    console.log("+-----------------------------+");
-    console.log("| Movement System:   Entity 1 |");
-    console.log("+----------------+------------+");
-    console.log("| Coordinate    | Value      |");
-    console.log("+----------------+------------+");
-    console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-    console.log("+----------------+------------+");
-    console.log("|                             |");
-    console.log("+-----------------------------+");
-    console.log("Component Position: ", componentPositionEntity1.toString());
+  it("Apply Simple Movement System (Up) on Entity 1", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemSimpleMovement,
+      entities: [
+        {
+          entity: entity1Pda,
+          components: [{ componentId: exampleComponentPosition.programId }],
+        },
+      ],
+      args: {
+        direction: Direction.Up,
+      },
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
+
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Movement System: Entity 1", position);
+    expect(position.x.toNumber()).to.equal(0);
+    expect(position.y.toNumber()).to.equal(1);
+    expect(position.z.toNumber()).to.equal(0);
   });
 
-  it("Simple Movement System and Right direction on Entity 1", async () => {
-    const args = {
-      direction: Direction.Right,
-    };
-    await worldProgram.methods
-      .apply(serializeArgs(args)) // Move Right
-      .accounts({
-        componentProgram: boltComponentPositionProgram.programId,
-        boltSystem: systemSimpleMovement,
-        boltComponent: componentPositionEntity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
-
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).y.toNumber()
-    ).to.equal(1);
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).y.toNumber()
-    ).to.equal(1);
-
-    const componentData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity1
-      );
-    const x = componentData.x.toNumber();
-    const y = componentData.y.toNumber();
-    const z = componentData.z.toNumber();
-    console.log("+-----------------------------+");
-    console.log("| Movement System:   Entity 1 |");
-    console.log("+----------------+------------+");
-    console.log("| Coordinate    | Value      |");
-    console.log("+----------------+------------+");
-    console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-    console.log("+----------------+------------+");
-    console.log("|                             |");
-    console.log("+-----------------------------+");
+  it("Apply Simple Movement System (Right) on Entity 1", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemSimpleMovement,
+      entities: [
+        {
+          entity: entity1Pda,
+          components: [{ componentId: exampleComponentPosition.programId }],
+        },
+      ],
+      args: {
+        direction: Direction.Right,
+      },
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
+
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Movement System: Entity 1", position);
+    expect(position.x.toNumber()).to.equal(1);
+    expect(position.y.toNumber()).to.equal(1);
+    expect(position.z.toNumber()).to.equal(0);
   });
 
-  it("Fly System on Entity 1", async () => {
-    await worldProgram.methods
-      .apply(Buffer.alloc(0)) // Move Up
-      .accounts({
-        componentProgram: boltComponentPositionProgram.programId,
-        boltSystem: systemFly,
-        boltComponent: componentPositionEntity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
-
-    expect(
-      (
-        await boltComponentPositionProgram.account.position.fetch(
-          componentPositionEntity1
-        )
-      ).z.toNumber()
-    ).to.equal(1);
-
-    const componentData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity1
-      );
-    const x = componentData.x.toNumber();
-    const y = componentData.y.toNumber();
-    const z = componentData.z.toNumber();
-    console.log("+-----------------------------+");
-    console.log("| Fly: Position Entity 1      |");
-    console.log("+----------------+------------+");
-    console.log("| Coordinate    | Value      |");
-    console.log("+----------------+------------+");
-    console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-    console.log("+----------------+------------+");
-    console.log("|                             |");
-    console.log("+-----------------------------+");
+  it("Apply Fly System on Entity 1", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemFly,
+      entities: [
+        {
+          entity: entity1Pda,
+          components: [{ componentId: exampleComponentPosition.programId }],
+        },
+      ],
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
+
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Fly System: Entity 1", position);
+    expect(position.x.toNumber()).to.equal(1);
+    expect(position.y.toNumber()).to.equal(1);
+    expect(position.z.toNumber()).to.equal(1);
   });
 
-  it("Apply Velocity on Entity 1", async () => {
-    await worldProgram.methods
-      .apply2(Buffer.alloc(0))
-      .accounts({
-        componentProgram1: boltComponentVelocityProgram.programId,
-        componentProgram2: boltComponentPositionProgram.programId,
-        boltSystem: applyVelocity,
-        boltComponent1: componentVelocityEntity1,
-        boltComponent2: componentPositionEntity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .rpc();
-
-    const componentData =
-      await boltComponentVelocityProgram.account.velocity.fetch(
-        componentVelocityEntity1
-      );
-    let x = componentData.x.toNumber();
-    let y = componentData.y.toNumber();
-    let z = componentData.z.toNumber();
-    const tmp = componentData.lastApplied.toNumber();
-    console.log("+-----------------------------+");
-    console.log("| Apply Velocity: Velocity Entity 1      |");
-    console.log("+----------------+------------+");
-    console.log("| Coordinate    | Value      |");
-    console.log("+----------------+------------+");
-    console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Timestamp    | ${String(tmp).padEnd(10, " ")} |`);
-    console.log("+----------------+------------+");
-    console.log("|                             |");
-    console.log("+-----------------------------+");
-
-    const positionData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity1
-      );
-    x = positionData.x.toNumber();
-    y = positionData.y.toNumber();
-    z = positionData.z.toNumber();
-    console.log("+-----------------------------+");
-    console.log("| Apply Velocity: Position Entity 1      |");
-    console.log("+----------------+------------+");
-    console.log("| Coordinate    | Value      |");
-    console.log("+----------------+------------+");
-    console.log(`| X Position    | ${String(x).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Y Position    | ${String(y).padEnd(10, " ")} |`);
-    console.log("|               |            |");
-    console.log(`| Z Position    | ${String(z).padEnd(10, " ")} |`);
-    console.log("+----------------+------------+");
-    console.log("|                             |");
-    console.log("+-----------------------------+");
-    expect(positionData.z.toNumber()).to.not.equal(300);
+  it("Apply System Velocity on Entity 1", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemApplyVelocity,
+      entities: [
+        {
+          entity: entity1Pda,
+          components: [
+            {
+              componentId: exampleComponentVelocity.programId,
+              seed: "component-velocity",
+            },
+            { componentId: exampleComponentPosition.programId },
+          ],
+        },
+      ],
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
+
+    const velocity = await exampleComponentVelocity.account.velocity.fetch(
+      componentVelocityEntity1Pda
+    );
+    logVelocity("Apply System Velocity: Entity 1", velocity);
+    expect(velocity.x.toNumber()).to.equal(10);
+    expect(velocity.y.toNumber()).to.equal(0);
+    expect(velocity.z.toNumber()).to.equal(0);
+    expect(velocity.lastApplied.toNumber()).to.not.equal(0);
+
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Apply System Velocity: Entity 1", position);
+    expect(position.x.toNumber()).to.greaterThan(1);
+    expect(position.y.toNumber()).to.equal(1);
+    expect(position.z.toNumber()).to.equal(1);
   });
 
-  it("Apply Velocity on Entity 1, with Clock external account", async () => {
-    await worldProgram.methods
-      .apply2(Buffer.alloc(0))
-      .accounts({
-        componentProgram1: boltComponentVelocityProgram.programId,
-        componentProgram2: boltComponentPositionProgram.programId,
-        boltSystem: applyVelocity,
-        boltComponent1: componentVelocityEntity1,
-        boltComponent2: componentPositionEntity1,
-        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-        authority: provider.wallet.publicKey,
-      })
-      .remainingAccounts([
+  it("Apply System Velocity on Entity 1, with Clock external account", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemApplyVelocity,
+      entities: [
+        {
+          entity: entity1Pda,
+          components: [
+            {
+              componentId: exampleComponentVelocity.programId,
+              seed: "component-velocity",
+            },
+            { componentId: exampleComponentPosition.programId },
+          ],
+        },
+      ],
+      extraAccounts: [
         {
           pubkey: new web3.PublicKey(
             "SysvarC1ock11111111111111111111111111111111"
@@ -549,127 +389,144 @@ describe("bolt", () => {
           isWritable: false,
           isSigner: false,
         },
-      ])
-      .rpc();
+      ],
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
 
-    const positionData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity1
-      );
-    // Check if the position has changed to 300 (which means the account clock was used)
-    expect(positionData.z.toNumber()).to.equal(300);
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity1Pda
+    );
+    logPosition("Apply System Velocity: Entity 1", position);
+    expect(position.x.toNumber()).to.greaterThan(1);
+    expect(position.y.toNumber()).to.equal(1);
+    expect(position.z.toNumber()).to.equal(300);
   });
 
-  // Check illegal authority usage
-  it("Check invalid component update", async () => {
-    const componentDataPrev =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity5
+  it("Apply Fly System on Entity 4", async () => {
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemFly,
+      entities: [
+        {
+          entity: entity4Pda,
+          components: [{ componentId: exampleComponentPosition.programId }],
+        },
+      ],
+    });
+    await provider.sendAndConfirm(applySystem.transaction);
+
+    const position = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity4Pda
+    );
+    logPosition("Fly System: Entity 4", position);
+    expect(position.x.toNumber()).to.equal(0);
+    expect(position.y.toNumber()).to.equal(0);
+    expect(position.z.toNumber()).to.equal(1);
+  });
+
+  it("Apply Fly System on Entity 5 (should fail with wrong authority)", async () => {
+    const positionBefore =
+      await exampleComponentPosition.account.position.fetch(
+        componentPositionEntity5Pda
       );
 
+    const applySystem = await ApplySystem({
+      authority: provider.wallet.publicKey,
+      systemId: exampleSystemFly,
+      entities: [
+        {
+          entity: entity5Pda,
+          components: [{ componentId: exampleComponentPosition.programId }],
+        },
+      ],
+    });
+
+    let failed = false;
     try {
-      await worldProgram.methods
-        .apply(Buffer.alloc(0)) // Move Up
-        .accounts({
-          componentProgram: boltComponentPositionProgram.programId,
-          boltSystem: systemFly,
-          boltComponent: componentPositionEntity5,
-          instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-          authority: provider.wallet.publicKey,
-        })
-        .rpc();
-    } catch (e) {
-      expect(e.message).to.contain("Invalid authority");
+      await provider.sendAndConfirm(applySystem.transaction);
+    } catch (error) {
+      failed = true;
+      //console.log("error", error);
+      expect(error.logs.join("\n")).to.contain("Error Code: InvalidAuthority");
     }
+    expect(failed).to.equal(true);
 
-    const componentData =
-      await boltComponentPositionProgram.account.position.fetch(
-        componentPositionEntity5
-      );
+    const positionAfter = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity5Pda
+    );
 
-    expect(
-      componentDataPrev.x.toNumber() === componentData.x.toNumber() &&
-        componentDataPrev.y.toNumber() === componentData.y.toNumber() &&
-        componentDataPrev.z.toNumber() === componentData.z.toNumber()
-    ).to.equal(true);
+    expect(positionBefore.x.toNumber()).to.equal(positionAfter.x.toNumber());
+    expect(positionBefore.y.toNumber()).to.equal(positionAfter.y.toNumber());
+    expect(positionBefore.z.toNumber()).to.equal(positionAfter.z.toNumber());
   });
 
-  // Check illegal call, without CPI
-  it("Check invalid init without CPI", async () => {
+  it("Check invalid component init without CPI", async () => {
     let invalid = false;
-    const componentVelocityEntity5 = FindComponentPda(
-      boltComponentVelocityProgram.programId,
-      entity5
-    );
     try {
-      await boltComponentProgramOrigin.methods
+      await exampleComponentPosition.methods
         .initialize()
         .accounts({
           payer: provider.wallet.publicKey,
-          data: componentVelocityEntity5,
-          entity: entity5,
-          instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-          systemProgram: anchor.web3.SystemProgram.programId,
+          data: componentPositionEntity5Pda,
+          entity: entity5Pda,
           authority: provider.wallet.publicKey,
         })
         .rpc();
-    } catch (e) {
+    } catch (error) {
+      //console.log("error", error);
+      expect(error.message).to.contain("Error Code: InvalidCaller");
       invalid = true;
     }
     expect(invalid).to.equal(true);
   });
 
-  // Check illegal call, without CPI
-  it("Check invalid update without CPI", async () => {
+  it("Check invalid component update without CPI", async () => {
     let invalid = false;
-    const componentVelocityEntity5 = FindComponentPda(
-      boltComponentVelocityProgram.programId,
-      entity5
-    );
     try {
-      await boltComponentProgramOrigin.methods
-        .update(null)
+      await boltComponentProgram.methods
+        .update(Buffer.from(""))
         .accounts({
-          boltComponent: componentVelocityEntity5,
-          instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
+          boltComponent: componentPositionEntity4Pda,
           authority: provider.wallet.publicKey,
         })
         .rpc();
-    } catch (e) {
+    } catch (error) {
+      //console.log("error", error);
+      expect(error.message).to.contain(
+        "bolt_component. Error Code: AccountOwnedByWrongProgram"
+      );
       invalid = true;
     }
     expect(invalid).to.equal(true);
   });
 
-  // Check component delegation
   it("Check component delegation", async () => {
     const delegateIx = createDelegateInstruction({
-      entity: entity1,
-      account: componentPositionEntity1,
-      ownerProgram: boltComponentPositionProgram.programId,
+      entity: entity1Pda,
+      account: componentPositionEntity1Pda,
+      ownerProgram: exampleComponentPosition.programId,
       payer: provider.wallet.publicKey,
     });
     const tx = new anchor.web3.Transaction().add(delegateIx);
-    await provider.sendAndConfirm(tx, [], { skipPreflight: true });
+    await provider.sendAndConfirm(tx);
     const acc = await provider.connection.getAccountInfo(
-      componentPositionEntity1
+      componentPositionEntity1Pda
     );
     expect(acc.owner.toString()).to.equal(DELEGATION_PROGRAM_ID);
   });
 
-  // Check component undelegation
   it("Check component undelegation", async () => {
     const delegateIx = createUndelegateInstruction({
       payer: provider.wallet.publicKey,
-      delegatedAccount: componentPositionEntity1,
-      ownerProgram: boltComponentPositionProgram.programId,
+      delegatedAccount: componentPositionEntity1Pda,
+      ownerProgram: exampleComponentPosition.programId,
       reimbursement: provider.wallet.publicKey,
     });
     const tx = new anchor.web3.Transaction().add(delegateIx);
-    await provider.sendAndConfirm(tx, [], { skipPreflight: true });
+    await provider.sendAndConfirm(tx);
     const acc = await provider.connection.getAccountInfo(
-      componentPositionEntity1
+      componentPositionEntity1Pda
     );
-    expect(acc.owner).to.deep.equal(boltComponentPositionProgram.programId);
+    expect(acc.owner).to.deep.equal(exampleComponentPosition.programId);
   });
 });