diff --git a/clients/bolt-sdk/src/index.ts b/clients/bolt-sdk/src/index.ts index 1f1429b..4050591 100644 --- a/clients/bolt-sdk/src/index.ts +++ b/clients/bolt-sdk/src/index.ts @@ -76,6 +76,26 @@ export function FindEntityPda({ )[0]; } +export function FindSessionTokenPda({ + targetProgram, + sessionSigner, + authority, +}: { + targetProgram: PublicKey; + sessionSigner: PublicKey; + authority: PublicKey; +}) { + return PublicKey.findProgramAddressSync( + [ + Buffer.from("session_token"), + targetProgram.toBytes(), + sessionSigner.toBytes(), + authority.toBytes(), + ], + SessionProgram.programId, + )[0]; +} + // TODO: seed must be Uint8Array like the other FindPda functions export function FindComponentPda({ componentId, diff --git a/clients/bolt-sdk/src/world/transactions.ts b/clients/bolt-sdk/src/world/transactions.ts index 3a930a5..4c656c6 100644 --- a/clients/bolt-sdk/src/world/transactions.ts +++ b/clients/bolt-sdk/src/world/transactions.ts @@ -11,11 +11,14 @@ import { SerializeArgs, SYSVAR_INSTRUCTIONS_PUBKEY, World, + SessionProgram, + FindSessionTokenPda, } from "../index"; import BN from "bn.js"; import type web3 from "@solana/web3.js"; import { type Connection, + Keypair, type PublicKey, Transaction, type TransactionInstruction, @@ -47,6 +50,49 @@ export async function InitializeRegistry({ }; } +export async function CreateSession({ + sessionSigner, + authority, + targetProgram, + topUp, + validity, +}: { + sessionSigner?: Keypair; + authority: PublicKey; + targetProgram: PublicKey; + topUp?: boolean; + validity?: BN; +}): Promise<{ + instruction: TransactionInstruction; + transaction: Transaction; + sessionToken: PublicKey; + sessionSigner: Keypair; +}> { + sessionSigner = sessionSigner ?? Keypair.generate(); + const sessionToken = FindSessionTokenPda({ + targetProgram, + sessionSigner: sessionSigner.publicKey, + authority, + }); + topUp = topUp ?? false; + let instruction = await SessionProgram.methods + .createSession(topUp, validity ?? null) + .accounts({ + sessionSigner: sessionSigner.publicKey, + authority, + targetProgram, + sessionToken, + }) + .instruction(); + const transaction = new Transaction().add(instruction); + return { + instruction, + transaction, + sessionToken, + sessionSigner, + }; +} + /** * Create the transaction to Initialize a new world * @param payer @@ -438,6 +484,7 @@ export async function ApplySystem({ world, extraAccounts, args, + sessionToken, }: { authority: PublicKey; systemId: PublicKey; @@ -445,6 +492,7 @@ export async function ApplySystem({ world: PublicKey; extraAccounts?: web3.AccountMeta[]; args?: object; + sessionToken?: PublicKey; }): Promise<{ instruction: TransactionInstruction; transaction: Transaction }> { const instruction = await createApplySystemInstruction({ authority, @@ -453,6 +501,7 @@ export async function ApplySystem({ world, extraAccounts, args, + sessionToken, }); const transaction = new Transaction().add(instruction); return { diff --git a/crates/bolt-lang/attribute/bolt-program/src/lib.rs b/crates/bolt-lang/attribute/bolt-program/src/lib.rs index bf3ca4e..5e83460 100644 --- a/crates/bolt-lang/attribute/bolt-program/src/lib.rs +++ b/crates/bolt-lang/attribute/bolt-program/src/lib.rs @@ -150,14 +150,18 @@ fn generate_update(component_type: &Type) -> (TokenStream2, TokenStream2) { pub fn update(ctx: Context, data: Vec) -> Result<()> { // TODO: Should we also check if the session token authority can be the World::id? if let Some(session_token) = &ctx.accounts.session_token { - let validity_ctx = bolt_lang::session_keys::ValidityChecker { - session_token: session_token.clone(), - session_signer: ctx.accounts.authority.clone(), - authority: ctx.accounts.bolt_component.bolt_metadata.authority.clone(), - target_program: crate::id(), - }; - require!(session_token.validate(validity_ctx)?, bolt_lang::session_keys::SessionError::InvalidToken); - require_eq!(ctx.accounts.bolt_component.bolt_metadata.authority, session_token.authority, bolt_lang::session_keys::SessionError::InvalidToken); + if ctx.accounts.bolt_component.bolt_metadata.authority == World::id() { + require!(Clock::get()?.unix_timestamp < session_token.valid_until, bolt_lang::session_keys::SessionError::InvalidToken); + } else { + let validity_ctx = bolt_lang::session_keys::ValidityChecker { + session_token: session_token.clone(), + session_signer: ctx.accounts.authority.clone(), + authority: ctx.accounts.bolt_component.bolt_metadata.authority.clone(), + target_program: crate::id(), + }; + require!(session_token.validate(validity_ctx)?, bolt_lang::session_keys::SessionError::InvalidToken); + require_eq!(ctx.accounts.bolt_component.bolt_metadata.authority, session_token.authority, bolt_lang::session_keys::SessionError::InvalidToken); + } } else { require!(ctx.accounts.bolt_component.bolt_metadata.authority == World::id() || (ctx.accounts.bolt_component.bolt_metadata.authority == *ctx.accounts.authority.key && ctx.accounts.authority.is_signer), BoltError::InvalidAuthority); } diff --git a/tests/framework.ts b/tests/framework.ts index 60ab6ec..7b47d89 100644 --- a/tests/framework.ts +++ b/tests/framework.ts @@ -71,20 +71,14 @@ export class Framework { entity1Pda: PublicKey; entity2Pda: PublicKey; entity4Pda: PublicKey; - entity5Pda: PublicKey; componentPositionEntity1Pda: PublicKey; componentVelocityEntity1Pda: PublicKey; componentPositionEntity4Pda: PublicKey; - componentPositionEntity5Pda: PublicKey; - - sessionSigner: Keypair; - sessionToken: PublicKey; constructor() { this.secondAuthority = Keypair.generate().publicKey; - this.sessionSigner = Keypair.generate(); this.worldProgram = anchor.workspace.World; this.exampleComponentPosition = anchor.workspace.Position; this.exampleComponentVelocity = anchor.workspace.Velocity; diff --git a/tests/intermediate-level/permissioning/component.ts b/tests/intermediate-level/permissioning/component.ts index bb2e07a..93dfe4e 100644 --- a/tests/intermediate-level/permissioning/component.ts +++ b/tests/intermediate-level/permissioning/component.ts @@ -1,34 +1,37 @@ import { expect } from "chai"; -import { AddEntity, ApplySystem, InitializeComponent } from "../../../clients/bolt-sdk/lib"; +import { anchor, AddEntity, ApplySystem, InitializeComponent } from "../../../clients/bolt-sdk/lib"; import { Keypair } from "@solana/web3.js"; export function component(framework) { describe("Component authority", () => { - it("Add entity 5", async () => { + let entity: anchor.web3.PublicKey; + let component: anchor.web3.PublicKey; + + it("Add authority test entity", async () => { const addEntity = await AddEntity({ payer: framework.provider.wallet.publicKey, world: framework.worldPda, connection: framework.provider.connection, }); await framework.provider.sendAndConfirm(addEntity.transaction); - framework.entity5Pda = addEntity.entityPda; // Saved for later + entity = addEntity.entityPda; // Saved for later }); - it("Initialize Position Component on Entity 5 (with authority)", async () => { + it("Initialize position component with authority on authority test entity", async () => { const initializeComponent = await InitializeComponent({ payer: framework.provider.wallet.publicKey, - entity: framework.entity5Pda, + entity: entity, componentId: framework.exampleComponentPosition.programId, authority: framework.provider.wallet.publicKey, }); await framework.provider.sendAndConfirm(initializeComponent.transaction); - framework.componentPositionEntity5Pda = initializeComponent.componentPda; // Saved for later + component = initializeComponent.componentPda; // Saved for later }); - it("Apply Fly System on Entity 5 (should fail with wrong authority)", async () => { + it("Shouldn't apply fly system on authority test entity with wrong authority", async () => { const positionBefore = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); let keypair = Keypair.generate(); @@ -39,22 +42,17 @@ export function component(framework) { world: framework.worldPda, entities: [ { - entity: framework.entity5Pda, + entity: entity, components: [ { componentId: framework.exampleComponentPosition.programId }, ], }, ], }); - applySystem.transaction.recentBlockhash = ( - await framework.provider.connection.getLatestBlockhash() - ).blockhash; - applySystem.transaction.feePayer = framework.provider.wallet.publicKey; - applySystem.transaction.sign(keypair); let failed = false; try { - await framework.provider.sendAndConfirm(applySystem.transaction); + await framework.provider.sendAndConfirm(applySystem.transaction, [keypair]); } catch (error) { failed = true; expect(error.logs.join("\n")).to.contain("Error Code: InvalidAuthority"); @@ -63,7 +61,7 @@ export function component(framework) { const positionAfter = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); expect(positionBefore.x.toNumber()).to.equal(positionAfter.x.toNumber()); @@ -71,10 +69,10 @@ export function component(framework) { expect(positionBefore.z.toNumber()).to.equal(positionAfter.z.toNumber()); }); - it("Apply Fly System on Entity 5 should succeed with correct authority", async () => { + it("Apply Fly System on authority test entity should succeed with correct authority", async () => { const positionBefore = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); const applySystem = await ApplySystem({ @@ -83,7 +81,7 @@ export function component(framework) { world: framework.worldPda, entities: [ { - entity: framework.entity5Pda, + entity, components: [ { componentId: framework.exampleComponentPosition.programId }, ], @@ -92,10 +90,10 @@ export function component(framework) { }); await framework.provider.sendAndConfirm(applySystem.transaction); - + const positionAfter = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); diff --git a/tests/intermediate-level/permissioning/world.ts b/tests/intermediate-level/permissioning/world.ts index eda6937..10f9a11 100644 --- a/tests/intermediate-level/permissioning/world.ts +++ b/tests/intermediate-level/permissioning/world.ts @@ -65,7 +65,7 @@ export function world(framework) { ); }); - it("Whitelist System", async () => { + it("Whitelist Fly System", async () => { const approveSystem = await ApproveSystem({ authority: framework.provider.wallet.publicKey, systemToApprove: framework.systemFly.programId, @@ -84,7 +84,7 @@ export function world(framework) { expect(worldAccount.systems.length).to.be.greaterThan(0); }); - it("Whitelist System 2", async () => { + it("Whitelist Apply Velocity System system", async () => { const approveSystem = await ApproveSystem({ authority: framework.provider.wallet.publicKey, systemToApprove: framework.systemApplyVelocity.programId, @@ -120,7 +120,7 @@ export function world(framework) { await framework.provider.sendAndConfirm(applySystem.transaction); }); - it("Remove System 1", async () => { + it("Remove Fly System", async () => { const removeSystem = await RemoveSystem({ authority: framework.provider.wallet.publicKey, systemToRemove: framework.systemFly.programId, @@ -162,42 +162,5 @@ export function world(framework) { } expect(invalid).to.equal(true); }); - - it("Check invalid component init without CPI", async () => { - let invalid = false; - try { - await framework.exampleComponentPosition.methods - .initialize() - .accounts({ - payer: framework.provider.wallet.publicKey, - data: framework.componentPositionEntity5Pda, - entity: framework.entity5Pda, - authority: framework.provider.wallet.publicKey, - }) - .rpc(); - } catch (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 framework.exampleComponentPosition.methods - .update(Buffer.from("")) - .accounts({ - boltComponent: framework.componentPositionEntity4Pda, - authority: framework.provider.wallet.publicKey, - sessionToken: null, - }) - .rpc(); - } catch (error) { - expect(error.message).to.contain("Error Code: InvalidCaller"); - invalid = true; - } - expect(invalid).to.equal(true); - }); }); } diff --git a/tests/intermediate-level/session.ts b/tests/intermediate-level/session.ts index 2d5ac03..12f802e 100644 --- a/tests/intermediate-level/session.ts +++ b/tests/intermediate-level/session.ts @@ -1,29 +1,132 @@ -import { anchor, SessionProgram } from "../../clients/bolt-sdk/lib"; +import { Keypair } from "@solana/web3.js"; +import { AddEntity, CreateSession, InitializeComponent, ApplySystem, anchor } from "../../clients/bolt-sdk/lib"; +import { expect } from "chai"; // TODO: Create the API for it. export function session(framework) { describe("Session", () => { + let sessionSigner: Keypair; + let sessionToken: anchor.web3.PublicKey; + let entity: anchor.web3.PublicKey; + let component: anchor.web3.PublicKey; + let entityWithAuthority: anchor.web3.PublicKey; + let componentWithAuthority: anchor.web3.PublicKey; + it("Create Session", async () => { - const airdrop = await framework.provider.connection.requestAirdrop( - framework.sessionSigner.publicKey, - anchor.web3.LAMPORTS_PER_SOL, - ); - - await framework.provider.connection.confirmTransaction( - airdrop, - "confirmed", - ); - - const keys = await SessionProgram.methods - .createSession(true, null) - .accounts({ - sessionSigner: framework.sessionSigner.publicKey, - authority: framework.provider.wallet.publicKey, - targetProgram: framework.exampleComponentPosition.programId, - }) - .signers([framework.sessionSigner]) - .rpcAndKeys(); - framework.sessionToken = keys.pubkeys.sessionToken as anchor.web3.PublicKey; + const createSession = await CreateSession({ + authority: framework.provider.wallet.publicKey, + targetProgram: framework.exampleComponentPosition.programId, + topUp: true, + }); + sessionSigner = createSession.sessionSigner; + sessionToken = createSession.sessionToken; + await framework.provider.sendAndConfirm(createSession.transaction, [sessionSigner]); + }); + + it("Add entity 1", async () => { + const addEntity = await AddEntity({ + payer: framework.provider.wallet.publicKey, + world: framework.worldPda, + connection: framework.provider.connection, + }); + await framework.provider.sendAndConfirm(addEntity.transaction); + entity = addEntity.entityPda; + }); + + it("Initialize position component", async () => { + const initializeComponent = await InitializeComponent({ + payer: sessionSigner.publicKey, + entity: entity, + componentId: framework.exampleComponentPosition.programId, + }); + await framework.provider.sendAndConfirm(initializeComponent.transaction, [sessionSigner]); + component = initializeComponent.componentPda; + }); + + it("Apply Fly System on component using session token", async () => { + const positionBefore = + await framework.exampleComponentPosition.account.position.fetch( + component, + ); + + const applySystem = await ApplySystem({ + authority: sessionSigner.publicKey, + systemId: framework.systemFly.programId, + world: framework.worldPda, + sessionToken, + entities: [ + { + entity: entity, + components: [ + { componentId: framework.exampleComponentPosition.programId }, + ], + }, + ], + }); + await framework.provider.sendAndConfirm(applySystem.transaction, [sessionSigner]); + + const positionAfter = + await framework.exampleComponentPosition.account.position.fetch( + component, + ); + + expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); + expect(positionAfter.y.toNumber()).to.equal(positionBefore.y.toNumber()); + expect(positionAfter.z.toNumber()).to.equal(positionBefore.z.toNumber() + 1); + }); + + it("Add entity for authority test", async () => { + const addEntity = await AddEntity({ + payer: framework.provider.wallet.publicKey, + world: framework.worldPda, + connection: framework.provider.connection, + }); + await framework.provider.sendAndConfirm(addEntity.transaction); + entityWithAuthority = addEntity.entityPda; + }); + + it("Initialize position component with authority", async () => { + const initializeComponent = await InitializeComponent({ + payer: sessionSigner.publicKey, + entity: entityWithAuthority, + componentId: framework.exampleComponentPosition.programId, + authority: framework.provider.wallet.publicKey, + }); + await framework.provider.sendAndConfirm(initializeComponent.transaction, [sessionSigner]); + componentWithAuthority = initializeComponent.componentPda; + }); + + it("Apply Fly System on component with authority using session token", async () => { + const positionBefore = + await framework.exampleComponentPosition.account.position.fetch( + componentWithAuthority, + ); + + const applySystem = await ApplySystem({ + authority: sessionSigner.publicKey, + systemId: framework.systemFly.programId, + world: framework.worldPda, + sessionToken, + entities: [ + { + entity: entityWithAuthority, + components: [ + { componentId: framework.exampleComponentPosition.programId }, + ], + }, + ], + }); + await framework.provider.sendAndConfirm(applySystem.transaction, [sessionSigner]); + + const positionAfter = + await framework.exampleComponentPosition.account.position.fetch( + componentWithAuthority, + ); + + expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); + expect(positionAfter.y.toNumber()).to.equal(positionBefore.y.toNumber()); + expect(positionAfter.z.toNumber()).to.equal(positionBefore.z.toNumber() + 1); }); }); } + diff --git a/tests/low-level/permissioning/component.ts b/tests/low-level/permissioning/component.ts index 218800a..f8a042f 100644 --- a/tests/low-level/permissioning/component.ts +++ b/tests/low-level/permissioning/component.ts @@ -3,12 +3,15 @@ import { anchor, FindEntityPda, FindComponentPda, SerializeArgs } from "../../.. import { expect } from "chai"; export function component(framework) { + let entity: anchor.web3.PublicKey; + let component: anchor.web3.PublicKey; + describe("Component authority", () => { it("Add entity 5", async () => { const world = await framework.worldProgram.account.world.fetch( framework.worldPda, ); - framework.entity5Pda = FindEntityPda({ + entity = FindEntityPda({ worldId: world.id, entityId: world.entities, }); @@ -17,25 +20,25 @@ export function component(framework) { .accounts({ payer: framework.provider.wallet.publicKey, world: framework.worldPda, - entity: framework.entity5Pda, + entity: entity, }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); await framework.provider.sendAndConfirm(transaction); }); - it("Initialize Position Component on Entity 5 (with authority)", async () => { + it("Initialize position component with authority on authority test entity", async () => { const componentId = framework.exampleComponentPosition.programId; - framework.componentPositionEntity5Pda = FindComponentPda({ + component = FindComponentPda({ componentId, - entity: framework.entity5Pda, + entity: entity, }); const instruction = await framework.worldProgram.methods .initializeComponent() .accounts({ payer: framework.provider.wallet.publicKey, - entity: framework.entity5Pda, - data: framework.componentPositionEntity5Pda, + entity: entity, + data: component, componentProgram: componentId, authority: framework.provider.wallet.publicKey, }) @@ -44,10 +47,10 @@ export function component(framework) { await framework.provider.sendAndConfirm(transaction); }); - it("Apply Fly System on Entity 5 (should fail with wrong authority)", async () => { + it("Shouldn't apply fly system on authority test entity with wrong authority", async () => { const positionBefore = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); let keypair = Keypair.generate(); @@ -67,22 +70,17 @@ export function component(framework) { isWritable: false, }, { - pubkey: framework.componentPositionEntity5Pda, + pubkey: component, isSigner: false, isWritable: true, }, ]) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); - transaction.recentBlockhash = ( - await framework.provider.connection.getLatestBlockhash() - ).blockhash; - transaction.feePayer = framework.provider.wallet.publicKey; - transaction.sign(keypair); let failed = false; try { - await framework.provider.sendAndConfirm(transaction); + await framework.provider.sendAndConfirm(transaction, [keypair]); } catch (error) { failed = true; expect(error.logs.join("\n")).to.contain("Error Code: InvalidAuthority"); @@ -91,7 +89,7 @@ export function component(framework) { const positionAfter = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); expect(positionBefore.x.toNumber()).to.equal(positionAfter.x.toNumber()); @@ -99,10 +97,10 @@ export function component(framework) { expect(positionBefore.z.toNumber()).to.equal(positionAfter.z.toNumber()); }); - it("Apply Fly System on Entity 5 should succeed with correct authority", async () => { + it("Apply Fly System on authority test entity should succeed with correct authority", async () => { const positionBefore = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); const instruction = await framework.worldProgram.methods @@ -120,7 +118,7 @@ export function component(framework) { isWritable: false, }, { - pubkey: framework.componentPositionEntity5Pda, + pubkey: component, isSigner: false, isWritable: true, }, @@ -131,12 +129,12 @@ export function component(framework) { await framework.provider.sendAndConfirm(transaction); const positionAfter = await framework.exampleComponentPosition.account.position.fetch( - framework.componentPositionEntity5Pda, + component, ); expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); expect(positionAfter.y.toNumber()).to.equal(positionBefore.y.toNumber()); expect(positionAfter.z.toNumber()).to.equal(positionBefore.z.toNumber() + 1); - }); + }); }); } \ No newline at end of file diff --git a/tests/low-level/permissioning/world.ts b/tests/low-level/permissioning/world.ts index 832c9fe..4240ad2 100644 --- a/tests/low-level/permissioning/world.ts +++ b/tests/low-level/permissioning/world.ts @@ -71,7 +71,7 @@ export function world(framework) { ); }); - it("Whitelist System", async () => { + it("Whitelist Fly System", async () => { const instruction = await framework.worldProgram.methods .approveSystem() .accounts({ @@ -93,7 +93,7 @@ export function world(framework) { expect(worldAccount.systems.length).to.be.greaterThan(0); }); - it("Whitelist System 2", async () => { + it("Whitelist Apply Velocity System system", async () => { const instruction = await framework.worldProgram.methods .approveSystem() .accounts({ @@ -141,7 +141,7 @@ export function world(framework) { await framework.provider.sendAndConfirm(transaction); }); - it("Remove System 1", async () => { + it("Remove Fly System", async () => { const instruction = await framework.worldProgram.methods .removeSystem() .accounts({ @@ -203,8 +203,8 @@ export function world(framework) { .initialize() .accounts({ payer: framework.provider.wallet.publicKey, - data: framework.componentPositionEntity5Pda, - entity: framework.entity5Pda, + data: framework.componentPositionEntity1Pda, + entity: framework.entity1Pda, authority: framework.provider.wallet.publicKey, }) .rpc(); diff --git a/tests/low-level/session.ts b/tests/low-level/session.ts index 3c1aeff..385966a 100644 --- a/tests/low-level/session.ts +++ b/tests/low-level/session.ts @@ -1,28 +1,193 @@ -import { anchor, SessionProgram } from "../../clients/bolt-sdk/lib"; +import { expect } from "chai"; +import { anchor, FindComponentPda, FindEntityPda, SerializeArgs, SessionProgram, FindSessionTokenPda } from "../../clients/bolt-sdk/lib"; +import { Keypair } from "@solana/web3.js"; export function session(framework) { describe("Session", () => { + const sessionSigner: Keypair = Keypair.generate(); + let sessionToken: anchor.web3.PublicKey; + let entity: anchor.web3.PublicKey; + let component: anchor.web3.PublicKey; + let entityWithAuthority: anchor.web3.PublicKey; + let componentWithAuthority: anchor.web3.PublicKey; + it("Create Session", async () => { - const airdrop = await framework.provider.connection.requestAirdrop( - framework.sessionSigner.publicKey, - anchor.web3.LAMPORTS_PER_SOL, - ); + sessionToken = FindSessionTokenPda({ + targetProgram: framework.exampleComponentPosition.programId, + sessionSigner: sessionSigner.publicKey, + authority: framework.provider.wallet.publicKey, + }); + let instruction = await SessionProgram.methods + .createSession(true, null) + .accounts({ + sessionSigner: sessionSigner.publicKey, + authority: framework.provider.wallet.publicKey, + targetProgram: framework.exampleComponentPosition.programId, + sessionToken + }) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + }); - await framework.provider.connection.confirmTransaction( - airdrop, - "confirmed", + it("Add entity", async () => { + const world = await framework.worldProgram.account.world.fetch( + framework.worldPda, ); + entity = FindEntityPda({ + worldId: world.id, + entityId: world.entities, + }); + const instruction = await framework.worldProgram.methods + .addEntity(null) + .accounts({ + payer: sessionSigner.publicKey, + entity: entity, + world: framework.worldPda, + }) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + }); - const keys = await SessionProgram.methods - .createSession(true, null) + it("Initialize position component", async () => { + const componentId = framework.exampleComponentPosition.programId; + component = FindComponentPda({ + componentId, + entity, + }); + const instruction = await framework.worldProgram.methods + .initializeComponent() .accounts({ - sessionSigner: framework.sessionSigner.publicKey, + payer: sessionSigner.publicKey, + entity: entity, + data: component, + componentProgram: componentId, + authority: framework.worldProgram.programId, + }) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + }); + + it("Apply Fly System on component using session token", async () => { + const positionBefore = + await framework.exampleComponentPosition.account.position.fetch( + component, + ); + + const instruction = await framework.worldProgram.methods + .apply(SerializeArgs()) + .accounts({ + authority: sessionSigner.publicKey, + boltSystem: framework.systemFly.programId, + world: framework.worldPda, + sessionToken, + }) + .remainingAccounts([ + { + pubkey: framework.exampleComponentPosition.programId, + isSigner: false, + isWritable: false, + }, + { + pubkey: component, + isSigner: false, + isWritable: true, + }, + ]) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + const positionAfter = + await framework.exampleComponentPosition.account.position.fetch( + component, + ); + + expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); + expect(positionAfter.y.toNumber()).to.equal(positionBefore.y.toNumber()); + expect(positionAfter.z.toNumber()).to.equal(positionBefore.z.toNumber() + 1); + }); + + it("Add entity for authority test", async () => { + const world = await framework.worldProgram.account.world.fetch( + framework.worldPda, + ); + entityWithAuthority = FindEntityPda({ + worldId: world.id, + entityId: world.entities, + }); + const instruction = await framework.worldProgram.methods + .addEntity(null) + .accounts({ + payer: sessionSigner.publicKey, + world: framework.worldPda, + entity: entityWithAuthority, + }) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + }); + + it("Initialize position component with authority", async () => { + const componentId = framework.exampleComponentPosition.programId; + componentWithAuthority = FindComponentPda({ + componentId, + entity: entityWithAuthority, + }); + const instruction = await framework.worldProgram.methods + .initializeComponent() + .accounts({ + payer: sessionSigner.publicKey, + entity: entityWithAuthority, + data: componentWithAuthority, + componentProgram: componentId, authority: framework.provider.wallet.publicKey, - targetProgram: framework.exampleComponentPosition.programId, }) - .signers([framework.sessionSigner]) - .rpcAndKeys(); - framework.sessionToken = keys.pubkeys.sessionToken as anchor.web3.PublicKey; + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + }); + + it("Apply Fly System on component with authority using session token", async () => { + const positionBefore = + await framework.exampleComponentPosition.account.position.fetch( + componentWithAuthority, + ); + + const instruction = await framework.worldProgram.methods + .apply(SerializeArgs()) + .accounts({ + authority: sessionSigner.publicKey, + boltSystem: framework.systemFly.programId, + world: framework.worldPda, + sessionToken, + }) + .remainingAccounts([ + { + pubkey: framework.exampleComponentPosition.programId, + isSigner: false, + isWritable: false, + }, + { + pubkey: componentWithAuthority, + isSigner: false, + isWritable: true, + }, + ]) + .instruction(); + const transaction = new anchor.web3.Transaction().add(instruction); + + await framework.provider.sendAndConfirm(transaction, [sessionSigner]); + const positionAfter = + await framework.exampleComponentPosition.account.position.fetch( + componentWithAuthority, + ); + + expect(positionAfter.x.toNumber()).to.equal(positionBefore.x.toNumber()); + expect(positionAfter.y.toNumber()).to.equal(positionBefore.y.toNumber()); + expect(positionAfter.z.toNumber()).to.equal(positionBefore.z.toNumber() + 1); }); }); }