lib.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. use anchor_lang::prelude::*;
  2. use bolt_helpers_world_apply::apply_system;
  3. use tuple_conv::RepeatedTuple;
  4. #[cfg(not(feature = "no-entrypoint"))]
  5. use solana_security_txt::security_txt;
  6. declare_id!("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n");
  7. #[cfg(not(feature = "no-entrypoint"))]
  8. security_txt! {
  9. name: "Bolt",
  10. project_url: "https://magicblock.gg",
  11. contacts: "email:dev@magicblock.gg,twitter:@magicblock",
  12. policy: "",
  13. preferred_languages: "en",
  14. source_code: "https://github.com/magicblock-labs/bolt"
  15. }
  16. mod error;
  17. #[apply_system(max_components = 5)]
  18. #[program]
  19. pub mod world {
  20. use super::*;
  21. use crate::error::WorldError;
  22. pub fn initialize_registry(_ctx: Context<InitializeRegistry>) -> Result<()> {
  23. Ok(())
  24. }
  25. pub fn initialize_new_world(ctx: Context<InitializeNewWorld>) -> Result<()> {
  26. ctx.accounts.world.id = ctx.accounts.registry.worlds;
  27. ctx.accounts.registry.worlds += 1;
  28. Ok(())
  29. }
  30. #[allow(unused_variables)]
  31. pub fn add_entity(ctx: Context<AddEntity>, extra_seed: Option<String>) -> Result<()> {
  32. require!(
  33. ctx.accounts.world.key() == ctx.accounts.world.pda().0,
  34. WorldError::WorldAccountMismatch
  35. );
  36. ctx.accounts.entity.id = ctx.accounts.world.entities;
  37. ctx.accounts.world.entities += 1;
  38. Ok(())
  39. }
  40. pub fn initialize_component(ctx: Context<InitializeComponent>) -> Result<()> {
  41. if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
  42. return Err(WorldError::InvalidAuthority.into());
  43. }
  44. bolt_component::cpi::initialize(ctx.accounts.build())?;
  45. Ok(())
  46. }
  47. pub fn apply<'info>(
  48. ctx: Context<'_, '_, '_, 'info, ApplySystem<'info>>,
  49. args: Vec<u8>,
  50. ) -> Result<()> {
  51. if !ctx.accounts.authority.is_signer && ctx.accounts.authority.key != &ID {
  52. return Err(WorldError::InvalidAuthority.into());
  53. }
  54. let remaining_accounts: Vec<AccountInfo<'info>> = ctx.remaining_accounts.to_vec();
  55. let res = bolt_system::cpi::execute(
  56. ctx.accounts
  57. .build()
  58. .with_remaining_accounts(remaining_accounts),
  59. args,
  60. )?;
  61. bolt_component::cpi::update(
  62. build_update_context(
  63. ctx.accounts.component_program.clone(),
  64. ctx.accounts.bolt_component.clone(),
  65. ctx.accounts.authority.clone(),
  66. ctx.accounts.instruction_sysvar_account.clone(),
  67. ),
  68. res.get(),
  69. )?;
  70. Ok(())
  71. }
  72. #[derive(Accounts)]
  73. pub struct ApplySystem<'info> {
  74. /// CHECK: bolt component program check
  75. pub component_program: UncheckedAccount<'info>,
  76. /// CHECK: bolt system program check
  77. pub bolt_system: UncheckedAccount<'info>,
  78. #[account(mut)]
  79. /// CHECK: component account
  80. pub bolt_component: UncheckedAccount<'info>,
  81. /// CHECK: authority check
  82. pub authority: Signer<'info>,
  83. #[account(address = anchor_lang::solana_program::sysvar::instructions::id())]
  84. /// CHECK: instruction sysvar check
  85. pub instruction_sysvar_account: UncheckedAccount<'info>,
  86. }
  87. impl<'info> ApplySystem<'info> {
  88. pub fn build(
  89. &self,
  90. ) -> CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::SetData<'info>> {
  91. let cpi_program = self.bolt_system.to_account_info();
  92. let cpi_accounts = bolt_system::cpi::accounts::SetData {
  93. component: self.bolt_component.to_account_info(),
  94. authority: self.authority.to_account_info(),
  95. };
  96. CpiContext::new(cpi_program, cpi_accounts)
  97. }
  98. }
  99. }
  100. #[derive(Accounts)]
  101. pub struct InitializeRegistry<'info> {
  102. #[account(init, payer = payer, space = Registry::size(), seeds = [Registry::seed()], bump)]
  103. pub registry: Account<'info, Registry>,
  104. #[account(mut)]
  105. pub payer: Signer<'info>,
  106. pub system_program: Program<'info, System>,
  107. }
  108. #[derive(Accounts)]
  109. pub struct InitializeNewWorld<'info> {
  110. #[account(mut)]
  111. pub payer: Signer<'info>,
  112. #[account(init, payer = payer, space = World::size(), seeds = [World::seed(), &registry.worlds.to_be_bytes()], bump)]
  113. pub world: Account<'info, World>,
  114. #[account(mut, address = Registry::pda().0)]
  115. pub registry: Account<'info, Registry>,
  116. pub system_program: Program<'info, System>,
  117. }
  118. #[derive(Accounts)]
  119. #[instruction(extra_seed: Option<String>)]
  120. pub struct AddEntity<'info> {
  121. #[account(mut)]
  122. pub payer: Signer<'info>,
  123. #[account(init, payer = payer, space = World::size(), seeds = [Entity::seed(), &world.id.to_be_bytes(),
  124. &match extra_seed {
  125. Some(ref seed) => [0; 8],
  126. None => world.entities.to_be_bytes()
  127. },
  128. match extra_seed {
  129. Some(ref seed) => seed.as_bytes(),
  130. None => &[],
  131. }], bump)]
  132. pub entity: Account<'info, Entity>,
  133. #[account(mut)]
  134. pub world: Account<'info, World>,
  135. pub system_program: Program<'info, System>,
  136. }
  137. #[derive(Accounts)]
  138. pub struct InitializeComponent<'info> {
  139. #[account(mut)]
  140. pub payer: Signer<'info>,
  141. #[account(mut)]
  142. /// CHECK: component data check
  143. pub data: AccountInfo<'info>,
  144. #[account()]
  145. pub entity: Account<'info, Entity>,
  146. /// CHECK: component program check
  147. pub component_program: AccountInfo<'info>,
  148. /// CHECK: authority check
  149. pub authority: AccountInfo<'info>,
  150. #[account(address = anchor_lang::solana_program::sysvar::instructions::id())]
  151. /// CHECK: instruction sysvar check
  152. pub instruction_sysvar_account: UncheckedAccount<'info>,
  153. pub system_program: Program<'info, System>,
  154. }
  155. impl<'info> InitializeComponent<'info> {
  156. pub fn build(
  157. &self,
  158. ) -> CpiContext<'_, '_, '_, 'info, bolt_component::cpi::accounts::Initialize<'info>> {
  159. let cpi_program = self.component_program.to_account_info();
  160. let cpi_accounts = bolt_component::cpi::accounts::Initialize {
  161. payer: self.payer.to_account_info(),
  162. data: self.data.to_account_info(),
  163. entity: self.entity.to_account_info(),
  164. authority: self.authority.to_account_info(),
  165. instruction_sysvar_account: self.instruction_sysvar_account.to_account_info(),
  166. system_program: self.system_program.to_account_info(),
  167. };
  168. CpiContext::new(cpi_program, cpi_accounts)
  169. }
  170. }
  171. #[account]
  172. #[derive(InitSpace, Default, Copy)]
  173. pub struct Registry {
  174. pub worlds: u64,
  175. }
  176. impl Registry {
  177. pub fn seed() -> &'static [u8] {
  178. b"registry"
  179. }
  180. pub fn size() -> usize {
  181. 8 + Registry::INIT_SPACE
  182. }
  183. pub fn pda() -> (Pubkey, u8) {
  184. Pubkey::find_program_address(&[Registry::seed()], &crate::ID)
  185. }
  186. }
  187. #[account]
  188. #[derive(InitSpace, Default, Copy)]
  189. pub struct World {
  190. pub id: u64,
  191. pub entities: u64,
  192. }
  193. impl World {
  194. pub fn seed() -> &'static [u8] {
  195. b"world"
  196. }
  197. pub fn size() -> usize {
  198. 8 + World::INIT_SPACE
  199. }
  200. pub fn pda(&self) -> (Pubkey, u8) {
  201. Pubkey::find_program_address(&[World::seed(), &self.id.to_be_bytes()], &crate::ID)
  202. }
  203. }
  204. #[account]
  205. #[derive(InitSpace, Default, Copy)]
  206. pub struct Entity {
  207. pub id: u64,
  208. }
  209. impl Entity {
  210. pub fn seed() -> &'static [u8] {
  211. b"entity"
  212. }
  213. }
  214. /// Builds the context for updating a component.
  215. pub fn build_update_context<'info>(
  216. component_program: UncheckedAccount<'info>,
  217. component: UncheckedAccount<'info>,
  218. authority: Signer<'info>,
  219. instruction_sysvar_account: UncheckedAccount<'info>,
  220. ) -> CpiContext<'info, 'info, 'info, 'info, bolt_component::cpi::accounts::Update<'info>> {
  221. let cpi_program = component_program.to_account_info();
  222. let cpi_accounts = bolt_component::cpi::accounts::Update {
  223. bolt_component: component.to_account_info(),
  224. authority: authority.to_account_info(),
  225. instruction_sysvar_account: instruction_sysvar_account.to_account_info(),
  226. };
  227. CpiContext::new(cpi_program, cpi_accounts)
  228. }