Skip to content

Instantly share code, notes, and snippets.

@gejeduck
Created August 22, 2022 16:41
Show Gist options
  • Save gejeduck/88fd00df74f765b41e3909f3970a6617 to your computer and use it in GitHub Desktop.
Save gejeduck/88fd00df74f765b41e3909f3970a6617 to your computer and use it in GitHub Desktop.
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>,
}
@true-eye
Copy link

@gejeduck Hi Can you contact me, please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment