1#![deny(rustdoc::all)]
3#![allow(rustdoc::missing_doc_code_examples)]
4
5use anchor_lang::prelude::*;
6use anchor_lang::solana_program::instruction::Instruction;
7use anchor_lang::solana_program::program::invoke_signed;
8use anchor_lang::Key;
9use anchor_spl::token::TokenAccount;
10use vipers::assert_keys_eq;
11use vipers::validate::Validate;
12
13mod account_validators;
14
15declare_id!("NFTUJzSHuUCsMMqMRJpB7PmbsaU7Wm51acdPk2FXMLn");
16
17#[program]
19pub mod token_signer {
20 use super::*;
21
22 #[access_control(ctx.accounts.validate())]
23 pub fn invoke_signed_instruction(
24 ctx: Context<InvokeSignedInstruction>,
25 data: Vec<u8>,
26 ) -> Result<()> {
27 let mint = ctx.accounts.nft_account.mint.to_bytes();
28 let seeds: &[&[u8]] = &[b"GokiTokenSigner", &mint];
29 let (nft_addr, bump) = Pubkey::find_program_address(seeds, ctx.program_id);
30 let full_seeds = &[b"GokiTokenSigner" as &[u8], &mint, &[bump]];
31
32 assert_keys_eq!(nft_addr, ctx.accounts.nft_pda, "nft_pda");
33
34 let accounts: Vec<AccountMeta> = ctx
35 .remaining_accounts
36 .iter()
37 .map(|acc| AccountMeta {
38 pubkey: acc.key(),
39 is_signer: if acc.key() == ctx.accounts.nft_pda.key() {
40 true
41 } else {
42 acc.is_signer
43 },
44 is_writable: acc.is_writable,
45 })
46 .collect();
47
48 let ix = Instruction {
50 program_id: ctx.remaining_accounts[0].key(),
51 accounts,
52 data,
53 };
54 invoke_signed(&ix, ctx.remaining_accounts, &[full_seeds])?;
55
56 Ok(())
57 }
58}
59
60#[derive(Accounts)]
61#[instruction(bump: u8)]
62pub struct InvokeSignedInstruction<'info> {
63 pub owner_authority: Signer<'info>,
65
66 pub nft_account: Account<'info, TokenAccount>,
69
70 #[account(
72 seeds = [
73 b"GokiTokenSigner".as_ref(),
74 nft_account.mint.as_ref()
75 ],
76 bump = bump,
77 )]
78 pub nft_pda: SystemAccount<'info>,
79}
80
81#[error_code]
82pub enum ErrorCode {
83 #[msg("Unauthorized.")]
84 Unauthorized,
85}