浏览代码

:sparkles: Adding low and intermediate api-levels (#120)

Danilo Guanabara 9 月之前
父节点
当前提交
512d27ba89

+ 8 - 1
Anchor.toml

@@ -36,7 +36,14 @@ members = [
 ]
 
 [scripts]
-test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/bolt.ts"
+test = """
+echo "Waiting for 2 seconds..."
+sleep 2
+echo "Running low level API tests..."
+yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/bolt.low-level.api.ts
+echo "Running intermediate level API tests..."
+yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/bolt.intermediate-level.api.ts
+"""
 
 [test]
 startup_wait = 5000

+ 1 - 1
clients/bolt-sdk/src/generated/idl/world.json

@@ -2,7 +2,7 @@
   "address": "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n",
   "metadata": {
     "name": "world",
-    "version": "0.1.12",
+    "version": "0.2.0",
     "spec": "0.1.0",
     "description": "Bolt World program",
     "repository": "https://github.com/magicblock-labs/bolt"

+ 1 - 0
clients/bolt-sdk/src/generated/types/index.ts

@@ -0,0 +1 @@
+export * from "./world";

+ 1 - 1
clients/bolt-sdk/src/generated/types/world.ts

@@ -8,7 +8,7 @@ export type World = {
   address: "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n";
   metadata: {
     name: "world";
-    version: "0.1.12";
+    version: "0.2.0";
     spec: "0.1.0";
     description: "Bolt World program";
     repository: "https://github.com/magicblock-labs/bolt";

+ 10 - 4
clients/bolt-sdk/src/index.ts

@@ -1,6 +1,7 @@
 import { PublicKey } from "@solana/web3.js";
 import type BN from "bn.js";
-import { PROGRAM_ID } from "./generated";
+import { PROGRAM_ID as WORLD_PROGRAM_ID } from "./generated";
+import { World as WORLD_PROGRAM_IDL } from "./generated/types";
 export * from "./generated/accounts";
 export * from "./generated/instructions";
 export * from "./world/transactions";
@@ -12,6 +13,7 @@ export { DELEGATION_PROGRAM_ID } from "@magicblock-labs/ephemeral-rollups-sdk";
 import * as anchor from "@coral-xyz/anchor";
 export { anchor };
 export { Provider, Program, Wallet, web3, workspace } from "@coral-xyz/anchor";
+export { WORLD_PROGRAM_ID, WORLD_PROGRAM_IDL };
 
 export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
   "Sysvar1nstructions1111111111111111111111111",
@@ -20,7 +22,7 @@ export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
 export function FindRegistryPda({ programId }: { programId?: PublicKey }) {
   return PublicKey.findProgramAddressSync(
     [Buffer.from("registry")],
-    programId ?? PROGRAM_ID,
+    programId ?? WORLD_PROGRAM_ID,
   )[0];
 }
 
@@ -34,7 +36,7 @@ export function FindWorldPda({
   const idBuffer = Buffer.from(worldId.toArrayLike(Buffer, "be", 8));
   return PublicKey.findProgramAddressSync(
     [Buffer.from("world"), idBuffer],
-    programId ?? PROGRAM_ID,
+    programId ?? WORLD_PROGRAM_ID,
   )[0];
 }
 
@@ -60,9 +62,13 @@ export function FindEntityPda({
   } else {
     throw new Error("An entity must have either an Id or a Seed");
   }
-  return PublicKey.findProgramAddressSync(seeds, programId ?? PROGRAM_ID)[0];
+  return PublicKey.findProgramAddressSync(
+    seeds,
+    programId ?? WORLD_PROGRAM_ID,
+  )[0];
 }
 
+// TODO: seed must be Uint8Array like the other FindPda functions
 export function FindComponentPda({
   componentId,
   entity,

+ 24 - 43
clients/bolt-sdk/src/world/transactions.ts

@@ -21,11 +21,34 @@ import {
   type TransactionInstruction,
 } from "@solana/web3.js";
 import type WorldProgram from "../generated";
-import { PROGRAM_ID, worldIdl } from "../generated";
+import {
+  createInitializeRegistryInstruction,
+  PROGRAM_ID,
+  worldIdl,
+} from "../generated";
 import { type Idl, Program } from "@coral-xyz/anchor";
 
 const MAX_COMPONENTS = 5;
 
+export async function InitializeRegistry({
+  payer,
+  connection,
+}: {
+  payer: PublicKey;
+  connection: Connection;
+}): Promise<{
+  instruction: TransactionInstruction;
+  transaction: Transaction;
+}> {
+  const registry = FindRegistryPda({});
+  const instruction = createInitializeRegistryInstruction({ registry, payer });
+  const transaction = new Transaction().add(instruction);
+  return {
+    instruction,
+    transaction,
+  };
+}
+
 /**
  * Create the transaction to Initialize a new world
  * @param payer
@@ -311,47 +334,6 @@ export async function InitializeComponent({
   };
 }
 
-export async function Apply({
-  authority,
-  boltSystem,
-  boltComponent,
-  componentProgram,
-  anchorRemainingAccounts,
-  world,
-  args,
-}: {
-  authority: PublicKey;
-  boltSystem: PublicKey;
-  boltComponent: PublicKey;
-  componentProgram: PublicKey;
-  world: PublicKey;
-  anchorRemainingAccounts?: web3.AccountMeta[];
-  args: Uint8Array;
-}): Promise<{
-  instruction: TransactionInstruction;
-  transaction: Transaction;
-}> {
-  const instruction = createApplyInstruction(
-    {
-      authority,
-      boltSystem,
-      boltComponent,
-      componentProgram,
-      instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
-      anchorRemainingAccounts,
-      world,
-    },
-    {
-      args,
-    },
-  );
-  const transaction = new Transaction().add(instruction);
-  return {
-    instruction,
-    transaction,
-  };
-}
-
 interface ApplySystemInstruction {
   authority: PublicKey;
   systemId: PublicKey;
@@ -398,7 +380,6 @@ async function createApplySystemInstruction({
   const applyAccounts = {
     authority: authority ?? PROGRAM_ID,
     boltSystem: systemId,
-    instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
     world,
   };
 

+ 2 - 2
crates/programs/bolt-component/Cargo.toml

@@ -24,5 +24,5 @@ custom-heap = []
 custom-panic = []
 
 [dependencies]
-anchor-lang = { workspace = true }
-bolt-system = { workspace = true, cpi = true }
+anchor-lang.workspace = true
+bolt-system.workspace = true

+ 2 - 2
crates/programs/bolt-system/Cargo.toml

@@ -24,5 +24,5 @@ custom-heap = []
 custom-panic = []
 
 [dependencies]
-anchor-lang = { workspace = true }
-bolt-helpers-system-template = { workspace = true, cpi = true }
+anchor-lang.workspace = true
+bolt-helpers-system-template.workspace = true

+ 6 - 6
crates/programs/world/Cargo.toml

@@ -24,10 +24,10 @@ custom-heap = []
 custom-panic = []
 
 [dependencies]
-anchor-lang = { workspace = true }
-bolt-component = { workspace = true, cpi = true }
-bolt-helpers-world-apply = { workspace = true }
-bolt-system = { workspace = true, cpi = true }
-solana-security-txt = { workspace = true }
-tuple-conv = { workspace = true }
+anchor-lang.workspace = true
+bolt-component.workspace = true
+bolt-helpers-world-apply.workspace = true
+bolt-system.workspace = true
+solana-security-txt.workspace = true
+tuple-conv.workspace = true
 

+ 11 - 12
tests/bolt.ts → tests/bolt.intermediate-level.api.ts

@@ -3,16 +3,14 @@ import { type Position } from "../target/types/position";
 import { type Velocity } from "../target/types/velocity";
 import { type BoltComponent } from "../target/types/bolt_component";
 import { type SystemSimpleMovement } from "../target/types/system_simple_movement";
-import { type World } from "../target/types/world";
 import { type SystemFly } from "../target/types/system_fly";
 import { type SystemApplyVelocity } from "../target/types/system_apply_velocity";
 import { expect } from "chai";
 import type BN from "bn.js";
 import {
   AddEntity,
-  createInitializeRegistryInstruction,
   DELEGATION_PROGRAM_ID,
-  FindRegistryPda,
+  InitializeRegistry,
   InitializeComponent,
   InitializeNewWorld,
   ApplySystem,
@@ -24,6 +22,7 @@ import {
   type Program,
   anchor,
   web3,
+  WORLD_PROGRAM_IDL as World,
 } from "../clients/bolt-sdk";
 
 enum Direction {
@@ -106,13 +105,15 @@ describe("bolt", () => {
   const secondAuthority = Keypair.generate().publicKey;
 
   it("InitializeRegistry", async () => {
-    const registryPda = FindRegistryPda({});
-    const initializeRegistryIx = createInitializeRegistryInstruction({
-      registry: registryPda,
+    const initializeRegistry = await InitializeRegistry({
       payer: provider.wallet.publicKey,
+      connection: provider.connection,
     });
-    const tx = new anchor.web3.Transaction().add(initializeRegistryIx);
-    await provider.sendAndConfirm(tx);
+    try {
+      await provider.sendAndConfirm(initializeRegistry.transaction);
+    } catch (error) {
+      // This is expected to fail because the registry already exists if another api level test ran before
+    }
   });
 
   it("InitializeNewWorld", async () => {
@@ -216,7 +217,7 @@ describe("bolt", () => {
     const addEntity = await AddEntity({
       payer: provider.wallet.publicKey,
       world: worldPda,
-      seed: Buffer.from("extra-seed"),
+      seed: Buffer.from("custom-seed"),
       connection: provider.connection,
     });
     await provider.sendAndConfirm(addEntity.transaction);
@@ -325,9 +326,7 @@ describe("bolt", () => {
         },
       ],
       world: worldPda,
-      args: {
-        direction: Direction.Up,
-      },
+      args: { direction: Direction.Up },
     });
 
     await provider.sendAndConfirm(apply.transaction);

+ 870 - 0
tests/bolt.low-level.api.ts

@@ -0,0 +1,870 @@
+import { Keypair, type PublicKey } from "@solana/web3.js";
+import { type Position } from "../target/types/position";
+import { type Velocity } from "../target/types/velocity";
+import { type BoltComponent } from "../target/types/bolt_component";
+import { type SystemSimpleMovement } from "../target/types/system_simple_movement";
+import { type SystemFly } from "../target/types/system_fly";
+import { type SystemApplyVelocity } from "../target/types/system_apply_velocity";
+import { expect } from "chai";
+import BN from "bn.js";
+import {
+  DELEGATION_PROGRAM_ID,
+  DelegateComponent,
+  type Program,
+  anchor,
+  web3,
+  FindRegistryPda,
+  FindWorldPda,
+  FindEntityPda,
+  FindComponentPda,
+  SerializeArgs,
+  WORLD_PROGRAM_IDL as World,
+} from "../clients/bolt-sdk";
+
+enum Direction {
+  Left = "Left",
+  Right = "Right",
+  Up = "Up",
+  Down = "Down",
+}
+
+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 boltComponentProgram = anchor.workspace
+    .BoltComponent as Program<BoltComponent>;
+
+  const exampleComponentPosition = anchor.workspace
+    .Position as Program<Position>;
+  const exampleComponentVelocity = anchor.workspace
+    .Velocity as Program<Velocity>;
+
+  const exampleSystemSimpleMovement = (
+    anchor.workspace.SystemSimpleMovement as Program<SystemSimpleMovement>
+  ).programId;
+  const exampleSystemFly = (anchor.workspace.SystemFly as Program<SystemFly>)
+    .programId;
+  const exampleSystemApplyVelocity = (
+    anchor.workspace.SystemApplyVelocity as Program<SystemApplyVelocity>
+  ).programId;
+
+  let worldPda: PublicKey;
+  let worldId: BN;
+
+  let entity1Pda: PublicKey;
+  let entity2Pda: PublicKey;
+  let entity4Pda: PublicKey;
+  let entity5Pda: PublicKey;
+
+  let componentPositionEntity1Pda: PublicKey;
+  let componentVelocityEntity1Pda: PublicKey;
+
+  let componentPositionEntity4Pda: PublicKey;
+  let componentPositionEntity5Pda: PublicKey;
+
+  const secondAuthority = Keypair.generate().publicKey;
+
+  it("InitializeRegistry", async () => {
+    const registryPda = FindRegistryPda({});
+    const instruction = await worldProgram.methods
+      .initializeRegistry()
+      .accounts({
+        registry: registryPda,
+        payer: provider.wallet.publicKey,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    await provider.sendAndConfirm(transaction);
+  });
+
+  it("InitializeNewWorld", async () => {
+    const registryPda = FindRegistryPda({});
+    const registry = await worldProgram.account.registry.fetch(registryPda);
+    worldId = new BN(registry.worlds);
+    worldPda = FindWorldPda({ worldId });
+    const instruction = await worldProgram.methods
+      .initializeNewWorld()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        registry: registryPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("InitializeNewWorld signature: ", signature);
+  });
+
+  it("Add authority", async () => {
+    const instruction = await worldProgram.methods
+      .addAuthority(worldId)
+      .accounts({
+        authority: provider.wallet.publicKey,
+        newAuthority: provider.wallet.publicKey,
+        world: worldPda,
+      })
+      .instruction();
+
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    await provider.sendAndConfirm(transaction, [], { skipPreflight: true });
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(
+      worldAccount.authorities.some((auth) =>
+        auth.equals(provider.wallet.publicKey),
+      ),
+    );
+  });
+
+  it("Add a second authority", async () => {
+    const instruction = await worldProgram.methods
+      .addAuthority(worldId)
+      .accounts({
+        authority: provider.wallet.publicKey,
+        newAuthority: secondAuthority,
+        world: worldPda,
+      })
+      .instruction();
+
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(`Add Authority signature: ${signature}`);
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(
+      worldAccount.authorities.some((auth) => auth.equals(secondAuthority)),
+    );
+  });
+
+  it("Remove an authority", async () => {
+    const instruction = await worldProgram.methods
+      .removeAuthority(worldId)
+      .accounts({
+        authority: provider.wallet.publicKey,
+        authorityToDelete: secondAuthority,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(`Remove Authority signature: ${signature}`);
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(
+      !worldAccount.authorities.some((auth) => auth.equals(secondAuthority)),
+    );
+  });
+
+  it("InitializeNewWorld 2", async () => {
+    const registryPda = FindRegistryPda({});
+    const registry = await worldProgram.account.registry.fetch(registryPda);
+    const worldId = new BN(registry.worlds);
+    const worldPda = FindWorldPda({ worldId });
+    const instruction = await worldProgram.methods
+      .initializeNewWorld()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        registry: registryPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("InitializeNewWorld 2 signature: ", signature);
+  });
+
+  it("Add entity 1", async () => {
+    const world = await worldProgram.account.world.fetch(worldPda);
+    entity1Pda = FindEntityPda({ worldId: world.id, entityId: world.entities });
+    const instruction = await worldProgram.methods
+      .addEntity(null)
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        entity: entity1Pda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Add Entity 1 signature: ", signature);
+  });
+
+  it("Add entity 2", async () => {
+    const world = await worldProgram.account.world.fetch(worldPda);
+    entity2Pda = FindEntityPda({ worldId: world.id, entityId: world.entities });
+    const instruction = await worldProgram.methods
+      .addEntity(null)
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        entity: entity2Pda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Add Entity 2 signature: ", signature);
+  });
+
+  it("Add entity 3", async () => {
+    const world = await worldProgram.account.world.fetch(worldPda);
+    const entity3Pda = FindEntityPda({
+      worldId: world.id,
+      entityId: world.entities,
+    });
+    const instruction = await worldProgram.methods
+      .addEntity(null)
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        entity: entity3Pda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Add Entity 3 signature: ", signature);
+  });
+
+  it("Add entity 4 (with seed)", async () => {
+    const world = await worldProgram.account.world.fetch(worldPda);
+    const seed = Buffer.from("custom-seed");
+    entity4Pda = FindEntityPda({ worldId: world.id, seed });
+    const instruction = await worldProgram.methods
+      .addEntity(seed)
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        entity: entity4Pda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Add Entity 4 signature: ", signature);
+  });
+
+  it("Add entity 5", async () => {
+    const world = await worldProgram.account.world.fetch(worldPda);
+    entity5Pda = FindEntityPda({ worldId: world.id, entityId: world.entities });
+    const instruction = await worldProgram.methods
+      .addEntity(null)
+      .accounts({
+        payer: provider.wallet.publicKey,
+        world: worldPda,
+        entity: entity5Pda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Add Entity 5 signature: ", signature);
+  });
+
+  it("Initialize Original Component on Entity 1, through the world instance", async () => {
+    const componentId = boltComponentProgram.programId;
+    const componentPda = FindComponentPda({
+      componentId,
+      entity: entity1Pda,
+      seed: "origin-component",
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity1Pda,
+        data: componentPda,
+        componentProgram: componentId,
+        authority: provider.wallet.publicKey,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Original Component on Entity 1 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Original Component on Entity 2, trough the world instance", async () => {
+    const componentId = boltComponentProgram.programId;
+    const componentPda = FindComponentPda({
+      componentId,
+      entity: entity2Pda,
+      seed: "origin-component",
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity2Pda,
+        data: componentPda,
+        componentProgram: componentId,
+        authority: provider.wallet.publicKey,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Original Component on Entity 2 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Position Component on Entity 1", async () => {
+    const componentId = exampleComponentPosition.programId;
+    componentPositionEntity1Pda = FindComponentPda({
+      componentId,
+      entity: entity1Pda,
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity1Pda,
+        data: componentPositionEntity1Pda,
+        componentProgram: componentId,
+        authority: worldProgram.programId,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Position Component on Entity 1 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Velocity Component on Entity 1 (with seed)", async () => {
+    const componentId = exampleComponentVelocity.programId;
+    componentVelocityEntity1Pda = FindComponentPda({
+      componentId,
+      entity: entity1Pda,
+      seed: "component-velocity",
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity1Pda,
+        data: componentVelocityEntity1Pda,
+        componentProgram: componentId,
+        authority: worldProgram.programId,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Velocity Component on Entity 1 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Position Component on Entity 2", async () => {
+    const componentId = exampleComponentPosition.programId;
+    const componentPositionEntity2Pda = FindComponentPda({
+      componentId,
+      entity: entity2Pda,
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity2Pda,
+        data: componentPositionEntity2Pda,
+        componentProgram: componentId,
+        authority: worldProgram.programId,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Position Component on Entity 2 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Position Component on Entity 4", async () => {
+    const componentId = exampleComponentPosition.programId;
+    componentPositionEntity4Pda = FindComponentPda({
+      componentId,
+      entity: entity4Pda,
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity4Pda,
+        data: componentPositionEntity4Pda,
+        componentProgram: componentId,
+        authority: worldProgram.programId,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Position Component on Entity 4 signature: ",
+      signature,
+    );
+  });
+
+  it("Initialize Position Component on Entity 5 (with authority)", async () => {
+    const componentId = exampleComponentPosition.programId;
+    componentPositionEntity5Pda = FindComponentPda({
+      componentId,
+      entity: entity5Pda,
+    });
+    const instruction = await worldProgram.methods
+      .initializeComponent()
+      .accounts({
+        payer: provider.wallet.publicKey,
+        entity: entity5Pda,
+        data: componentPositionEntity5Pda,
+        componentProgram: componentId,
+        authority: provider.wallet.publicKey,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Initialize Position Component on Entity 5 signature: ",
+      signature,
+    );
+  });
+
+  it("Check Position on Entity 1 is default", async () => {
+    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("Apply Simple Movement System (Up) on Entity 1 using Apply", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs({ direction: Direction.Up }))
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemSimpleMovement,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Apply Simple Movement System (Up) on Entity 1 signature: ",
+      signature,
+    );
+
+    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("Apply Simple Movement System (Up) on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs({ direction: Direction.Up }))
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemSimpleMovement,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Apply Simple Movement System (Up) on Entity 1 signature: ",
+      signature,
+    );
+
+    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(2);
+    expect(position.z.toNumber()).to.equal(0);
+  });
+
+  it("Apply Simple Movement System (Right) on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs({ direction: Direction.Right }))
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemSimpleMovement,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log(
+      "Apply Simple Movement System (Right) on Entity 1 signature: ",
+      signature,
+    );
+
+    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(2);
+    expect(position.z.toNumber()).to.equal(0);
+  });
+
+  it("Apply Fly System on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemFly,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Apply Fly System on Entity 1 signature: ", signature);
+
+    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(2);
+    expect(position.z.toNumber()).to.equal(1);
+  });
+
+  it("Apply System Velocity on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply2(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemApplyVelocity,
+        boltComponent1: componentVelocityEntity1Pda,
+        componentProgram1: exampleComponentVelocity.programId,
+        boltComponent2: componentPositionEntity1Pda,
+        componentProgram2: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .remainingAccounts([])
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction);
+    console.log("Apply System Velocity on Entity 1 signature: ", signature);
+
+    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(2);
+    expect(position.z.toNumber()).to.equal(1);
+  });
+
+  it("Apply System Velocity on Entity 1, with Clock external account", async () => {
+    const instruction = await worldProgram.methods
+      .apply2(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemApplyVelocity,
+        boltComponent1: componentVelocityEntity1Pda,
+        componentProgram1: exampleComponentVelocity.programId,
+        boltComponent2: componentPositionEntity1Pda,
+        componentProgram2: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .remainingAccounts([
+        {
+          pubkey: new web3.PublicKey(
+            "SysvarC1ock11111111111111111111111111111111",
+          ),
+          isWritable: false,
+          isSigner: false,
+        },
+      ])
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    await provider.sendAndConfirm(transaction);
+
+    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(2);
+    expect(position.z.toNumber()).to.equal(300);
+  });
+
+  it("Apply Fly System on Entity 4", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemFly,
+        boltComponent: componentPositionEntity4Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    await provider.sendAndConfirm(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 instruction = await worldProgram.methods
+      .apply(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemFly,
+        boltComponent: componentPositionEntity5Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+
+    let failed = false;
+    try {
+      await provider.sendAndConfirm(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 positionAfter = await exampleComponentPosition.account.position.fetch(
+      componentPositionEntity5Pda,
+    );
+
+    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());
+  });
+
+  it("Whitelist System", async () => {
+    const instruction = await worldProgram.methods
+      .approveSystem()
+      .accounts({
+        authority: provider.wallet.publicKey,
+        system: exampleSystemFly,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction, [], {
+      skipPreflight: true,
+    });
+    console.log(`Whitelist 2 system approval signature: ${signature}`);
+
+    // Get World and check permissionless and systems
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(worldAccount.permissionless).to.equal(false);
+    expect(worldAccount.systems.length).to.be.greaterThan(0);
+  });
+
+  it("Whitelist System 2", async () => {
+    const instruction = await worldProgram.methods
+      .approveSystem()
+      .accounts({
+        authority: provider.wallet.publicKey,
+        system: exampleSystemApplyVelocity,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction, [], {
+      skipPreflight: true,
+    });
+    console.log(`Whitelist 2 system approval signature: ${signature}`);
+
+    // Get World and check permissionless and systems
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(worldAccount.permissionless).to.equal(false);
+    expect(worldAccount.systems.length).to.be.greaterThan(0);
+  });
+
+  it("Apply Fly System on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemFly,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    await provider.sendAndConfirm(transaction);
+  });
+
+  it("Remove System 1", async () => {
+    const instruction = await worldProgram.methods
+      .removeSystem()
+      .accounts({
+        authority: provider.wallet.publicKey,
+        system: exampleSystemFly,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const signature = await provider.sendAndConfirm(transaction, [], {
+      skipPreflight: true,
+    });
+    console.log(`Remove System 1 signature: ${signature}`);
+
+    // Get World and check permissionless and systems
+    const worldAccount = await worldProgram.account.world.fetch(worldPda);
+    expect(worldAccount.permissionless).to.equal(false);
+    expect(worldAccount.systems.length).to.be.greaterThan(0);
+  });
+
+  it("Apply Invalid Fly System on Entity 1", async () => {
+    const instruction = await worldProgram.methods
+      .apply(SerializeArgs())
+      .accounts({
+        authority: provider.wallet.publicKey,
+        boltSystem: exampleSystemFly,
+        boltComponent: componentPositionEntity1Pda,
+        componentProgram: exampleComponentPosition.programId,
+        world: worldPda,
+      })
+      .instruction();
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    let invalid = false;
+    try {
+      await provider.sendAndConfirm(transaction);
+    } catch (error) {
+      expect(error.logs.join(" ")).to.contain("Error Code: SystemNotApproved");
+      invalid = true;
+    }
+    expect(invalid).to.equal(true);
+  });
+
+  it("Check invalid component init without CPI", async () => {
+    let invalid = false;
+    try {
+      await exampleComponentPosition.methods
+        .initialize()
+        .accounts({
+          payer: provider.wallet.publicKey,
+          data: componentPositionEntity5Pda,
+          entity: entity5Pda,
+          authority: provider.wallet.publicKey,
+        })
+        .rpc();
+    } catch (error) {
+      // console.log("error", error);
+      expect(error.message).to.contain("Error Code: InvalidCaller");
+      invalid = true;
+    }
+    expect(invalid).to.equal(true);
+  });
+
+  it("Check invalid component update without CPI", async () => {
+    let invalid = false;
+    try {
+      await boltComponentProgram.methods
+        .update(Buffer.from(""))
+        .accounts({
+          boltComponent: componentPositionEntity4Pda,
+          authority: provider.wallet.publicKey,
+        })
+        .rpc();
+    } catch (error) {
+      // console.log("error", error);
+      expect(error.message).to.contain(
+        "bolt_component. Error Code: AccountOwnedByWrongProgram",
+      );
+      invalid = true;
+    }
+    expect(invalid).to.equal(true);
+  });
+
+  it("Check component delegation", async () => {
+    const delegateComponent = await DelegateComponent({
+      payer: provider.wallet.publicKey,
+      entity: entity1Pda,
+      componentId: exampleComponentPosition.programId,
+    });
+    const instruction = delegateComponent.transaction;
+    const transaction = new anchor.web3.Transaction().add(instruction);
+    const txSign = await provider.sendAndConfirm(transaction, [], {
+      skipPreflight: true,
+      commitment: "confirmed",
+    });
+    console.log(`Delegation signature: ${txSign}`);
+    const acc = await provider.connection.getAccountInfo(
+      delegateComponent.componentPda,
+    );
+    expect(acc?.owner.toBase58()).to.equal(DELEGATION_PROGRAM_ID.toBase58());
+  });
+});