From 818faefac7cf6ace84e7ad9a032212d801630cbd Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Wed, 2 Oct 2024 18:10:53 +0700 Subject: [PATCH] feat: Add authority approval/removal for world instances --- .github/workflows/publish-bolt-crates.yml | 2 +- .github/workflows/publish-bolt-sdk.yml | 2 +- .github/workflows/run-tests.yml | 6 +- clients/bolt-sdk/src/generated/idl/world.json | 598 ++++++++++++++++++ clients/bolt-sdk/src/generated/index.ts | 5 + clients/bolt-sdk/src/generated/types/world.ts | 478 ++++++++++++++ clients/bolt-sdk/src/world/transactions.ts | 86 ++- programs/world/src/error.rs | 4 + programs/world/src/lib.rs | 67 +- tests/bolt.ts | 61 +- 10 files changed, 1295 insertions(+), 14 deletions(-) create mode 100644 clients/bolt-sdk/src/generated/idl/world.json create mode 100644 clients/bolt-sdk/src/generated/types/world.ts diff --git a/.github/workflows/publish-bolt-crates.yml b/.github/workflows/publish-bolt-crates.yml index 56d9623..d3a3494 100644 --- a/.github/workflows/publish-bolt-crates.yml +++ b/.github/workflows/publish-bolt-crates.yml @@ -70,7 +70,7 @@ jobs: - name: Cache rust uses: Swatinem/rust-cache@v2 - name: Run fmt - run: cargo fmt -- --check + run: cargo fmt -- --check --verbose - name: Run clippy run: cargo clippy -- --deny=warnings diff --git a/.github/workflows/publish-bolt-sdk.yml b/.github/workflows/publish-bolt-sdk.yml index 4f03960..fcf543f 100644 --- a/.github/workflows/publish-bolt-sdk.yml +++ b/.github/workflows/publish-bolt-sdk.yml @@ -70,7 +70,7 @@ jobs: - name: Cache rust uses: Swatinem/rust-cache@v2 - name: Run fmt - run: cargo fmt -- --check + run: cargo fmt -- --check --verbose - name: Run clippy run: cargo clippy -- --deny=warnings diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d15c2ba..5bb7ba1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 21 - name: install essentials run: | @@ -71,8 +71,8 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Check Rust version run: rustc --version -# - name: Run fmt -# run: cargo fmt -- --check --verbose + - name: Run fmt + run: cargo fmt -- --check --verbose - name: Run clippy run: cargo clippy -- --deny=warnings diff --git a/clients/bolt-sdk/src/generated/idl/world.json b/clients/bolt-sdk/src/generated/idl/world.json new file mode 100644 index 0000000..6e0b2b5 --- /dev/null +++ b/clients/bolt-sdk/src/generated/idl/world.json @@ -0,0 +1,598 @@ +{ + "address": "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n", + "metadata": { + "name": "world", + "version": "0.1.9", + "spec": "0.1.0", + "description": "Bolt World program", + "repository": "https://github.com/magicblock-labs/bolt" + }, + "instructions": [ + { + "name": "add_authority", + "discriminator": [ + 229, + 9, + 106, + 73, + 91, + 213, + 109, + 183 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "new_authority" + }, + { + "name": "world", + "writable": true + } + ], + "args": [ + { + "name": "world_id", + "type": "u64" + } + ] + }, + { + "name": "add_entity", + "discriminator": [ + 163, + 241, + 57, + 35, + 244, + 244, + 48, + 57 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "entity", + "writable": true + }, + { + "name": "world", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "extra_seed", + "type": { + "option": "string" + } + } + ] + }, + { + "name": "apply", + "discriminator": [ + 248, + 243, + 145, + 24, + 105, + 50, + 162, + 225 + ], + "accounts": [ + { + "name": "component_program" + }, + { + "name": "bolt_system" + }, + { + "name": "bolt_component", + "writable": true + }, + { + "name": "authority", + "signer": true + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": "bytes" + } + ] + }, + { + "name": "apply2", + "discriminator": [ + 120, + 32, + 116, + 154, + 158, + 159, + 208, + 73 + ], + "accounts": [ + { + "name": "bolt_system" + }, + { + "name": "component_program_1" + }, + { + "name": "bolt_component_1", + "writable": true + }, + { + "name": "component_program_2" + }, + { + "name": "bolt_component_2", + "writable": true + }, + { + "name": "authority", + "signer": true + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": "bytes" + } + ] + }, + { + "name": "apply3", + "discriminator": [ + 254, + 146, + 49, + 7, + 236, + 131, + 105, + 221 + ], + "accounts": [ + { + "name": "bolt_system" + }, + { + "name": "component_program_1" + }, + { + "name": "bolt_component_1", + "writable": true + }, + { + "name": "component_program_2" + }, + { + "name": "bolt_component_2", + "writable": true + }, + { + "name": "component_program_3" + }, + { + "name": "bolt_component_3", + "writable": true + }, + { + "name": "authority", + "signer": true + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": "bytes" + } + ] + }, + { + "name": "apply4", + "discriminator": [ + 223, + 104, + 24, + 79, + 252, + 196, + 14, + 109 + ], + "accounts": [ + { + "name": "bolt_system" + }, + { + "name": "component_program_1" + }, + { + "name": "bolt_component_1", + "writable": true + }, + { + "name": "component_program_2" + }, + { + "name": "bolt_component_2", + "writable": true + }, + { + "name": "component_program_3" + }, + { + "name": "bolt_component_3", + "writable": true + }, + { + "name": "component_program_4" + }, + { + "name": "bolt_component_4", + "writable": true + }, + { + "name": "authority", + "signer": true + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": "bytes" + } + ] + }, + { + "name": "apply5", + "discriminator": [ + 70, + 164, + 214, + 28, + 136, + 116, + 84, + 153 + ], + "accounts": [ + { + "name": "bolt_system" + }, + { + "name": "component_program_1" + }, + { + "name": "bolt_component_1", + "writable": true + }, + { + "name": "component_program_2" + }, + { + "name": "bolt_component_2", + "writable": true + }, + { + "name": "component_program_3" + }, + { + "name": "bolt_component_3", + "writable": true + }, + { + "name": "component_program_4" + }, + { + "name": "bolt_component_4", + "writable": true + }, + { + "name": "component_program_5" + }, + { + "name": "bolt_component_5", + "writable": true + }, + { + "name": "authority", + "signer": true + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "args", + "type": "bytes" + } + ] + }, + { + "name": "initialize_component", + "discriminator": [ + 36, + 143, + 233, + 113, + 12, + 234, + 61, + 30 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "data", + "writable": true + }, + { + "name": "entity" + }, + { + "name": "component_program" + }, + { + "name": "authority" + }, + { + "name": "instruction_sysvar_account", + "address": "Sysvar1nstructions1111111111111111111111111" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "initialize_new_world", + "discriminator": [ + 23, + 96, + 88, + 194, + 200, + 203, + 200, + 98 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "world", + "writable": true + }, + { + "name": "registry", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "initialize_registry", + "discriminator": [ + 189, + 181, + 20, + 17, + 174, + 57, + 249, + 59 + ], + "accounts": [ + { + "name": "registry", + "writable": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "remove_authority", + "discriminator": [ + 242, + 104, + 208, + 132, + 190, + 250, + 74, + 216 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "authority_to_delete" + }, + { + "name": "world", + "writable": true + } + ], + "args": [ + { + "name": "world_id", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "Entity", + "discriminator": [ + 46, + 157, + 161, + 161, + 254, + 46, + 79, + 24 + ] + }, + { + "name": "Registry", + "discriminator": [ + 47, + 174, + 110, + 246, + 184, + 182, + 252, + 218 + ] + }, + { + "name": "World", + "discriminator": [ + 145, + 45, + 170, + 174, + 122, + 32, + 155, + 124 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "InvalidAuthority", + "msg": "Invalid authority for instruction" + }, + { + "code": 6001, + "name": "WorldAccountMismatch", + "msg": "The provided world account does not match the expected PDA." + }, + { + "code": 6002, + "name": "TooManyAuthorities", + "msg": "Exceed the maximum number of authorities." + }, + { + "code": 6003, + "name": "AuthorityNotFound", + "msg": "The provided authority not found" + } + ], + "types": [ + { + "name": "Entity", + "type": { + "kind": "struct", + "fields": [ + { + "name": "id", + "type": "u64" + } + ] + } + }, + { + "name": "Registry", + "type": { + "kind": "struct", + "fields": [ + { + "name": "worlds", + "type": "u64" + } + ] + } + }, + { + "name": "World", + "type": { + "kind": "struct", + "fields": [ + { + "name": "id", + "type": "u64" + }, + { + "name": "entities", + "type": "u64" + }, + { + "name": "authorities", + "type": { + "vec": "pubkey" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/clients/bolt-sdk/src/generated/index.ts b/clients/bolt-sdk/src/generated/index.ts index 7dbd9d2..50e75dd 100644 --- a/clients/bolt-sdk/src/generated/index.ts +++ b/clients/bolt-sdk/src/generated/index.ts @@ -6,6 +6,8 @@ */ import { PublicKey } from "@solana/web3.js"; +import { type World as WorldProgram } from "./types/world"; +import idl from "./idl/world.json"; export * from "./accounts"; export * from "./errors"; export * from "./instructions"; @@ -25,3 +27,6 @@ export const PROGRAM_ADDRESS = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"; * @category generated */ export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS); + +export default WorldProgram; +export { idl as worldIdl }; diff --git a/clients/bolt-sdk/src/generated/types/world.ts b/clients/bolt-sdk/src/generated/types/world.ts new file mode 100644 index 0000000..86241ab --- /dev/null +++ b/clients/bolt-sdk/src/generated/types/world.ts @@ -0,0 +1,478 @@ +/** + * Program IDL in camelCase format in order to be used in JS/TS. + * + * Note that this is only a type helper and is not the actual IDL. The original + * IDL can be found at `target/idl/world.json`. + */ +export interface World { + address: "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"; + metadata: { + name: "world"; + version: "0.1.9"; + spec: "0.1.0"; + description: "Bolt World program"; + repository: "https://github.com/magicblock-labs/bolt"; + }; + instructions: [ + { + name: "addAuthority"; + discriminator: [229, 9, 106, 73, 91, 213, 109, 183]; + accounts: [ + { + name: "authority"; + writable: true; + signer: true; + }, + { + name: "newAuthority"; + }, + { + name: "world"; + writable: true; + } + ]; + args: [ + { + name: "worldId"; + type: "u64"; + } + ]; + }, + { + name: "addEntity"; + discriminator: [163, 241, 57, 35, 244, 244, 48, 57]; + accounts: [ + { + name: "payer"; + writable: true; + signer: true; + }, + { + name: "entity"; + writable: true; + }, + { + name: "world"; + writable: true; + }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + } + ]; + args: [ + { + name: "extraSeed"; + type: { + option: "string"; + }; + } + ]; + }, + { + name: "apply"; + discriminator: [248, 243, 145, 24, 105, 50, 162, 225]; + accounts: [ + { + name: "componentProgram"; + }, + { + name: "boltSystem"; + }, + { + name: "boltComponent"; + writable: true; + }, + { + name: "authority"; + signer: true; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + } + ]; + args: [ + { + name: "args"; + type: "bytes"; + } + ]; + }, + { + name: "apply2"; + discriminator: [120, 32, 116, 154, 158, 159, 208, 73]; + accounts: [ + { + name: "boltSystem"; + }, + { + name: "componentProgram1"; + }, + { + name: "boltComponent1"; + writable: true; + }, + { + name: "componentProgram2"; + }, + { + name: "boltComponent2"; + writable: true; + }, + { + name: "authority"; + signer: true; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + } + ]; + args: [ + { + name: "args"; + type: "bytes"; + } + ]; + }, + { + name: "apply3"; + discriminator: [254, 146, 49, 7, 236, 131, 105, 221]; + accounts: [ + { + name: "boltSystem"; + }, + { + name: "componentProgram1"; + }, + { + name: "boltComponent1"; + writable: true; + }, + { + name: "componentProgram2"; + }, + { + name: "boltComponent2"; + writable: true; + }, + { + name: "componentProgram3"; + }, + { + name: "boltComponent3"; + writable: true; + }, + { + name: "authority"; + signer: true; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + } + ]; + args: [ + { + name: "args"; + type: "bytes"; + } + ]; + }, + { + name: "apply4"; + discriminator: [223, 104, 24, 79, 252, 196, 14, 109]; + accounts: [ + { + name: "boltSystem"; + }, + { + name: "componentProgram1"; + }, + { + name: "boltComponent1"; + writable: true; + }, + { + name: "componentProgram2"; + }, + { + name: "boltComponent2"; + writable: true; + }, + { + name: "componentProgram3"; + }, + { + name: "boltComponent3"; + writable: true; + }, + { + name: "componentProgram4"; + }, + { + name: "boltComponent4"; + writable: true; + }, + { + name: "authority"; + signer: true; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + } + ]; + args: [ + { + name: "args"; + type: "bytes"; + } + ]; + }, + { + name: "apply5"; + discriminator: [70, 164, 214, 28, 136, 116, 84, 153]; + accounts: [ + { + name: "boltSystem"; + }, + { + name: "componentProgram1"; + }, + { + name: "boltComponent1"; + writable: true; + }, + { + name: "componentProgram2"; + }, + { + name: "boltComponent2"; + writable: true; + }, + { + name: "componentProgram3"; + }, + { + name: "boltComponent3"; + writable: true; + }, + { + name: "componentProgram4"; + }, + { + name: "boltComponent4"; + writable: true; + }, + { + name: "componentProgram5"; + }, + { + name: "boltComponent5"; + writable: true; + }, + { + name: "authority"; + signer: true; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + } + ]; + args: [ + { + name: "args"; + type: "bytes"; + } + ]; + }, + { + name: "initializeComponent"; + discriminator: [36, 143, 233, 113, 12, 234, 61, 30]; + accounts: [ + { + name: "payer"; + writable: true; + signer: true; + }, + { + name: "data"; + writable: true; + }, + { + name: "entity"; + }, + { + name: "componentProgram"; + }, + { + name: "authority"; + }, + { + name: "instructionSysvarAccount"; + address: "Sysvar1nstructions1111111111111111111111111"; + }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + } + ]; + args: []; + }, + { + name: "initializeNewWorld"; + discriminator: [23, 96, 88, 194, 200, 203, 200, 98]; + accounts: [ + { + name: "payer"; + writable: true; + signer: true; + }, + { + name: "world"; + writable: true; + }, + { + name: "registry"; + writable: true; + }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + } + ]; + args: []; + }, + { + name: "initializeRegistry"; + discriminator: [189, 181, 20, 17, 174, 57, 249, 59]; + accounts: [ + { + name: "registry"; + writable: true; + }, + { + name: "payer"; + writable: true; + signer: true; + }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + } + ]; + args: []; + }, + { + name: "removeAuthority"; + discriminator: [242, 104, 208, 132, 190, 250, 74, 216]; + accounts: [ + { + name: "authority"; + writable: true; + signer: true; + }, + { + name: "authorityToDelete"; + }, + { + name: "world"; + writable: true; + } + ]; + args: [ + { + name: "worldId"; + type: "u64"; + } + ]; + } + ]; + accounts: [ + { + name: "entity"; + discriminator: [46, 157, 161, 161, 254, 46, 79, 24]; + }, + { + name: "registry"; + discriminator: [47, 174, 110, 246, 184, 182, 252, 218]; + }, + { + name: "world"; + discriminator: [145, 45, 170, 174, 122, 32, 155, 124]; + } + ]; + errors: [ + { + code: 6000; + name: "invalidAuthority"; + msg: "Invalid authority for instruction"; + }, + { + code: 6001; + name: "worldAccountMismatch"; + msg: "The provided world account does not match the expected PDA."; + }, + { + code: 6002; + name: "tooManyAuthorities"; + msg: "Exceed the maximum number of authorities."; + }, + { + code: 6003; + name: "authorityNotFound"; + msg: "The provided authority not found"; + } + ]; + types: [ + { + name: "entity"; + type: { + kind: "struct"; + fields: [ + { + name: "id"; + type: "u64"; + } + ]; + }; + }, + { + name: "registry"; + type: { + kind: "struct"; + fields: [ + { + name: "worlds"; + type: "u64"; + } + ]; + }; + }, + { + name: "world"; + type: { + kind: "struct"; + fields: [ + { + name: "id"; + type: "u64"; + }, + { + name: "entities"; + type: "u64"; + }, + { + name: "authorities"; + type: { + vec: "pubkey"; + }; + } + ]; + }; + } + ]; +} diff --git a/clients/bolt-sdk/src/world/transactions.ts b/clients/bolt-sdk/src/world/transactions.ts index c7486b6..1660972 100644 --- a/clients/bolt-sdk/src/world/transactions.ts +++ b/clients/bolt-sdk/src/world/transactions.ts @@ -24,7 +24,9 @@ import { Transaction, type TransactionInstruction, } from "@solana/web3.js"; -import { PROGRAM_ID } from "../generated"; +import type WorldProgram from "../generated"; +import { PROGRAM_ID, worldIdl } from "../generated"; +import { type Idl, Program } from "@coral-xyz/anchor"; const MAX_COMPONENTS = 5; @@ -63,6 +65,88 @@ export async function InitializeNewWorld({ }; } +/** + * Create the transaction to Add a new authority + * @param authority + * @param newAuthority + * @param world + * @param connection + * @constructor + */ +export async function AddAuthority({ + authority, + newAuthority, + world, + connection, +}: { + authority: PublicKey; + newAuthority: PublicKey; + world: PublicKey; + connection: Connection; +}): Promise<{ + instruction: TransactionInstruction; + transaction: Transaction; +}> { + const program = new Program( + worldIdl as Idl + ) as unknown as Program; + const worldInstance = await World.fromAccountAddress(connection, world); + const worldId = new BN(worldInstance.id); + const addAuthorityIx = await program.methods + .addAuthority(worldId) + .accounts({ + authority, + newAuthority, + world, + }) + .instruction(); + return { + instruction: addAuthorityIx, + transaction: new Transaction().add(addAuthorityIx), + }; +} + +/** + * Create the transaction to Remove an authority + * @param authority + * @param authorityToDelete + * @param world + * @param connection + * @constructor + */ +export async function RemoveAuthority({ + authority, + authorityToDelete, + world, + connection, +}: { + authority: PublicKey; + authorityToDelete: PublicKey; + world: PublicKey; + connection: Connection; +}): Promise<{ + instruction: TransactionInstruction; + transaction: Transaction; +}> { + const program = new Program( + worldIdl as Idl + ) as unknown as Program; + const worldInstance = await World.fromAccountAddress(connection, world); + const worldId = new BN(worldInstance.id); + const removeAuthorityIx = await program.methods + .removeAuthority(worldId) + .accounts({ + authority, + authorityToDelete, + world, + }) + .instruction(); + return { + instruction: removeAuthorityIx, + transaction: new Transaction().add(removeAuthorityIx), + }; +} + /** * Create the transaction to Add a new entity * @param payer diff --git a/programs/world/src/error.rs b/programs/world/src/error.rs index ec92468..980c651 100644 --- a/programs/world/src/error.rs +++ b/programs/world/src/error.rs @@ -6,4 +6,8 @@ pub enum WorldError { InvalidAuthority, #[msg("The provided world account does not match the expected PDA.")] WorldAccountMismatch, + #[msg("Exceed the maximum number of authorities.")] + TooManyAuthorities, + #[msg("The provided authority not found")] + AuthorityNotFound, } diff --git a/programs/world/src/lib.rs b/programs/world/src/lib.rs index 5d4ca64..fd42234 100644 --- a/programs/world/src/lib.rs +++ b/programs/world/src/lib.rs @@ -35,6 +35,45 @@ pub mod world { Ok(()) } + #[allow(unused_variables)] + pub fn add_authority(ctx: Context, world_id: u64) -> Result<()> { + if ctx.accounts.world.authorities.len() == 3 { + return Err(WorldError::TooManyAuthorities.into()); + } + if ctx.accounts.world.authorities.is_empty() + || ctx + .accounts + .world + .authorities + .contains(ctx.accounts.authority.key) + { + ctx.accounts + .world + .authorities + .push(*ctx.accounts.new_authority.key); + } + Ok(()) + } + + #[allow(unused_variables)] + pub fn remove_authority(ctx: Context, world_id: u64) -> Result<()> { + if !ctx.accounts.world.authorities.contains(ctx.accounts.authority.key) { + return Err(WorldError::InvalidAuthority.into()); + } + if let Some(index) = ctx + .accounts + .world + .authorities + .iter() + .position(|&x| x == *ctx.accounts.authority_to_delete.key) + { + ctx.accounts.world.authorities.remove(index); + Ok(()) + } else { + Err(WorldError::AuthorityNotFound.into()) + } + } + #[allow(unused_variables)] pub fn add_entity(ctx: Context, extra_seed: Option) -> Result<()> { require!( @@ -131,6 +170,30 @@ pub struct InitializeNewWorld<'info> { pub system_program: Program<'info, System>, } +#[derive(Accounts)] +#[instruction(world_id: u64)] +pub struct AddAuthority<'info> { + #[account(mut)] + pub authority: Signer<'info>, + #[account()] + /// CHECK: new authority check + pub new_authority: AccountInfo<'info>, + #[account(mut, seeds = [World::seed(), &world_id.to_be_bytes()], bump)] + pub world: Account<'info, World>, +} + +#[derive(Accounts)] +#[instruction(world_id: u64)] +pub struct RemoveAuthority<'info> { + #[account(mut)] + pub authority: Signer<'info>, + #[account()] + /// CHECK: new authority check + pub authority_to_delete: AccountInfo<'info>, + #[account(mut, seeds = [World::seed(), &world_id.to_be_bytes()], bump)] + pub world: Account<'info, World>, +} + #[derive(Accounts)] #[instruction(extra_seed: Option)] pub struct AddEntity<'info> { @@ -209,10 +272,12 @@ impl Registry { } #[account] -#[derive(InitSpace, Default, Copy)] +#[derive(InitSpace, Default)] pub struct World { pub id: u64, pub entities: u64, + #[max_len(3)] + pub authorities: Vec, } impl World { diff --git a/tests/bolt.ts b/tests/bolt.ts index 25b8dbb..942a4e3 100644 --- a/tests/bolt.ts +++ b/tests/bolt.ts @@ -1,27 +1,26 @@ import * as anchor from "@coral-xyz/anchor"; import { type Program, web3 } from "@coral-xyz/anchor"; -import { type PublicKey } from "@solana/web3.js"; +import { Keypair, type PublicKey } from "@solana/web3.js"; import { type Position } from "../target/types/position"; import { type Velocity } from "../target/types/velocity"; import { type BoltComponent } from "../target/types/bolt_component"; import { type SystemSimpleMovement } from "../target/types/system_simple_movement"; +import { type World } from "../target/types/world"; import { type SystemFly } from "../target/types/system_fly"; import { type SystemApplyVelocity } from "../target/types/system_apply_velocity"; import { expect } from "chai"; import type BN from "bn.js"; import { AddEntity, - createDelegateInstruction, - createUndelegateInstruction, createInitializeRegistryInstruction, DELEGATION_PROGRAM_ID, FindRegistryPda, InitializeComponent, InitializeNewWorld, ApplySystem, - createAllowUndelegationInstruction, + DelegateComponent, + AddAuthority, RemoveAuthority } from "../clients/bolt-sdk"; -import { DelegateComponent } from "../clients/bolt-sdk/src"; enum Direction { Left = "Left", @@ -68,6 +67,8 @@ describe("bolt", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); + const worldProgram = anchor.workspace.World as Program; + const boltComponentProgram = anchor.workspace .BoltComponent as Program; @@ -98,7 +99,9 @@ describe("bolt", () => { let componentPositionEntity4Pda: PublicKey; let componentPositionEntity5Pda: PublicKey; - it("InitializeRegistry", async () => { + const secondAuthority = Keypair.generate().publicKey; + + it.only("InitializeRegistry", async () => { const registryPda = FindRegistryPda({}); const initializeRegistryIx = createInitializeRegistryInstruction({ registry: registryPda, @@ -108,7 +111,7 @@ describe("bolt", () => { await provider.sendAndConfirm(tx); }); - it("InitializeNewWorld", async () => { + it.only("InitializeNewWorld", async () => { const initializeNewWorld = await InitializeNewWorld({ payer: provider.wallet.publicKey, connection: provider.connection, @@ -117,6 +120,50 @@ describe("bolt", () => { worldPda = initializeNewWorld.worldPda; // Saved for later }); + it.only("Add authority", async () => { + const addAuthority = await AddAuthority({ + authority: provider.wallet.publicKey, + newAuthority: provider.wallet.publicKey, + world: worldPda, + connection: provider.connection, + }); + await provider.sendAndConfirm(addAuthority.transaction, [], { + skipPreflight: true, + }); + const worldAccount = await worldProgram.account.world.fetch(worldPda); + expect( + worldAccount.authorities.some((auth) => + auth.equals(provider.wallet.publicKey) + ) + ); + }); + + it.only("Add a second authority", async () => { + const addAuthority = await AddAuthority({ + authority: provider.wallet.publicKey, + newAuthority: secondAuthority, + world: worldPda, + connection: provider.connection, + }); + const signature = await provider.sendAndConfirm(addAuthority.transaction); + console.log(`Add Authority signature: ${signature}`); + const worldAccount = await worldProgram.account.world.fetch(worldPda); + expect(worldAccount.authorities.some((auth) => auth.equals(secondAuthority))); + }); + + it.only("Remove an authority", async () => { + const addAuthority = await RemoveAuthority({ + authority: provider.wallet.publicKey, + authorityToDelete: secondAuthority, + world: worldPda, + connection: provider.connection, + }); + const signature = await provider.sendAndConfirm(addAuthority.transaction); + console.log(`Add Authority signature: ${signature}`); + const worldAccount = await worldProgram.account.world.fetch(worldPda); + expect(!worldAccount.authorities.some((auth) => auth.equals(secondAuthority))); + }); + it("InitializeNewWorld 2", async () => { const initializeNewWorld = await InitializeNewWorld({ payer: provider.wallet.publicKey,