lib.rs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. #![allow(clippy::manual_unwrap_or_default)]
  2. use anchor_lang::{prelude::*, system_program};
  3. use error::WorldError;
  4. use std::collections::BTreeSet;
  5. static CPI_AUTH_ADDRESS: Pubkey =
  6. Pubkey::from_str_const("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); // Seeds: ["cpi_auth", [251]]
  7. #[cfg(not(feature = "no-entrypoint"))]
  8. use solana_security_txt::security_txt;
  9. declare_id!("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n");
  10. #[cfg(not(feature = "no-entrypoint"))]
  11. security_txt! {
  12. name: "Bolt",
  13. project_url: "https://magicblock.gg",
  14. contacts: "email:dev@magicblock.gg,twitter:@magicblock",
  15. policy: "",
  16. preferred_languages: "en",
  17. source_code: "https://github.com/magicblock-labs/bolt"
  18. }
  19. mod error;
  20. #[program]
  21. pub mod world {
  22. use super::*;
  23. use crate::error::WorldError;
  24. pub fn initialize_registry(_ctx: Context<InitializeRegistry>) -> Result<()> {
  25. Ok(())
  26. }
  27. pub fn initialize_new_world(ctx: Context<InitializeNewWorld>) -> Result<()> {
  28. ctx.accounts.world.set_inner(World::default());
  29. ctx.accounts.world.id = ctx.accounts.registry.worlds;
  30. ctx.accounts.registry.worlds += 1;
  31. Ok(())
  32. }
  33. #[allow(unused_variables)]
  34. pub fn add_authority(ctx: Context<AddAuthority>, world_id: u64) -> Result<()> {
  35. if ctx.accounts.world.authorities.is_empty()
  36. || (ctx
  37. .accounts
  38. .world
  39. .authorities
  40. .contains(ctx.accounts.authority.key)
  41. && !ctx
  42. .accounts
  43. .world
  44. .authorities
  45. .contains(ctx.accounts.new_authority.key))
  46. {
  47. ctx.accounts
  48. .world
  49. .authorities
  50. .push(*ctx.accounts.new_authority.key);
  51. let new_space = World::space_for_authorities(
  52. ctx.accounts.world.authorities.len(),
  53. ctx.accounts.world.systems.len(),
  54. );
  55. // Transfer to make it rent exempt
  56. let rent = Rent::get()?;
  57. let new_minimum_balance = rent.minimum_balance(new_space);
  58. let lamports_diff =
  59. new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
  60. if lamports_diff > 0 {
  61. anchor_lang::solana_program::program::invoke(
  62. &anchor_lang::solana_program::system_instruction::transfer(
  63. ctx.accounts.authority.key,
  64. ctx.accounts.world.to_account_info().key,
  65. lamports_diff,
  66. ),
  67. &[
  68. ctx.accounts.authority.to_account_info(),
  69. ctx.accounts.world.to_account_info(),
  70. ctx.accounts.system_program.to_account_info(),
  71. ],
  72. )?;
  73. }
  74. ctx.accounts
  75. .world
  76. .to_account_info()
  77. .realloc(new_space, false)?;
  78. }
  79. Ok(())
  80. }
  81. #[allow(unused_variables)]
  82. pub fn remove_authority(ctx: Context<RemoveAuthority>, world_id: u64) -> Result<()> {
  83. if !ctx
  84. .accounts
  85. .world
  86. .authorities
  87. .contains(ctx.accounts.authority.key)
  88. {
  89. return Err(WorldError::InvalidAuthority.into());
  90. }
  91. if let Some(index) = ctx
  92. .accounts
  93. .world
  94. .authorities
  95. .iter()
  96. .position(|&x| x == *ctx.accounts.authority_to_delete.key)
  97. {
  98. ctx.accounts.world.authorities.remove(index);
  99. let new_space = World::space_for_authorities(
  100. ctx.accounts.world.authorities.len(),
  101. ctx.accounts.world.systems.len(),
  102. );
  103. // Remove the extra rent
  104. let rent = Rent::get()?;
  105. let new_minimum_balance = rent.minimum_balance(new_space);
  106. let lamports_diff =
  107. new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
  108. **ctx
  109. .accounts
  110. .world
  111. .to_account_info()
  112. .try_borrow_mut_lamports()? += lamports_diff;
  113. **ctx
  114. .accounts
  115. .authority
  116. .to_account_info()
  117. .try_borrow_mut_lamports()? -= lamports_diff;
  118. ctx.accounts
  119. .world
  120. .to_account_info()
  121. .realloc(new_space, false)?;
  122. Ok(())
  123. } else {
  124. Err(WorldError::AuthorityNotFound.into())
  125. }
  126. }
  127. pub fn approve_system(ctx: Context<ApproveSystem>) -> Result<()> {
  128. if !ctx.accounts.authority.is_signer {
  129. return Err(WorldError::InvalidAuthority.into());
  130. }
  131. if !ctx
  132. .accounts
  133. .world
  134. .authorities
  135. .contains(ctx.accounts.authority.key)
  136. {
  137. return Err(WorldError::InvalidAuthority.into());
  138. }
  139. if ctx.accounts.world.permissionless {
  140. ctx.accounts.world.permissionless = false;
  141. }
  142. let mut world_systems = ctx.accounts.world.systems();
  143. world_systems
  144. .approved_systems
  145. .insert(ctx.accounts.system.key());
  146. let encoded_world_systems = world_systems.try_to_vec()?;
  147. ctx.accounts.world.systems = encoded_world_systems.clone();
  148. let new_space = World::space_for_authorities(
  149. ctx.accounts.world.authorities.len(),
  150. encoded_world_systems.len(),
  151. );
  152. // Transfer to make it rent exempt
  153. let rent = Rent::get()?;
  154. let new_minimum_balance = rent.minimum_balance(new_space);
  155. let lamports_diff =
  156. new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
  157. if lamports_diff > 0 {
  158. anchor_lang::solana_program::program::invoke(
  159. &anchor_lang::solana_program::system_instruction::transfer(
  160. ctx.accounts.authority.key,
  161. ctx.accounts.world.to_account_info().key,
  162. lamports_diff,
  163. ),
  164. &[
  165. ctx.accounts.authority.to_account_info(),
  166. ctx.accounts.world.to_account_info(),
  167. ctx.accounts.system_program.to_account_info(),
  168. ],
  169. )?;
  170. }
  171. ctx.accounts
  172. .world
  173. .to_account_info()
  174. .realloc(new_space, false)?;
  175. msg!("Approved system: {:?}", world_systems);
  176. Ok(())
  177. }
  178. pub fn remove_system(ctx: Context<RemoveSystem>) -> Result<()> {
  179. if !ctx.accounts.authority.is_signer {
  180. return Err(WorldError::InvalidAuthority.into());
  181. }
  182. if !ctx
  183. .accounts
  184. .world
  185. .authorities
  186. .contains(ctx.accounts.authority.key)
  187. {
  188. return Err(WorldError::InvalidAuthority.into());
  189. }
  190. let mut world_systems = ctx.accounts.world.systems();
  191. world_systems
  192. .approved_systems
  193. .remove(&ctx.accounts.system.key());
  194. let encoded_world_systems = world_systems.try_to_vec()?;
  195. ctx.accounts.world.systems = encoded_world_systems.clone();
  196. let new_space = World::space_for_authorities(
  197. ctx.accounts.world.authorities.len(),
  198. encoded_world_systems.len(),
  199. );
  200. if world_systems.approved_systems.is_empty() {
  201. ctx.accounts.world.permissionless = true;
  202. }
  203. // Remove the extra rent
  204. let rent = Rent::get()?;
  205. let new_minimum_balance = rent.minimum_balance(new_space);
  206. let lamports_diff =
  207. new_minimum_balance.saturating_sub(ctx.accounts.world.to_account_info().lamports());
  208. **ctx
  209. .accounts
  210. .world
  211. .to_account_info()
  212. .try_borrow_mut_lamports()? += lamports_diff;
  213. **ctx
  214. .accounts
  215. .authority
  216. .to_account_info()
  217. .try_borrow_mut_lamports()? -= lamports_diff;
  218. ctx.accounts
  219. .world
  220. .to_account_info()
  221. .realloc(new_space, false)?;
  222. msg!("Approved system: {:?}", world_systems);
  223. Ok(())
  224. }
  225. #[allow(unused_variables)]
  226. pub fn add_entity(ctx: Context<AddEntity>, extra_seed: Option<Vec<u8>>) -> Result<()> {
  227. require!(
  228. ctx.accounts.world.key() == ctx.accounts.world.pda().0,
  229. WorldError::WorldAccountMismatch
  230. );
  231. ctx.accounts.entity.id = ctx.accounts.world.entities;
  232. ctx.accounts.world.entities += 1;
  233. Ok(())
  234. }
  235. pub fn initialize_component(ctx: Context<InitializeComponent>) -> Result<()> {
  236. if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
  237. return Err(WorldError::InvalidAuthority.into());
  238. }
  239. bolt_component::cpi::initialize(ctx.accounts.build(&[World::cpi_auth_seeds().as_slice()]))?;
  240. // Create the buffer account only if it does not already exist.
  241. // Subsequent applies reuse the same PDA and only reallocate its data.
  242. if ctx.accounts.buffer.lamports() == 0 {
  243. let size = 0;
  244. let lamports = Rent::get()?.minimum_balance(size);
  245. system_program::create_account(
  246. CpiContext::new_with_signer(
  247. ctx.accounts.system_program.to_account_info(),
  248. system_program::CreateAccount {
  249. from: ctx.accounts.payer.to_account_info(),
  250. to: ctx.accounts.buffer.to_account_info(),
  251. },
  252. &[&[b"buffer", ctx.accounts.data.key.as_ref(), &[ctx.bumps.buffer]]],
  253. ),
  254. lamports,
  255. size as u64,
  256. &ID,
  257. )?;
  258. }
  259. Ok(())
  260. }
  261. pub fn delegate_buffer(ctx: Context<DelegateBuffer>, commit_frequency_ms: u32, validator: Option<Pubkey>) -> Result<()> {
  262. let pda_seeds: &[&[u8]] = &[b"buffer", &ctx.accounts.component.key().to_bytes()];
  263. let del_accounts = ephemeral_rollups_sdk::cpi::DelegateAccounts {
  264. payer: &ctx.accounts.payer,
  265. pda: &ctx.accounts.component_buffer,
  266. owner_program: &ctx.accounts.owner_program,
  267. buffer: &ctx.accounts.buffer,
  268. delegation_record: &ctx.accounts.delegation_record,
  269. delegation_metadata: &ctx.accounts.delegation_metadata,
  270. delegation_program: &ctx.accounts.delegation_program,
  271. system_program: &ctx.accounts.system_program,
  272. };
  273. let config = ephemeral_rollups_sdk::cpi::DelegateConfig {
  274. commit_frequency_ms,
  275. validator,
  276. };
  277. ephemeral_rollups_sdk::cpi::delegate_account(
  278. del_accounts,
  279. pda_seeds,
  280. config,
  281. )?;
  282. Ok(())
  283. }
  284. #[derive(Accounts)]
  285. pub struct DelegateBuffer<'info> {
  286. pub payer: Signer<'info>,
  287. /// CHECK:
  288. #[account()]
  289. pub component: AccountInfo<'info>,
  290. /// CHECK:
  291. #[account(mut)]
  292. pub component_buffer: AccountInfo<'info>,
  293. /// CHECK:`
  294. pub owner_program: AccountInfo<'info>,
  295. /// CHECK:
  296. #[account(mut)]
  297. pub buffer: AccountInfo<'info>,
  298. /// CHECK:`
  299. #[account(mut)]
  300. pub delegation_record: AccountInfo<'info>,
  301. /// CHECK:`
  302. #[account(mut)]
  303. pub delegation_metadata: AccountInfo<'info>,
  304. /// CHECK:`
  305. pub delegation_program: AccountInfo<'info>,
  306. /// CHECK:`
  307. pub system_program: Program<'info, System>,
  308. }
  309. pub fn destroy_component(ctx: Context<DestroyComponent>) -> Result<()> {
  310. bolt_component::cpi::destroy(ctx.accounts.build(&[World::cpi_auth_seeds().as_slice()]))?;
  311. Ok(())
  312. }
  313. pub fn apply<'info>(
  314. ctx: Context<'_, '_, '_, 'info, Apply<'info>>,
  315. args: Vec<u8>,
  316. ) -> Result<()> {
  317. apply_impl(
  318. &ctx.accounts.buffer,
  319. &ctx.accounts.authority,
  320. &ctx.accounts.world,
  321. &ctx.accounts.bolt_system,
  322. &ctx.accounts.cpi_auth,
  323. ctx.accounts.build(),
  324. args,
  325. None,
  326. ctx.remaining_accounts,
  327. )?;
  328. Ok(())
  329. }
  330. #[derive(Accounts)]
  331. pub struct Apply<'info> {
  332. /// CHECK: buffer data check
  333. #[account(mut)]
  334. pub buffer: AccountInfo<'info>,
  335. /// CHECK: bolt system program check
  336. #[account()]
  337. pub bolt_system: UncheckedAccount<'info>,
  338. /// CHECK: authority check
  339. #[account(mut)]
  340. pub authority: Signer<'info>,
  341. #[account()]
  342. /// CHECK: cpi auth check
  343. pub cpi_auth: UncheckedAccount<'info>,
  344. #[account()]
  345. pub world: Account<'info, World>,
  346. pub system_program: Program<'info, System>,
  347. }
  348. impl<'info> Apply<'info> {
  349. pub fn build(
  350. &self,
  351. ) -> CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::BoltExecute<'info>> {
  352. let cpi_program = self.bolt_system.to_account_info();
  353. let cpi_accounts = bolt_system::cpi::accounts::BoltExecute {
  354. authority: self.authority.to_account_info(),
  355. };
  356. CpiContext::new(cpi_program, cpi_accounts)
  357. }
  358. }
  359. pub fn apply_with_session<'info>(
  360. ctx: Context<'_, '_, '_, 'info, ApplyWithSession<'info>>,
  361. args: Vec<u8>,
  362. ) -> Result<()> {
  363. apply_impl(
  364. &ctx.accounts.buffer,
  365. &ctx.accounts.authority,
  366. &ctx.accounts.world,
  367. &ctx.accounts.bolt_system,
  368. &ctx.accounts.cpi_auth,
  369. ctx.accounts.build(),
  370. args,
  371. Some(&ctx.accounts.session_token),
  372. ctx.remaining_accounts,
  373. )?;
  374. Ok(())
  375. }
  376. #[derive(Accounts)]
  377. pub struct ApplyWithSession<'info> {
  378. /// CHECK: buffer data check
  379. #[account(mut)]
  380. pub buffer: AccountInfo<'info>,
  381. /// CHECK: bolt system program check
  382. #[account()]
  383. pub bolt_system: UncheckedAccount<'info>,
  384. /// CHECK: authority check
  385. #[account(mut)]
  386. pub authority: Signer<'info>,
  387. #[account()]
  388. /// CHECK: cpi auth check
  389. pub cpi_auth: UncheckedAccount<'info>,
  390. #[account()]
  391. pub world: Account<'info, World>,
  392. #[account(constraint = session_token.to_account_info().owner == &session_keys::ID)]
  393. pub session_token: Account<'info, session_keys::SessionToken>,
  394. pub system_program: Program<'info, System>,
  395. }
  396. impl<'info> ApplyWithSession<'info> {
  397. pub fn build(
  398. &self,
  399. ) -> CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::BoltExecute<'info>> {
  400. let cpi_program = self.bolt_system.to_account_info();
  401. let cpi_accounts = bolt_system::cpi::accounts::BoltExecute {
  402. authority: self.authority.to_account_info(),
  403. };
  404. CpiContext::new(cpi_program, cpi_accounts)
  405. }
  406. }
  407. }
  408. #[allow(clippy::type_complexity, clippy::too_many_arguments)]
  409. fn apply_impl<'info>(
  410. buffer: &AccountInfo<'info>,
  411. authority: &Signer<'info>,
  412. world: &Account<'info, World>,
  413. bolt_system: &UncheckedAccount<'info>,
  414. cpi_auth: &UncheckedAccount<'info>,
  415. cpi_context: CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::BoltExecute<'info>>,
  416. args: Vec<u8>,
  417. session_token: Option<&Account<'info, session_keys::SessionToken>>,
  418. remaining_accounts: &[AccountInfo<'info>],
  419. ) -> Result<()> {
  420. if !authority.is_signer && authority.key != &ID {
  421. return Err(WorldError::InvalidAuthority.into());
  422. }
  423. if !world.permissionless
  424. && !world
  425. .systems()
  426. .approved_systems
  427. .contains(&bolt_system.key())
  428. {
  429. return Err(WorldError::SystemNotApproved.into());
  430. }
  431. require!(buffer.data_len() == 0, WorldError::InvalidBufferAccount);
  432. let index = remaining_accounts.iter().position(|x| x.key() == ID).unwrap_or(remaining_accounts.len());
  433. // Authority check against component metadata (partial deserialize)
  434. for component in remaining_accounts[..index].iter().skip(1).step_by(2) {
  435. let data_ref = component.try_borrow_data()?;
  436. // Expect at least Anchor discriminator (8) + BoltMetadata (32)
  437. if data_ref.len() < 8 + 32 {
  438. return Err(WorldError::InvalidAuthority.into());
  439. }
  440. // BoltMetadata.authority is the last 32 bytes of the serialized component
  441. let start = 8; // Skip the discriminator
  442. let mut key_bytes = [0u8; 32];
  443. key_bytes.copy_from_slice(&data_ref[start..start + 32]);
  444. let component_authority = Pubkey::new_from_array(key_bytes);
  445. let unix_timestamp = Clock::get()?.unix_timestamp;
  446. if let Some(session_token) = session_token {
  447. if component_authority == ID {
  448. require!(
  449. unix_timestamp < session_token.valid_until,
  450. session_keys::SessionError::InvalidToken
  451. );
  452. } else {
  453. let validity_ctx = session_keys::ValidityChecker {
  454. session_token: session_token.clone(),
  455. session_signer: authority.clone(),
  456. authority: component_authority,
  457. target_program: ID,
  458. };
  459. require!(
  460. session_token.validate(validity_ctx)?,
  461. session_keys::SessionError::InvalidToken
  462. );
  463. require_eq!(
  464. component_authority,
  465. session_token.authority,
  466. session_keys::SessionError::InvalidToken
  467. );
  468. }
  469. } else {
  470. require!(
  471. component_authority == ID
  472. || (component_authority == *authority.key && authority.is_signer),
  473. WorldError::InvalidAuthority
  474. );
  475. }
  476. }
  477. for pair in remaining_accounts[..index].chunks(2) {
  478. let [program, component] = pair else { continue };
  479. buffer.realloc(component.data_len(), false)?;
  480. {
  481. let mut data = buffer.try_borrow_mut_data()?;
  482. data.copy_from_slice(component.try_borrow_data()?.as_ref());
  483. }
  484. if component.owner != bolt_system.key {
  485. bolt_component::cpi::set_owner(
  486. CpiContext::new_with_signer(
  487. program.to_account_info(),
  488. bolt_component::cpi::accounts::SetOwner {
  489. cpi_auth: cpi_auth.to_account_info(),
  490. component: component.to_account_info(),
  491. },
  492. &[World::cpi_auth_seeds().as_slice()],
  493. ),
  494. *bolt_system.key,
  495. )?;
  496. bolt_system::cpi::set_data(CpiContext::new_with_signer(
  497. bolt_system.to_account_info(),
  498. bolt_system::cpi::accounts::SetData {
  499. cpi_auth: cpi_auth.to_account_info(),
  500. buffer: buffer.to_account_info(),
  501. component: component.to_account_info(),
  502. },
  503. &[World::cpi_auth_seeds().as_slice()],
  504. ))?;
  505. }
  506. }
  507. let cpi_remaining_accounts = remaining_accounts[..index].iter().skip(1).step_by(2).chain(remaining_accounts[index..].iter().skip(1)).cloned().collect::<Vec<_>>();
  508. bolt_system::cpi::bolt_execute(
  509. cpi_context.with_remaining_accounts(cpi_remaining_accounts),
  510. args,
  511. )?;
  512. for pair in remaining_accounts[..index].chunks(2) {
  513. let [program, component] = pair else { continue };
  514. buffer.realloc(component.data_len(), false)?;
  515. {
  516. let mut data = buffer.try_borrow_mut_data()?;
  517. data.copy_from_slice(component.try_borrow_data()?.as_ref());
  518. }
  519. if *component.owner != program.key() {
  520. bolt_system::cpi::set_owner(
  521. CpiContext::new_with_signer(
  522. bolt_system.to_account_info(),
  523. bolt_system::cpi::accounts::SetOwner {
  524. cpi_auth: cpi_auth.to_account_info(),
  525. component: component.to_account_info(),
  526. },
  527. &[World::cpi_auth_seeds().as_slice()],
  528. ),
  529. program.key(),
  530. )?;
  531. if *component.owner != program.key() {
  532. return Err(WorldError::InvalidComponentOwner.into());
  533. }
  534. bolt_component::cpi::set_data(CpiContext::new_with_signer(
  535. program.to_account_info(),
  536. bolt_component::cpi::accounts::SetData {
  537. cpi_auth: cpi_auth.to_account_info(),
  538. buffer: buffer.to_account_info(),
  539. component: component.to_account_info(),
  540. },
  541. &[World::cpi_auth_seeds().as_slice()],
  542. ))?;
  543. }
  544. }
  545. buffer.realloc(0, false)?;
  546. Ok(())
  547. }
  548. #[derive(Accounts)]
  549. pub struct InitializeRegistry<'info> {
  550. #[account(init, payer = payer, space = Registry::size(), seeds = [Registry::seed()], bump)]
  551. pub registry: Account<'info, Registry>,
  552. #[account(mut)]
  553. pub payer: Signer<'info>,
  554. pub system_program: Program<'info, System>,
  555. }
  556. #[derive(Accounts)]
  557. pub struct InitializeNewWorld<'info> {
  558. #[account(mut)]
  559. pub payer: Signer<'info>,
  560. #[account(init, payer = payer, space = World::size(), seeds = [World::seed(), &registry.worlds.to_be_bytes()], bump)]
  561. pub world: Account<'info, World>,
  562. #[account(mut, address = Registry::pda().0)]
  563. pub registry: Account<'info, Registry>,
  564. pub system_program: Program<'info, System>,
  565. }
  566. #[derive(Accounts)]
  567. #[instruction(world_id: u64)]
  568. pub struct AddAuthority<'info> {
  569. #[account(mut)]
  570. pub authority: Signer<'info>,
  571. #[account()]
  572. /// CHECK: new authority check
  573. pub new_authority: AccountInfo<'info>,
  574. #[account(mut, seeds = [World::seed(), &world_id.to_be_bytes()], bump)]
  575. pub world: Account<'info, World>,
  576. pub system_program: Program<'info, System>,
  577. }
  578. #[derive(Accounts)]
  579. #[instruction(world_id: u64)]
  580. pub struct RemoveAuthority<'info> {
  581. #[account(mut)]
  582. pub authority: Signer<'info>,
  583. #[account()]
  584. /// CHECK: new authority check
  585. pub authority_to_delete: AccountInfo<'info>,
  586. #[account(mut, seeds = [World::seed(), &world_id.to_be_bytes()], bump)]
  587. pub world: Account<'info, World>,
  588. pub system_program: Program<'info, System>,
  589. }
  590. #[derive(Accounts)]
  591. pub struct ApproveSystem<'info> {
  592. #[account(mut)]
  593. pub authority: Signer<'info>,
  594. #[account(mut)]
  595. pub world: Account<'info, World>,
  596. /// CHECK: Used for the pda derivation
  597. pub system: AccountInfo<'info>,
  598. pub system_program: Program<'info, System>,
  599. }
  600. #[derive(Accounts)]
  601. pub struct RemoveSystem<'info> {
  602. #[account(mut)]
  603. pub authority: Signer<'info>,
  604. #[account(mut)]
  605. pub world: Account<'info, World>,
  606. /// CHECK: Used for the pda derivation
  607. pub system: AccountInfo<'info>,
  608. pub system_program: Program<'info, System>,
  609. }
  610. #[derive(Accounts)]
  611. #[instruction(extra_seed: Option<Vec<u8>>)]
  612. pub struct AddEntity<'info> {
  613. #[account(mut)]
  614. pub payer: Signer<'info>,
  615. #[account(init, payer = payer, space = World::size(), seeds = [Entity::seed(), &world.id.to_be_bytes(),
  616. &match extra_seed {
  617. Some(ref _seed) => [0; 8],
  618. None => world.entities.to_be_bytes()
  619. },
  620. match extra_seed {
  621. Some(ref seed) => seed,
  622. None => &[],
  623. }], bump)]
  624. pub entity: Account<'info, Entity>,
  625. #[account(mut)]
  626. pub world: Account<'info, World>,
  627. pub system_program: Program<'info, System>,
  628. }
  629. #[derive(Accounts)]
  630. pub struct InitializeComponent<'info> {
  631. #[account(mut)]
  632. pub payer: Signer<'info>,
  633. #[account(mut)]
  634. /// CHECK: component data check
  635. pub data: AccountInfo<'info>,
  636. #[account()]
  637. pub entity: Account<'info, Entity>,
  638. /// CHECK: component program check
  639. pub component_program: AccountInfo<'info>,
  640. /// CHECK: authority check
  641. pub authority: AccountInfo<'info>,
  642. #[account()]
  643. /// CHECK: cpi auth check
  644. pub cpi_auth: UncheckedAccount<'info>,
  645. pub system_program: Program<'info, System>,
  646. /// CHECK: Buffer account
  647. #[account(mut, seeds = [b"buffer", data.key.as_ref()], bump)]
  648. pub buffer: UncheckedAccount<'info>
  649. }
  650. impl<'info> InitializeComponent<'info> {
  651. pub fn build<'a, 'b, 'c>(
  652. &self,
  653. signer_seeds: &'a [&'b [&'c [u8]]],
  654. ) -> CpiContext<'a, 'b, 'c, 'info, bolt_component::cpi::accounts::Initialize<'info>> {
  655. let cpi_program = self.component_program.to_account_info();
  656. let cpi_accounts = bolt_component::cpi::accounts::Initialize {
  657. payer: self.payer.to_account_info(),
  658. data: self.data.to_account_info(),
  659. entity: self.entity.to_account_info(),
  660. authority: self.authority.to_account_info(),
  661. cpi_auth: self.cpi_auth.to_account_info(),
  662. system_program: self.system_program.to_account_info(),
  663. };
  664. CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds)
  665. }
  666. }
  667. #[derive(Accounts)]
  668. pub struct DestroyComponent<'info> {
  669. #[account(mut)]
  670. pub authority: Signer<'info>,
  671. #[account(mut)]
  672. /// CHECK: receiver check
  673. pub receiver: AccountInfo<'info>,
  674. /// CHECK: component program check
  675. pub component_program: AccountInfo<'info>,
  676. /// CHECK: component program data check
  677. pub component_program_data: AccountInfo<'info>,
  678. #[account()]
  679. pub entity: Account<'info, Entity>,
  680. #[account(mut)]
  681. /// CHECK: component data check
  682. pub component: UncheckedAccount<'info>,
  683. #[account()]
  684. /// CHECK: cpi auth check
  685. pub cpi_auth: UncheckedAccount<'info>,
  686. pub system_program: Program<'info, System>,
  687. }
  688. impl<'info> DestroyComponent<'info> {
  689. pub fn build<'a, 'b, 'c>(
  690. &self,
  691. signer_seeds: &'a [&'b [&'c [u8]]],
  692. ) -> CpiContext<'a, 'b, 'c, 'info, bolt_component::cpi::accounts::Destroy<'info>> {
  693. let cpi_program = self.component_program.to_account_info();
  694. let cpi_accounts = bolt_component::cpi::accounts::Destroy {
  695. authority: self.authority.to_account_info(),
  696. receiver: self.receiver.to_account_info(),
  697. entity: self.entity.to_account_info(),
  698. component: self.component.to_account_info(),
  699. component_program_data: self.component_program_data.to_account_info(),
  700. cpi_auth: self.cpi_auth.to_account_info(),
  701. system_program: self.system_program.to_account_info(),
  702. };
  703. CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds)
  704. }
  705. }
  706. #[account]
  707. #[derive(InitSpace, Default, Copy)]
  708. pub struct Registry {
  709. pub worlds: u64,
  710. }
  711. impl Registry {
  712. pub fn seed() -> &'static [u8] {
  713. b"registry"
  714. }
  715. pub fn size() -> usize {
  716. 8 + Registry::INIT_SPACE
  717. }
  718. pub fn pda() -> (Pubkey, u8) {
  719. Pubkey::find_program_address(&[Registry::seed()], &crate::ID)
  720. }
  721. }
  722. #[account]
  723. #[derive(Debug)]
  724. pub struct World {
  725. pub id: u64,
  726. pub entities: u64,
  727. pub authorities: Vec<Pubkey>,
  728. pub permissionless: bool,
  729. pub systems: Vec<u8>,
  730. }
  731. impl Default for World {
  732. fn default() -> Self {
  733. Self {
  734. id: 0,
  735. entities: 0,
  736. authorities: Vec::new(),
  737. permissionless: true,
  738. systems: Vec::new(),
  739. }
  740. }
  741. }
  742. impl World {
  743. fn space_for_authorities(auths: usize, systems_space: usize) -> usize {
  744. 16 + 8 + 32 * auths + 1 + 8 + systems_space
  745. }
  746. pub fn systems(&self) -> WorldSystems {
  747. if self.permissionless {
  748. return WorldSystems::default();
  749. }
  750. WorldSystems::try_from_slice(self.systems.as_ref()).unwrap_or_default()
  751. }
  752. }
  753. #[derive(
  754. anchor_lang::prelude::borsh::BorshSerialize,
  755. anchor_lang::prelude::borsh::BorshDeserialize,
  756. Default,
  757. Debug,
  758. )]
  759. pub struct WorldSystems {
  760. pub approved_systems: BTreeSet<Pubkey>,
  761. }
  762. impl World {
  763. pub fn seed() -> &'static [u8] {
  764. b"world"
  765. }
  766. pub fn size() -> usize {
  767. 16 + 8 + 1 + 8
  768. }
  769. pub fn pda(&self) -> (Pubkey, u8) {
  770. Pubkey::find_program_address(&[World::seed(), &self.id.to_be_bytes()], &crate::ID)
  771. }
  772. pub const fn cpi_auth_seeds() -> [&'static [u8]; 2] {
  773. [b"cpi_auth", &[251]] // 251 is the pre-computed bump for cpi_auth.
  774. }
  775. pub const fn cpi_auth_address() -> &'static Pubkey {
  776. &CPI_AUTH_ADDRESS // This is the pre-computed address for cpi_auth.
  777. }
  778. }
  779. #[account]
  780. #[derive(InitSpace, Default, Copy)]
  781. pub struct Entity {
  782. pub id: u64,
  783. }
  784. impl Entity {
  785. pub fn seed() -> &'static [u8] {
  786. b"entity"
  787. }
  788. }
  789. #[account]
  790. #[derive(InitSpace, Default)]
  791. pub struct SystemWhitelist {}
  792. impl SystemWhitelist {
  793. pub fn seed() -> &'static [u8] {
  794. b"whitelist"
  795. }
  796. pub fn size() -> usize {
  797. 8 + Registry::INIT_SPACE
  798. }
  799. }