Skip to content

Commit

Permalink
feat: Allow passing multiple entities in the ApplySystem function (#51)
Browse files Browse the repository at this point in the history
## Problem

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

## Solution

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

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

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

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

## Notes

Matching PR for docs update:
magicblock-labs/docs#6
  • Loading branch information
crypto-vincent committed Jun 17, 2024
1 parent 03a585c commit eef1e0e
Show file tree
Hide file tree
Showing 5 changed files with 542 additions and 1,191 deletions.
74 changes: 39 additions & 35 deletions cli/src/rust_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,6 @@ import {{
AddEntity,
InitializeComponent,
ApplySystem,
FindComponentPda,
}} from "@magicblock-labs/bolt-sdk"
import {{expect}} from "chai";
Expand All @@ -422,6 +421,7 @@ describe("{}", () => {{
// Constants used to test the program.
let worldPda: PublicKey;
let entityPda: PublicKey;
let componentPda: PublicKey;
const positionComponent = anchor.workspace.Position as Program<Position>;
const systemMovement = anchor.workspace.Movement as Program<Movement>;
Expand All @@ -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);
}});
}});
Expand Down
83 changes: 43 additions & 40 deletions clients/bolt-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit eef1e0e

Please sign in to comment.