1use anchor_lang::prelude::ProgramError;
2use anchor_lang::prelude::*;
3use anchor_lang::solana_program::instruction::Instruction;
4use anchor_lang::solana_program::program::invoke_signed;
5
6declare_id!("LLMrieZMpbJFwN52WgmBNMxYojrpRVYXdC1RCweEbab");
7
8#[program]
9pub mod solana_gpt_oracle {
10 use super::*;
11
12 pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
13 Ok(())
14 }
15
16 pub fn create_llm_context(ctx: Context<CreateLlmContext>, text: String) -> Result<()> {
17 let context_account = &mut ctx.accounts.context_account;
18 context_account.text = text;
19 ctx.accounts.counter.count += 1;
20 Ok(())
21 }
22
23 pub fn interact_with_llm(
24 ctx: Context<InteractWithLlm>,
25 text: String,
26 callback_program_id: Pubkey,
27 callback_discriminator: [u8; 8],
28 account_metas: Option<Vec<AccountMeta>>,
29 ) -> Result<()> {
30 let interaction = &mut ctx.accounts.interaction;
31 interaction.context = ctx.accounts.context_account.key();
32 interaction.user = ctx.accounts.payer.key();
33 interaction.text = text;
34 interaction.callback_program_id = callback_program_id;
35 interaction.callback_discriminator = callback_discriminator;
36 interaction.callback_account_metas = account_metas.unwrap_or_default();
37 Ok(())
38 }
39
40 pub fn callback_from_llm<'info>(
41 ctx: Context<'_, '_, '_, 'info, CallbackFromLlm<'info>>,
42 response: String,
43 ) -> Result<()> {
44 let response_data = [
45 ctx.accounts.interaction.callback_discriminator.to_vec(),
46 response.try_to_vec()?,
47 ]
48 .concat();
49
50 let mut accounts_metas: Vec<anchor_lang::solana_program::instruction::AccountMeta> =
52 vec![anchor_lang::solana_program::instruction::AccountMeta {
53 pubkey: ctx.accounts.identity.key(),
54 is_signer: true,
55 is_writable: false,
56 }];
57 accounts_metas.extend(
58 ctx.accounts
59 .interaction
60 .callback_account_metas
61 .iter()
62 .map(
63 |meta| anchor_lang::solana_program::instruction::AccountMeta {
64 pubkey: meta.pubkey,
65 is_signer: meta.is_signer,
66 is_writable: meta.is_writable,
67 },
68 ),
69 );
70
71 if ctx
73 .remaining_accounts
74 .iter()
75 .any(|acc| acc.key().eq(&ctx.accounts.payer.key()))
76 {
77 return Err(ProgramError::InvalidAccountData.into());
78 }
79
80 let instruction = Instruction {
82 program_id: ctx.accounts.program.key(),
83 accounts: accounts_metas,
84 data: response_data.to_vec(),
85 };
86 let mut remaining_accounts: Vec<AccountInfo<'info>> = ctx.remaining_accounts.to_vec();
87 remaining_accounts.push(ctx.accounts.identity.to_account_info());
88 remaining_accounts.push(ctx.accounts.program.to_account_info());
89 let identity_bump = ctx.bumps.identity;
90 invoke_signed(
91 &instruction,
92 &remaining_accounts,
93 &[&[b"identity", &[identity_bump]]],
94 )?;
95 Ok(())
96 }
97
98 pub fn callback_from_oracle(ctx: Context<CallbackFromOracle>, response: String) -> Result<()> {
99 if !ctx.accounts.identity.to_account_info().is_signer {
100 return Err(ProgramError::InvalidAccountData.into());
101 }
102 msg!("Callback response: {:?}", response);
103 Ok(())
104 }
105}
106
107#[derive(Accounts)]
110pub struct Initialize<'info> {
111 #[account(mut)]
112 pub payer: Signer<'info>,
113 #[account(
114 init,
115 payer = payer,
116 space = 8,
117 seeds = [b"identity"],
118 bump
119 )]
120 pub identity: Account<'info, Identity>,
121 #[account(
122 init,
123 payer = payer,
124 space = 8 + 32,
125 seeds = [b"counter"],
126 bump
127 )]
128 pub counter: Account<'info, Counter>,
129 pub system_program: Program<'info, System>,
130}
131
132#[derive(Accounts)]
133#[instruction(text: String)]
134pub struct CreateLlmContext<'info> {
135 #[account(mut)]
136 pub payer: Signer<'info>,
137 #[account(mut,
138 seeds = [b"counter"],
139 bump
140 )]
141 pub counter: Account<'info, Counter>,
142 #[account(
143 init,
144 payer = payer,
145 space = 8 + text.as_bytes().len() + 8,
146 seeds = [ContextAccount::seed(), &counter.count.to_le_bytes()],
147 bump
148 )]
149 pub context_account: Account<'info, ContextAccount>,
150 pub system_program: Program<'info, System>,
151}
152
153#[derive(Accounts)]
154#[instruction(text: String, callback_program_id: Pubkey, callback_discriminator: [u8; 8], account_metas: Option<Vec<AccountMeta>>)]
155pub struct InteractWithLlm<'info> {
156 #[account(mut)]
157 pub payer: Signer<'info>,
158 #[account(
159 init,
160 payer = payer,
161 space = 120 + text.as_bytes().len() + account_metas.as_ref().map_or(0, |m| m.len()) * AccountMeta::size(),
162 seeds = [Interaction::seed(), payer.key().as_ref(), context_account.key().as_ref()],
163 bump
164 )]
165 pub interaction: Account<'info, Interaction>,
166 pub context_account: Account<'info, ContextAccount>,
168 pub system_program: Program<'info, System>,
169}
170
171#[derive(Accounts)]
172pub struct CallbackFromLlm<'info> {
173 #[account(mut)]
174 pub payer: Signer<'info>,
175 #[account(seeds = [b"identity"], bump)]
176 pub identity: Account<'info, Identity>,
177 #[account(mut, close = payer)]
179 pub interaction: Account<'info, Interaction>,
180 pub program: AccountInfo<'info>,
182}
183
184#[derive(Accounts)]
185pub struct CallbackFromOracle<'info> {
186 #[account(seeds = [b"identity"], bump)]
187 pub identity: Account<'info, Identity>,
188}
189
190#[account]
193pub struct ContextAccount {
194 pub text: String,
195}
196
197impl ContextAccount {
198 pub fn seed() -> &'static [u8] {
199 b"context"
200 }
201}
202
203#[account]
204#[derive(Debug)]
205pub struct Interaction {
206 pub context: Pubkey,
207 pub user: Pubkey,
208 pub text: String,
209 pub callback_program_id: Pubkey,
210 pub callback_discriminator: [u8; 8],
211 pub callback_account_metas: Vec<AccountMeta>,
212}
213
214impl Interaction {
215 pub fn seed() -> &'static [u8] {
216 b"interaction"
217 }
218}
219
220#[derive(InitSpace, AnchorSerialize, AnchorDeserialize, Clone, Debug)]
221pub struct AccountMeta {
222 pub pubkey: Pubkey,
223 pub is_signer: bool,
224 pub is_writable: bool,
225}
226
227impl AccountMeta {
228 pub fn size() -> usize {
229 8 + AccountMeta::INIT_SPACE
230 }
231}
232
233#[account]
234pub struct Counter {
235 pub count: u32,
236}
237
238#[account]
239pub struct Identity {}