Skip to content

Commit

Permalink
feat: Implement approval check
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielePicco committed Oct 4, 2024
1 parent b43c9b6 commit e647681
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 49 deletions.
5 changes: 5 additions & 0 deletions clients/bolt-sdk/src/generated/idl/world.json
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,11 @@
"code": 6003,
"name": "AuthorityNotFound",
"msg": "The provided authority not found"
},
{
"code": 6004,
"name": "SystemNotApproved",
"msg": "The system is not approved in this world instance"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions clients/bolt-sdk/src/generated/types/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,11 @@ export interface World {
code: 6003;
name: "authorityNotFound";
msg: "The provided authority not found";
},
{
code: 6004;
name: "systemNotApproved";
msg: "The system is not approved in this world instance";
}
];
types: [
Expand Down
45 changes: 19 additions & 26 deletions clients/bolt-sdk/src/world/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {
createAddEntityInstruction,
createApply2Instruction,
createApply3Instruction,
createApply4Instruction,
createApply5Instruction,
createApplyInstruction,
createInitializeComponentInstruction,
createInitializeNewWorldInstruction,
FindComponentPda,
Expand Down Expand Up @@ -312,12 +307,12 @@ interface ApplySystemInstruction {
authority: PublicKey;
systemId: PublicKey;
entities: ApplySystemEntity[];
world: PublicKey;
extraAccounts?: web3.AccountMeta[];
args?: object;
}
function getApplyInstructionFunctionName(componentsCount: number) {
if (componentsCount === 1) return "createApplyInstruction";
return `createApply${componentsCount}Instruction`;
return `apply${componentsCount > 1 ? componentsCount : ""}`;
}
function getBoltComponentName(index: number, componentsCount: number) {
if (componentsCount === 1) return "boltComponent";
Expand All @@ -327,13 +322,14 @@ function getBoltComponentProgramName(index: number, componentsCount: number) {
if (componentsCount === 1) return "componentProgram";
return `componentProgram${index + 1}`;
}
function createApplySystemInstruction({
async function createApplySystemInstruction({
authority,
systemId,
entities,
world,
extraAccounts,
args,
}: ApplySystemInstruction): web3.TransactionInstruction {
}: ApplySystemInstruction): Promise<web3.TransactionInstruction> {
const program = new Program(
worldIdl as Idl
) as unknown as Program<WorldProgram>;
Expand All @@ -350,11 +346,11 @@ function createApplySystemInstruction({
);
}

const instructionArgs = {
const applyAccounts = {
authority: authority ?? PROGRAM_ID,
boltSystem: systemId,
instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
anchorRemainingAccounts: extraAccounts,
world,
};

let componentIndex = 0;
Expand All @@ -365,26 +361,20 @@ function createApplySystemInstruction({
entity: entity.entity,
seed: component.seed,
});
instructionArgs[
applyAccounts[
getBoltComponentProgramName(componentIndex, componentCount)
] = component.componentId;
instructionArgs[getBoltComponentName(componentIndex, componentCount)] =
applyAccounts[getBoltComponentName(componentIndex, componentCount)] =
componentPda;
componentIndex++;
});
});

const instructionFunctions = {
createApplyInstruction,
createApply2Instruction,
createApply3Instruction,
createApply4Instruction,
createApply5Instruction,
};
const functionName = getApplyInstructionFunctionName(componentCount);
return instructionFunctions[functionName](instructionArgs, {
args: SerializeArgs(args),
});
return program.methods[getApplyInstructionFunctionName(componentCount)](
SerializeArgs(args)
)
.accounts(applyAccounts)
.remainingAccounts(extraAccounts ?? [])
.instruction();
}

interface ApplySystemEntity {
Expand All @@ -409,19 +399,22 @@ export async function ApplySystem({
authority,
systemId,
entities,
world,
extraAccounts,
args,
}: {
authority: PublicKey;
systemId: PublicKey;
entities: ApplySystemEntity[];
world: PublicKey;
extraAccounts?: web3.AccountMeta[];
args?: object;
}): Promise<{ instruction: TransactionInstruction; transaction: Transaction }> {
const applySystemIx = createApplySystemInstruction({
const applySystemIx = await createApplySystemInstruction({
authority,
systemId,
entities,
world,
extraAccounts,
args,
});
Expand Down
2 changes: 2 additions & 0 deletions programs/world/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ pub enum WorldError {
TooManyAuthorities,
#[msg("The provided authority not found")]
AuthorityNotFound,
#[msg("The system is not approved in this world instance")]
SystemNotApproved,
}
95 changes: 75 additions & 20 deletions programs/world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod world {
}

pub fn initialize_new_world(ctx: Context<InitializeNewWorld>) -> Result<()> {
ctx.accounts.world.set_inner(World::default());
ctx.accounts.world.id = ctx.accounts.registry.worlds;
ctx.accounts.registry.worlds += 1;
Ok(())
Expand Down Expand Up @@ -61,13 +62,14 @@ pub mod world {
// Transfer to make it rent exempt
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_space);
let lamports_diff = new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
let lamports_diff =
new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
if lamports_diff > 0 {
anchor_lang::solana_program::program::invoke(
&anchor_lang::solana_program::system_instruction::transfer(
ctx.accounts.authority.key,
ctx.accounts.world.to_account_info().key,
lamports_diff
lamports_diff,
),
&[
ctx.accounts.authority.to_account_info(),
Expand All @@ -76,7 +78,10 @@ pub mod world {
],
)?;
}
ctx.accounts.world.to_account_info().realloc(new_space, false)?;
ctx.accounts
.world
.to_account_info()
.realloc(new_space, false)?;
}
Ok(())
}
Expand Down Expand Up @@ -108,10 +113,22 @@ pub mod world {
// Remove the extra rent
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_space);
let lamports_diff = new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
**ctx.accounts.world.to_account_info().try_borrow_mut_lamports()? += lamports_diff;
**ctx.accounts.authority.to_account_info().try_borrow_mut_lamports()? -= lamports_diff;
ctx.accounts.world.to_account_info().realloc(new_space, false)?;
let lamports_diff =
new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
**ctx
.accounts
.world
.to_account_info()
.try_borrow_mut_lamports()? += lamports_diff;
**ctx
.accounts
.authority
.to_account_info()
.try_borrow_mut_lamports()? -= lamports_diff;
ctx.accounts
.world
.to_account_info()
.realloc(new_space, false)?;
Ok(())
} else {
Err(WorldError::AuthorityNotFound.into())
Expand All @@ -134,8 +151,10 @@ pub mod world {
ctx.accounts.world.permissionless = false;
}

let mut world_systems = WorldSystems::try_from_slice(ctx.accounts.world.systems.as_ref()).unwrap_or_default();
world_systems.approved_systems.insert(ctx.accounts.system.key());
let mut world_systems = ctx.accounts.world.systems();
world_systems
.approved_systems
.insert(ctx.accounts.system.key());

let encoded_world_systems = world_systems.try_to_vec()?;
ctx.accounts.world.systems = encoded_world_systems.clone();
Expand All @@ -148,13 +167,14 @@ pub mod world {
// Transfer to make it rent exempt
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_space);
let lamports_diff = new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
let lamports_diff =
new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
if lamports_diff > 0 {
anchor_lang::solana_program::program::invoke(
&anchor_lang::solana_program::system_instruction::transfer(
ctx.accounts.authority.key,
ctx.accounts.world.to_account_info().key,
lamports_diff
lamports_diff,
),
&[
ctx.accounts.authority.to_account_info(),
Expand All @@ -163,12 +183,14 @@ pub mod world {
],
)?;
}
ctx.accounts.world.to_account_info().realloc(new_space, false)?;
ctx.accounts
.world
.to_account_info()
.realloc(new_space, false)?;
msg!("Approved system: {:?}", world_systems);
Ok(())
}


pub fn remove_system(ctx: Context<RemoveSystem>) -> Result<()> {
if !ctx.accounts.authority.is_signer {
return Err(WorldError::InvalidAuthority.into());
Expand All @@ -182,8 +204,10 @@ pub mod world {
return Err(WorldError::InvalidAuthority.into());
}

let mut world_systems = WorldSystems::try_from_slice(ctx.accounts.world.systems.as_ref()).unwrap_or_default();
world_systems.approved_systems.remove(&ctx.accounts.system.key());
let mut world_systems = ctx.accounts.world.systems();
world_systems
.approved_systems
.remove(&ctx.accounts.system.key());

let encoded_world_systems = world_systems.try_to_vec()?;
ctx.accounts.world.systems = encoded_world_systems.clone();
Expand All @@ -196,10 +220,22 @@ pub mod world {
// Remove the extra rent
let rent = Rent::get()?;
let new_minimum_balance = rent.minimum_balance(new_space);
let lamports_diff = new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
**ctx.accounts.world.to_account_info().try_borrow_mut_lamports()? += lamports_diff;
**ctx.accounts.authority.to_account_info().try_borrow_mut_lamports()? -= lamports_diff;
ctx.accounts.world.to_account_info().realloc(new_space, false)?;
let lamports_diff =
new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
**ctx
.accounts
.world
.to_account_info()
.try_borrow_mut_lamports()? += lamports_diff;
**ctx
.accounts
.authority
.to_account_info()
.try_borrow_mut_lamports()? -= lamports_diff;
ctx.accounts
.world
.to_account_info()
.realloc(new_space, false)?;
msg!("Approved system: {:?}", world_systems);
Ok(())
}
Expand Down Expand Up @@ -230,6 +266,18 @@ pub mod world {
if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
return Err(WorldError::InvalidAuthority.into());
}
msg!("Applying system");
msg!("Permisionless: {:?}", ctx.accounts.world.permissionless);
if !ctx.accounts.world.permissionless
&& !ctx
.accounts
.world
.systems()
.approved_systems
.contains(&ctx.accounts.bolt_system.key())
{
return Err(WorldError::SystemNotApproved.into());
}
let remaining_accounts: Vec<AccountInfo<'info>> = ctx.remaining_accounts.to_vec();
let res = bolt_system::cpi::execute(
ctx.accounts
Expand Down Expand Up @@ -453,13 +501,20 @@ impl World {
fn space_for_authorities(auths: usize, systems_space: usize) -> usize {
16 + 8 + 32 * auths + 1 + 8 + systems_space
}

pub fn systems(&self) -> WorldSystems {
if self.permissionless {
return WorldSystems::default();
}
WorldSystems::try_from_slice(self.systems.as_ref()).unwrap_or_default()
}
}

#[derive(
anchor_lang::prelude::borsh::BorshSerialize,
anchor_lang::prelude::borsh::BorshDeserialize,
Default,
Debug
Debug,
)]
pub struct WorldSystems {
pub approved_systems: BTreeSet<Pubkey>,
Expand Down
Loading

0 comments on commit e647681

Please sign in to comment.