diff --git a/cli/src/rust_template.rs b/cli/src/rust_template.rs index 0a0e536..91062ac 100644 --- a/cli/src/rust_template.rs +++ b/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; const systemMovement = anchor.workspace.Movement as Program; @@ -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); }}); }}); diff --git a/clients/bolt-sdk/src/index.ts b/clients/bolt-sdk/src/index.ts index 5386c2a..6d62a13 100644 --- a/clients/bolt-sdk/src/index.ts +++ b/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 diff --git a/clients/bolt-sdk/src/transactions/transactions.ts b/clients/bolt-sdk/src/world/transactions.ts similarity index 61% rename from clients/bolt-sdk/src/transactions/transactions.ts rename to clients/bolt-sdk/src/world/transactions.ts index dc0ae9e..62e4428 100644 --- a/clients/bolt-sdk/src/transactions/transactions.ts +++ b/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}`; -} diff --git a/tests/bolt.js b/tests/bolt.js deleted file mode 100644 index 41b1a95..0000000 --- a/tests/bolt.js +++ /dev/null @@ -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]; - } -}); diff --git a/tests/bolt.ts b/tests/bolt.ts index 3af0505..497cd74 100644 --- a/tests/bolt.ts +++ b/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; - const boltComponentPositionProgram = anchor.workspace + const boltWorld = anchor.workspace.World as Program; + const boltComponentProgram = anchor.workspace + .BoltComponent as Program; + + const exampleComponentPosition = anchor.workspace .Position as Program; - const boltComponentVelocityProgram = anchor.workspace + const exampleComponentVelocity = anchor.workspace .Velocity as Program; - const boltComponentProgramOrigin = anchor.workspace - .BoltComponent as Program; - const systemSimpleMovement = ( + const exampleSystemSimpleMovement = ( anchor.workspace.SystemSimpleMovement as Program ).programId; - const systemFly = (anchor.workspace.SystemFly as Program) + const exampleSystemFly = (anchor.workspace.SystemFly as Program) .programId; - const applyVelocity = ( + const exampleSystemApplyVelocity = ( anchor.workspace.SystemApplyVelocity as Program ).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); }); });