Created
August 22, 2022 16:41
-
-
Save gejeduck/88fd00df74f765b41e3909f3970a6617 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use anchor_lang::prelude::*; | |
use anchor_lang::solana_program; | |
use anchor_lang::solana_program::instruction::Instruction; | |
use anchor_lang::solana_program::pubkey; | |
use anchor_lang::solana_program::system_instruction; | |
use anchor_spl::token::{CloseAccount, Mint, Token, TokenAccount, Transfer}; | |
use solana_program::sysvar::fees::Fees; | |
use spl_token::{self}; | |
use stake_manager::GlobalState as GlobalStakeState; | |
use std::convert::Into; | |
use stake_manager::SLOTS_IN_EPOCH; | |
declare_id!("8LRxyb7ftDYSbSsZ3pqbW9jjUZgMGSZhx69BLtLVBdBf"); | |
#[program] | |
pub mod registry { | |
use anchor_spl::token; | |
use super::*; | |
const NUM_SIGNATURES: u8 = 1; | |
const MIN_ESCROW_LAMPORT: u64 = 50000; | |
const WSOL_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112"); | |
pub fn initialize(ctx: Context<Initialize>) -> Result<()> { | |
ctx.accounts.global_state.main_authority = *ctx.accounts.initializer.key; | |
Ok(()) | |
} | |
pub fn set_stake_manager(ctx: Context<SetStakeManager>, stake_manager: Pubkey) -> Result<()> { | |
ctx.accounts.global_state.stake_manager = stake_manager; | |
Ok(()) | |
} | |
pub fn set_user_verify_forwarder( | |
ctx: Context<SetUserVerifyForwarder>, | |
user_verify_forwarder: Pubkey, | |
) -> Result<()> { | |
ctx.accounts.global_state.user_verify_forwarder = user_verify_forwarder; | |
Ok(()) | |
} | |
pub fn register_execution( | |
ctx: Context<RegisterExecution>, | |
verify_user: bool, | |
pid: Pubkey, | |
accs: Vec<TransactionAccount>, | |
data: Vec<u8>, | |
) -> Result<()> { | |
let request_id = ctx.accounts.register_state.next_request_id; | |
ctx.accounts.register_state.next_request_id = request_id + 1; | |
ctx.accounts.register_request.transaction = *ctx.accounts.transaction.to_account_info().key; | |
ctx.accounts.register_request.verify_user = verify_user; | |
let tx = &mut ctx.accounts.transaction; | |
tx.program_id = pid; | |
tx.accounts = accs.clone(); | |
tx.data = data; | |
solana_program::program::invoke( | |
&system_instruction::transfer( | |
&ctx.accounts.register.to_account_info().key(), | |
&ctx.accounts.register_request.to_account_info().key(), | |
MIN_ESCROW_LAMPORT, | |
), | |
&[ | |
ctx.accounts.register.to_account_info(), | |
ctx.accounts.register_request.to_account_info(), | |
ctx.accounts.system_program.to_account_info(), | |
], | |
)?; | |
emit!(Register { | |
register: ctx.accounts.register.to_account_info().key(), | |
request_id, | |
program_id: pid, | |
accounts: accs | |
}); | |
Ok(()) | |
} | |
pub fn register_execution_with_escrow( | |
ctx: Context<RegisterExecutionWithEscrow>, | |
escrow_amount: u64, | |
pid: Pubkey, | |
accs: Vec<TransactionAccount>, | |
data: Vec<u8>, | |
) -> Result<()> { | |
let request_id = ctx.accounts.register_state.next_request_id; | |
ctx.accounts.register_state.next_request_id = request_id + 1; | |
ctx.accounts.register_request.transaction = *ctx.accounts.transaction.to_account_info().key; | |
ctx.accounts.register_request.verify_user = false; // We don't use the escrow for the forwarded requests? | |
let tx = &mut ctx.accounts.transaction; | |
tx.program_id = pid; | |
tx.accounts = accs.clone(); | |
tx.data = data; | |
solana_program::program::invoke( | |
&system_instruction::transfer( | |
&ctx.accounts.register.to_account_info().key(), | |
&ctx.accounts.register_request.to_account_info().key(), | |
MIN_ESCROW_LAMPORT, | |
), | |
&[ | |
ctx.accounts.register.to_account_info(), | |
ctx.accounts.register_request.to_account_info(), | |
ctx.accounts.system_program.to_account_info(), | |
], | |
)?; | |
if ctx.accounts.mint_account.key() == WSOL_MINT { | |
solana_program::program::invoke( | |
&system_instruction::transfer( | |
&ctx.accounts.register.to_account_info().key(), | |
&ctx.accounts.register_escrow.to_account_info().key(), | |
escrow_amount, | |
), | |
&[ | |
ctx.accounts.register.to_account_info(), | |
ctx.accounts.register_escrow.to_account_info(), | |
ctx.accounts.system_program.to_account_info(), | |
], | |
)?; | |
let ix = spl_token::instruction::sync_native( | |
ctx.accounts.token_program.to_account_info().key, | |
ctx.accounts.register_escrow.to_account_info().key, | |
)?; | |
solana_program::program::invoke( | |
&ix, | |
&[ | |
ctx.accounts.token_program.to_account_info(), | |
ctx.accounts.register_escrow.to_account_info(), | |
], | |
)?; | |
} else { | |
token::transfer(ctx.accounts.into_escrow_context(), escrow_amount)?; | |
} | |
emit!(Register { | |
register: ctx.accounts.register.to_account_info().key(), | |
request_id, | |
program_id: pid, | |
accounts: accs | |
}); | |
Ok(()) | |
} | |
pub fn execute( | |
ctx: Context<Execute>, | |
_global_state_bump: u8, | |
register_request_bump: u8, | |
request_id: u64, | |
) -> Result<()> { | |
let (global_stake_state_key, _) = Pubkey::find_program_address( | |
&[b"global-state".as_ref()], | |
&ctx.accounts.global_state.stake_manager, | |
); | |
if !ctx | |
.accounts | |
.global_stake_state | |
.key() | |
.eq(&global_stake_state_key) | |
{ | |
return Err(ProgramError::InvalidAccountData.into()); | |
} | |
let mut ix: Instruction = (&*ctx.accounts.transaction).into(); | |
ix.accounts = ix | |
.accounts | |
.iter() | |
.map(|acc| { | |
let mut acc = acc.clone(); | |
if &acc.pubkey == ctx.accounts.register_request.to_account_info().key { | |
acc.is_signer = true; | |
} | |
acc | |
}) | |
.collect(); | |
for acc in ctx.remaining_accounts.iter() { | |
match ix.accounts.iter().position(|a| a.pubkey == acc.key()) { | |
Some(_) => {} | |
None => { | |
if acc.is_writable { | |
ix.accounts.push(AccountMeta::new(acc.key(), false)); | |
} else { | |
ix.accounts | |
.push(AccountMeta::new_readonly(acc.key(), false)); | |
} | |
} | |
} | |
} | |
if ctx.accounts.register_request.verify_user { | |
let (_user_verify_authority, user_verify_authority_bump) = | |
Pubkey::find_program_address(&[b"user-verify"], ctx.program_id); | |
let seeds = &[&b"user-verify"[..], &[user_verify_authority_bump]]; | |
let signer = &[&seeds[..]]; | |
let accounts = ctx.remaining_accounts; | |
solana_program::program::invoke_signed(&ix, accounts, signer)?; | |
} else { | |
let t = request_id.to_string(); | |
let seeds = &[ | |
ctx.accounts.register.to_account_info().key.as_ref(), | |
b"register-request", | |
t.as_bytes(), | |
&[register_request_bump], | |
]; | |
let signer = &[&seeds[..]]; | |
let accounts = ctx.remaining_accounts; | |
solana_program::program::invoke_signed(&ix, accounts, signer)?; | |
} | |
let fee = ctx.accounts.fees.fee_calculator.lamports_per_signature * NUM_SIGNATURES as u64; | |
let register_request_info = &mut ctx.accounts.register_request.to_account_info(); | |
let execution_authority_info = &mut ctx.accounts.execution_authority.to_account_info(); | |
// we can decrement the register_request account's lamports b/c we own it | |
**register_request_info.try_borrow_mut_lamports()? = register_request_info | |
.lamports() | |
.checked_sub(fee) | |
// or whatever, some error of your choosing | |
.ok_or(ProgramError::InvalidArgument)?; | |
// *incrementing* an account's lamports is always ok though | |
**execution_authority_info.try_borrow_mut_lamports()? = execution_authority_info | |
.lamports() | |
.checked_add(fee) | |
.ok_or(ProgramError::InvalidArgument)?; | |
Ok(()) | |
} | |
pub fn cancel( | |
_ctx: Context<Cancel>, | |
_register_request_bump: u8, | |
_request_id: u64, | |
) -> Result<()> { | |
Ok(()) | |
} | |
pub fn cancel_with_escrow( | |
ctx: Context<CancelWithEscrow>, | |
register_request_bump: u8, | |
request_id: u64, | |
) -> Result<()> { | |
token::transfer( | |
ctx.accounts | |
.into_return_to_register_context() | |
.with_signer(&[&[ | |
ctx.accounts.register.key().as_ref(), | |
b"register-request".as_ref(), | |
request_id.to_string().as_bytes(), | |
&[register_request_bump], | |
]]), | |
ctx.accounts.token_account.amount, | |
)?; | |
token::close_account(ctx.accounts.into_close_context().with_signer(&[&[ | |
ctx.accounts.register.key().as_ref(), | |
b"register-request".as_ref(), | |
request_id.to_string().as_bytes(), | |
&[register_request_bump], | |
]]))?; | |
Ok(()) | |
} | |
} | |
#[derive(Accounts)] | |
pub struct Initialize<'info> { | |
#[account(mut)] | |
pub initializer: Signer<'info>, | |
#[account( | |
init, | |
seeds = [b"global-state".as_ref()], | |
space = 8 + 32 + 32 + 32, | |
bump, | |
payer = initializer | |
)] | |
pub global_state: Account<'info, GlobalState>, | |
pub system_program: Program<'info, System>, | |
} | |
#[derive(Accounts)] | |
pub struct SetStakeManager<'info> { | |
#[account(mut)] | |
pub main_authority: Signer<'info>, | |
#[account( | |
mut, | |
seeds = [b"global-state".as_ref()], | |
bump, | |
constraint = *main_authority.key == global_state.main_authority | |
)] | |
pub global_state: Account<'info, GlobalState>, | |
} | |
#[derive(Accounts)] | |
pub struct SetUserVerifyForwarder<'info> { | |
#[account(mut)] | |
pub main_authority: Signer<'info>, | |
#[account( | |
mut, | |
seeds = [b"global-state".as_ref()], | |
bump, | |
constraint = *main_authority.key == global_state.main_authority | |
)] | |
pub global_state: Account<'info, GlobalState>, | |
} | |
#[derive(Accounts)] | |
pub struct RegisterExecution<'info> { | |
#[account(mut)] | |
pub register: Signer<'info>, | |
#[account( | |
init_if_needed, | |
seeds = [register.to_account_info().key.as_ref(), b"register-state"], | |
space = 8 + 8, | |
bump, | |
payer = register | |
)] | |
pub register_state: Account<'info, RegisterState>, | |
#[account( | |
init, | |
seeds = [register.to_account_info().key.as_ref(), b"register-request", register_state.next_request_id.to_string().as_bytes()], | |
space = 8 + 1 + 32, | |
bump, | |
payer = register | |
)] | |
pub register_request: Account<'info, RegisterRequest>, | |
#[account(zero)] | |
pub transaction: Box<Account<'info, Transaction>>, | |
pub system_program: Program<'info, System>, | |
} | |
#[derive(Accounts)] | |
pub struct RegisterExecutionWithEscrow<'info> { | |
#[account(mut)] | |
pub register: Signer<'info>, | |
#[account( | |
init_if_needed, | |
seeds = [register.to_account_info().key.as_ref(), b"register-state"], | |
space = 8 + 8, | |
bump, | |
payer = register | |
)] | |
pub register_state: Account<'info, RegisterState>, | |
#[account( | |
init, | |
seeds = [register.to_account_info().key.as_ref(), b"register-request", register_state.next_request_id.to_string().as_bytes()], | |
space = 8 + 32, | |
bump, | |
payer = register | |
)] | |
pub register_request: Account<'info, RegisterRequest>, | |
pub mint_account: Account<'info, Mint>, | |
/// CHECK: safe | |
#[account(mut)] | |
pub from_account: AccountInfo<'info>, | |
#[account( | |
init, | |
seeds = [register.to_account_info().key.as_ref(), b"register-escrow", register_state.next_request_id.to_string().as_bytes()], | |
bump, | |
payer = register, | |
token::mint = mint_account, | |
token::authority = register_request, | |
)] | |
pub register_escrow: Account<'info, TokenAccount>, | |
#[account(zero)] | |
pub transaction: Box<Account<'info, Transaction>>, | |
pub rent: Sysvar<'info, Rent>, | |
pub token_program: Program<'info, Token>, | |
pub system_program: Program<'info, System>, | |
} | |
impl<'info> RegisterExecutionWithEscrow<'info> { | |
pub fn into_escrow_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { | |
let cpi_accounts = Transfer { | |
from: self.from_account.clone(), | |
to: self.register_escrow.to_account_info().clone(), | |
authority: self.register.to_account_info().clone(), | |
}; | |
CpiContext::new(self.token_program.to_account_info(), cpi_accounts) | |
} | |
} | |
#[derive(Accounts)] | |
#[instruction(global_state_bump: u8, register_request_bump: u8, request_id: u64)] | |
pub struct Execute<'info> { | |
#[account(mut)] | |
pub execution_authority: Signer<'info>, | |
#[account( | |
seeds = [b"global-state".as_ref()], | |
bump = global_state_bump, | |
)] | |
pub global_state: Account<'info, GlobalState>, | |
/// CHECK: register wallet | |
#[account(mut)] | |
pub register: AccountInfo<'info>, | |
#[account( | |
mut, | |
seeds = [register.to_account_info().key.as_ref(), b"register-request", request_id.to_string().as_bytes()], | |
bump = register_request_bump, | |
constraint = register_request.transaction == *transaction.to_account_info().key, | |
close = register | |
)] | |
pub register_request: Account<'info, RegisterRequest>, | |
#[account(mut, close = register)] | |
transaction: Account<'info, Transaction>, | |
#[account( | |
constraint = global_stake_state.executor == *execution_authority.key, | |
constraint = global_stake_state.epoch == clock.slot / SLOTS_IN_EPOCH * SLOTS_IN_EPOCH + 1 | |
)] | |
pub global_stake_state: Account<'info, GlobalStakeState>, | |
pub fees: Sysvar<'info, Fees>, | |
pub clock: Sysvar<'info, Clock>, | |
pub system_program: Program<'info, System>, | |
} | |
#[derive(Accounts)] | |
#[instruction(register_request_bump: u8, request_id: u64)] | |
pub struct Cancel<'info> { | |
#[account(mut)] | |
pub register: Signer<'info>, | |
#[account( | |
mut, | |
seeds = [register.to_account_info().key.as_ref(), b"register-request", request_id.to_string().as_bytes()], | |
bump = register_request_bump, | |
constraint = register_request.transaction == *transaction.to_account_info().key, | |
close = register | |
)] | |
pub register_request: Account<'info, RegisterRequest>, | |
#[account(mut, close = register)] | |
transaction: Account<'info, Transaction>, | |
pub system_program: Program<'info, System>, | |
} | |
#[derive(Accounts)] | |
#[instruction(register_request_bump: u8, request_id: u64)] | |
pub struct CancelWithEscrow<'info> { | |
#[account(mut)] | |
pub register: Signer<'info>, | |
#[account( | |
mut, | |
seeds = [register.to_account_info().key.as_ref(), b"register-request", request_id.to_string().as_bytes()], | |
bump = register_request_bump, | |
constraint = register_request.transaction == *transaction.to_account_info().key, | |
close = register | |
)] | |
pub register_request: Account<'info, RegisterRequest>, | |
#[account(mut)] | |
pub token_account: Account<'info, TokenAccount>, | |
#[account(mut)] | |
pub receive_token_account: Account<'info, TokenAccount>, | |
#[account(mut, close = register)] | |
transaction: Account<'info, Transaction>, | |
pub token_program: Program<'info, Token>, | |
pub system_program: Program<'info, System>, | |
} | |
impl<'info> CancelWithEscrow<'info> { | |
pub fn into_return_to_register_context( | |
&self, | |
) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { | |
let cpi_accounts = Transfer { | |
from: self.token_account.to_account_info().clone(), | |
to: self.receive_token_account.to_account_info().clone(), | |
authority: self.register_request.to_account_info().clone(), | |
}; | |
CpiContext::new(self.token_program.to_account_info(), cpi_accounts) | |
} | |
pub fn into_close_context(&self) -> CpiContext<'_, '_, '_, 'info, CloseAccount<'info>> { | |
let cpi_accounts = CloseAccount { | |
account: self.token_account.to_account_info().clone(), | |
destination: self.register.to_account_info().clone(), | |
authority: self.register_request.to_account_info().clone(), | |
}; | |
CpiContext::new(self.token_program.to_account_info(), cpi_accounts) | |
} | |
} | |
#[account] | |
#[derive(Default)] | |
pub struct GlobalState { | |
pub main_authority: Pubkey, | |
pub stake_manager: Pubkey, | |
pub user_verify_forwarder: Pubkey, | |
} | |
#[account] | |
#[derive(Default)] | |
pub struct RegisterState { | |
pub next_request_id: u64, | |
} | |
#[account] | |
#[derive(Default)] | |
pub struct RegisterRequest { | |
pub verify_user: bool, | |
pub transaction: Pubkey, | |
} | |
#[account] | |
#[derive(Default)] | |
pub struct Transaction { | |
pub program_id: Pubkey, | |
pub accounts: Vec<TransactionAccount>, | |
pub data: Vec<u8>, | |
} | |
impl From<&Transaction> for Instruction { | |
fn from(tx: &Transaction) -> Instruction { | |
Instruction { | |
program_id: tx.program_id, | |
accounts: tx.accounts.iter().map(Into::into).collect(), | |
data: tx.data.clone(), | |
} | |
} | |
} | |
#[derive(AnchorSerialize, AnchorDeserialize, Clone)] | |
pub struct TransactionAccount { | |
pub pubkey: Pubkey, | |
pub is_signer: bool, | |
pub is_writable: bool, | |
} | |
impl From<&TransactionAccount> for AccountMeta { | |
fn from(account: &TransactionAccount) -> AccountMeta { | |
match account.is_writable { | |
false => AccountMeta::new_readonly(account.pubkey, account.is_signer), | |
true => AccountMeta::new(account.pubkey, account.is_signer), | |
} | |
} | |
} | |
impl From<&AccountMeta> for TransactionAccount { | |
fn from(account_meta: &AccountMeta) -> TransactionAccount { | |
TransactionAccount { | |
pubkey: account_meta.pubkey, | |
is_signer: account_meta.is_signer, | |
is_writable: account_meta.is_writable, | |
} | |
} | |
} | |
#[event] | |
pub struct Register { | |
pub register: Pubkey, | |
pub request_id: u64, | |
pub program_id: Pubkey, | |
pub accounts: Vec<TransactionAccount>, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@gejeduck Hi Can you contact me, please?