repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | use anchor_lang::prelude::ProgramError; |
| #2 | use anchor_lang::prelude::*; |
| #3 | use anchor_lang::solana_program::instruction::Instruction; |
| #4 | use anchor_lang::solana_program::program::invoke_signed; |
| #5 | use ephemeral_rollups_sdk::anchor::{delegate, ephemeral}; |
| #6 | use ephemeral_rollups_sdk::cpi::DelegateConfig; |
| #7 | |
| #8 | declare_id!("LLMrieZMpbJFwN52WgmBNMxYojrpRVYXdC1RCweEbab"); |
| #9 | |
| #10 | const ORACLE_IDENTITY: Pubkey = pubkey!("A1ooMmN1fz6LbEFrjh6GukFS2ZeRYFzdyFjeafyyS7Ca"); |
| #11 | |
| #12 | #[ephemeral] |
| #13 | #[program] |
| #14 | pub mod solana_gpt_oracle { |
| #15 | use super::*; |
| #16 | |
| #17 | pub fn initialize(_ctx: Context<Initialize>) -> Result<()> { |
| #18 | Ok(()) |
| #19 | } |
| #20 | |
| #21 | pub fn create_llm_context(ctx: Context<CreateLlmContext>, text: String) -> Result<()> { |
| #22 | let context_account = &mut ctx.accounts.context_account; |
| #23 | context_account.text = text; |
| #24 | ctx.accounts.counter.count += 1; |
| #25 | Ok(()) |
| #26 | } |
| #27 | |
| #28 | pub fn interact_with_llm( |
| #29 | ctx: Context<InteractWithLlm>, |
| #30 | text: String, |
| #31 | callback_program_id: Pubkey, |
| #32 | callback_discriminator: [u8; 8], |
| #33 | account_metas: Option<Vec<AccountMeta>>, |
| #34 | ) -> Result<()> { |
| #35 | let interaction = &mut ctx.accounts.interaction; |
| #36 | let current_len = interaction.to_account_info().data_len(); |
| #37 | let space = Interaction::space(&text, account_metas.as_ref().map_or(0, |m| m.len())); |
| #38 | let rent = Rent::get()?; |
| #39 | |
| #40 | let mut additional_rent = rent.minimum_balance(space); |
| #41 | |
| #42 | let interaction_info = interaction.to_account_info(); |
| #43 | let payer_info = ctx.accounts.payer.to_account_info(); |
| #44 | let system_program_info = ctx.accounts.system_program.to_account_info(); |
| #45 | |
| #46 | if interaction_info.owner.eq(&anchor_lang::system_program::ID) { |
| #47 | let create_instruction = |
| #48 | anchor_lang::solana_program::system_instruction::create_account( |
| #49 | &ctx.accounts.payer.key(), |
| #50 | &interaction.key(), |
| #51 | additional_rent, |
| #52 | space as u64, |
| #53 | &crate::ID, |
| #54 | ); |
| #55 | |
| #56 | let payer = ctx.accounts.payer.key(); |
| #57 | let context_account = ctx.accounts.context_account.key(); |
| #58 | let signer_seeds: &[&[&[u8]]] = &[&[ |
| #59 | Interaction::seed(), |
| #60 | payer.as_ref(), |
| #61 | context_account.as_ref(), |
| #62 | &[ctx.bumps.interaction], |
| #63 | ]]; |
| #64 | |
| #65 | anchor_lang::solana_program::program::invoke_signed( |
| #66 | &create_instruction, |
| #67 | &[ |
| #68 | payer_info.clone(), |
| #69 | interaction_info.clone(), |
| #70 | system_program_info.clone(), |
| #71 | ], |
| #72 | signer_seeds, |
| #73 | )?; |
| #74 | } else { |
| #75 | additional_rent = additional_rent.saturating_sub(rent.minimum_balance(current_len)); |
| #76 | interaction_info.realloc(space, false)?; |
| #77 | if additional_rent > 0 { |
| #78 | let cpi_context = CpiContext::new( |
| #79 | system_program_info, |
| #80 | anchor_lang::system_program::Transfer { |
| #81 | from: payer_info.clone(), |
| #82 | to: interaction_info.clone(), |
| #83 | }, |
| #84 | ); |
| #85 | anchor_lang::system_program::transfer(cpi_context, additional_rent)?; |
| #86 | } |
| #87 | } |
| #88 | |
| #89 | let mut interaction_data = interaction.try_borrow_mut_data()?; |
| #90 | let mut interaction = |
| #91 | Interaction::try_deserialize_unchecked(&mut interaction_data.as_ref()) |
| #92 | .unwrap_or_default(); |
| #93 | |
| #94 | interaction.context = ctx.accounts.context_account.key(); |
| #95 | interaction.user = ctx.accounts.payer.key(); |
| #96 | interaction.text = text; |
| #97 | interaction.callback_program_id = callback_program_id; |
| #98 | interaction.callback_discriminator = callback_discriminator; |
| #99 | interaction.callback_account_metas = account_metas.unwrap_or_default(); |
| #100 | interaction.is_processed = false; |
| #101 | |
| #102 | interaction.try_serialize(&mut interaction_data.as_mut())?; |
| #103 | Ok(()) |
| #104 | } |
| #105 | |
| #106 | pub fn callback_from_llm<'info>( |
| #107 | ctx: Context<'_, '_, '_, 'info, CallbackFromLlm<'info>>, |
| #108 | response: String, |
| #109 | ) -> Result<()> { |
| #110 | let response_data = [ |
| #111 | ctx.accounts.interaction.callback_discriminator.to_vec(), |
| #112 | response.try_to_vec()?, |
| #113 | ] |
| #114 | .concat(); |
| #115 | |
| #116 | // Prepare accounts metas |
| #117 | let mut accounts_metas: Vec<anchor_lang::solana_program::instruction::AccountMeta> = |
| #118 | vec![anchor_lang::solana_program::instruction::AccountMeta { |
| #119 | pubkey: ctx.accounts.identity.key(), |
| #120 | is_signer: true, |
| #121 | is_writable: false, |
| #122 | }]; |
| #123 | accounts_metas.extend( |
| #124 | ctx.accounts |
| #125 | .interaction |
| #126 | .callback_account_metas |
| #127 | .iter() |
| #128 | .map( |
| #129 | |meta| anchor_lang::solana_program::instruction::AccountMeta { |
| #130 | pubkey: meta.pubkey, |
| #131 | is_signer: meta.is_signer, |
| #132 | is_writable: meta.is_writable, |
| #133 | }, |
| #134 | ), |
| #135 | ); |
| #136 | |
| #137 | // Verify payer is not in remaining accounts |
| #138 | if ctx |
| #139 | .remaining_accounts |
| #140 | .iter() |
| #141 | .any(|acc| acc.key().eq(&ctx.accounts.payer.key())) |
| #142 | { |
| #143 | return Err(ProgramError::InvalidAccountData.into()); |
| #144 | } |
| #145 | |
| #146 | // Set processed flag |
| #147 | ctx.accounts.interaction.is_processed = true; |
| #148 | |
| #149 | // CPI to the callback program |
| #150 | let instruction = Instruction { |
| #151 | program_id: ctx.accounts.program.key(), |
| #152 | accounts: accounts_metas, |
| #153 | data: response_data.to_vec(), |
| #154 | }; |
| #155 | let mut remaining_accounts: Vec<AccountInfo<'info>> = ctx.remaining_accounts.to_vec(); |
| #156 | remaining_accounts.push(ctx.accounts.identity.to_account_info()); |
| #157 | remaining_accounts.push(ctx.accounts.program.to_account_info()); |
| #158 | let identity_bump = ctx.bumps.identity; |
| #159 | invoke_signed( |
| #160 | &instruction, |
| #161 | &remaining_accounts, |
| #162 | &[&[b"identity", &[identity_bump]]], |
| #163 | )?; |
| #164 | Ok(()) |
| #165 | } |
| #166 | |
| #167 | pub fn callback_from_oracle(ctx: Context<CallbackFromOracle>, response: String) -> Result<()> { |
| #168 | if !ctx.accounts.identity.to_account_info().is_signer { |
| #169 | return Err(ProgramError::InvalidAccountData.into()); |
| #170 | } |
| #171 | msg!("Callback response: {:?}", response); |
| #172 | Ok(()) |
| #173 | } |
| #174 | |
| #175 | pub fn delegate_interaction(ctx: Context<DelegateInteraction>) -> Result<()> { |
| #176 | ctx.accounts.delegate_interaction( |
| #177 | &ctx.accounts.payer, |
| #178 | &[ |
| #179 | Interaction::seed(), |
| #180 | &ctx.accounts.payer.key().to_bytes(), |
| #181 | &ctx.accounts.context_account.key().to_bytes(), |
| #182 | ], |
| #183 | DelegateConfig::default(), |
| #184 | )?; |
| #185 | Ok(()) |
| #186 | } |
| #187 | } |
| #188 | |
| #189 | /// Contexts |
| #190 | |
| #191 | #[derive(Accounts)] |
| #192 | pub struct Initialize<'info> { |
| #193 | #[account(mut)] |
| #194 | pub payer: Signer<'info>, |
| #195 | #[account( |
| #196 | init, |
| #197 | payer = payer, |
| #198 | space = 8, |
| #199 | seeds = [b"identity"], |
| #200 | bump |
| #201 | )] |
| #202 | pub identity: Account<'info, Identity>, |
| #203 | #[account( |
| #204 | init, |
| #205 | payer = payer, |
| #206 | space = 8 + 32, |
| #207 | seeds = [b"counter"], |
| #208 | bump |
| #209 | )] |
| #210 | pub counter: Account<'info, Counter>, |
| #211 | pub system_program: Program<'info, System>, |
| #212 | } |
| #213 | |
| #214 | #[derive(Accounts)] |
| #215 | #[instruction(text: String)] |
| #216 | pub struct CreateLlmContext<'info> { |
| #217 | #[account(mut)] |
| #218 | pub payer: Signer<'info>, |
| #219 | #[account(mut, |
| #220 | seeds = [b"counter"], |
| #221 | bump |
| #222 | )] |
| #223 | pub counter: Account<'info, Counter>, |
| #224 | #[account( |
| #225 | init, |
| #226 | payer = payer, |
| #227 | space = 8 + text.as_bytes().len() + 8, |
| #228 | seeds = [ContextAccount::seed(), &counter.count.to_le_bytes()], |
| #229 | bump |
| #230 | )] |
| #231 | pub context_account: Account<'info, ContextAccount>, |
| #232 | pub system_program: Program<'info, System>, |
| #233 | } |
| #234 | |
| #235 | #[derive(Accounts)] |
| #236 | #[instruction(text: String, callback_program_id: Pubkey, callback_discriminator: [u8; 8], account_metas: Option<Vec<AccountMeta>>)] |
| #237 | pub struct InteractWithLlm<'info> { |
| #238 | #[account(mut)] |
| #239 | pub payer: Signer<'info>, |
| #240 | /// CHECK: the correct interaction account |
| #241 | #[account( |
| #242 | mut, |
| #243 | seeds = [Interaction::seed(), payer.key().as_ref(), context_account.key().as_ref()], |
| #244 | bump |
| #245 | )] |
| #246 | pub interaction: AccountInfo<'info>, |
| #247 | /// CHECK: we accept any context |
| #248 | pub context_account: Account<'info, ContextAccount>, |
| #249 | pub system_program: Program<'info, System>, |
| #250 | } |
| #251 | |
| #252 | #[derive(Accounts)] |
| #253 | pub struct CallbackFromLlm<'info> { |
| #254 | #[account(mut, address = ORACLE_IDENTITY)] |
| #255 | pub payer: Signer<'info>, |
| #256 | #[account(seeds = [b"identity"], bump)] |
| #257 | pub identity: Account<'info, Identity>, |
| #258 | /// CHECK: we accept any context |
| #259 | #[account(mut)] |
| #260 | pub interaction: Account<'info, Interaction>, |
| #261 | /// CHECK: the callback program |
| #262 | pub program: AccountInfo<'info>, |
| #263 | } |
| #264 | |
| #265 | #[derive(Accounts)] |
| #266 | pub struct CallbackFromOracle<'info> { |
| #267 | #[account(seeds = [b"identity"], bump)] |
| #268 | pub identity: Account<'info, Identity>, |
| #269 | } |
| #270 | |
| #271 | #[delegate] |
| #272 | #[derive(Accounts)] |
| #273 | pub struct DelegateInteraction<'info> { |
| #274 | #[account(mut)] |
| #275 | pub payer: Signer<'info>, |
| #276 | /// CHECK: the correct interaction account |
| #277 | #[account( |
| #278 | mut, del, |
| #279 | seeds = [Interaction::seed(), payer.key().as_ref(), context_account.key().as_ref()], |
| #280 | bump |
| #281 | )] |
| #282 | pub interaction: AccountInfo<'info>, |
| #283 | /// CHECK: we accept any context |
| #284 | pub context_account: Account<'info, ContextAccount>, |
| #285 | } |
| #286 | |
| #287 | /// Accounts |
| #288 | |
| #289 | #[account] |
| #290 | pub struct ContextAccount { |
| #291 | pub text: String, |
| #292 | } |
| #293 | |
| #294 | impl ContextAccount { |
| #295 | pub fn seed() -> &'static [u8] { |
| #296 | b"test-context" |
| #297 | } |
| #298 | } |
| #299 | |
| #300 | #[account] |
| #301 | #[derive(Default, Debug)] |
| #302 | pub struct Interaction { |
| #303 | pub context: Pubkey, |
| #304 | pub user: Pubkey, |
| #305 | pub text: String, |
| #306 | pub callback_program_id: Pubkey, |
| #307 | pub callback_discriminator: [u8; 8], |
| #308 | pub callback_account_metas: Vec<AccountMeta>, |
| #309 | pub is_processed: bool, |
| #310 | } |
| #311 | |
| #312 | impl Interaction { |
| #313 | pub fn seed() -> &'static [u8] { |
| #314 | b"interaction" |
| #315 | } |
| #316 | |
| #317 | pub fn space(text: &String, account_metas_len: usize) -> usize { |
| #318 | 121 + text.as_bytes().len() + account_metas_len * AccountMeta::size() |
| #319 | } |
| #320 | } |
| #321 | |
| #322 | #[derive(InitSpace, AnchorSerialize, AnchorDeserialize, Clone, Debug)] |
| #323 | pub struct AccountMeta { |
| #324 | pub pubkey: Pubkey, |
| #325 | pub is_signer: bool, |
| #326 | pub is_writable: bool, |
| #327 | } |
| #328 | |
| #329 | impl AccountMeta { |
| #330 | pub fn size() -> usize { |
| #331 | 8 + AccountMeta::INIT_SPACE |
| #332 | } |
| #333 | } |
| #334 | |
| #335 | #[account] |
| #336 | pub struct Counter { |
| #337 | pub count: u32, |
| #338 | } |
| #339 | |
| #340 | #[account] |
| #341 | pub struct Identity {} |
| #342 |