pub mod error;
pub mod sdk;
pub mod state;
pub mod utils;
use {
crate::{
error::ErrorCode,
state::*,
utils::*,
},
anchor_lang::{
prelude::*,
solana_program::{
instruction::{
get_stack_height,
TRANSACTION_LEVEL_STACK_HEIGHT,
},
sysvar::instructions as sysvar_instructions,
},
system_program::System,
},
};
declare_id!("PytERJFhAKuNNuaiXkApLfWzwNwSNDACpigT3LwQfou");
#[program]
pub mod express_relay {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: InitializeArgs) -> Result<()> {
validate_fee_split(data.split_router_default)?;
validate_fee_split(data.split_relayer)?;
let express_relay_metadata_data = &mut ctx.accounts.express_relay_metadata;
express_relay_metadata_data.admin = *ctx.accounts.admin.key;
express_relay_metadata_data.relayer_signer = *ctx.accounts.relayer_signer.key;
express_relay_metadata_data.fee_receiver_relayer = *ctx.accounts.fee_receiver_relayer.key;
express_relay_metadata_data.split_router_default = data.split_router_default;
express_relay_metadata_data.split_relayer = data.split_relayer;
Ok(())
}
pub fn set_admin(ctx: Context<SetAdmin>) -> Result<()> {
let express_relay_metadata_data = &mut ctx.accounts.express_relay_metadata;
express_relay_metadata_data.admin = *ctx.accounts.admin_new.key;
Ok(())
}
pub fn set_relayer(ctx: Context<SetRelayer>) -> Result<()> {
let express_relay_metadata_data = &mut ctx.accounts.express_relay_metadata;
express_relay_metadata_data.relayer_signer = *ctx.accounts.relayer_signer.key;
express_relay_metadata_data.fee_receiver_relayer = *ctx.accounts.fee_receiver_relayer.key;
Ok(())
}
pub fn set_splits(ctx: Context<SetSplits>, data: SetSplitsArgs) -> Result<()> {
validate_fee_split(data.split_router_default)?;
validate_fee_split(data.split_relayer)?;
let express_relay_metadata_data = &mut ctx.accounts.express_relay_metadata;
express_relay_metadata_data.split_router_default = data.split_router_default;
express_relay_metadata_data.split_relayer = data.split_relayer;
Ok(())
}
pub fn set_router_split(ctx: Context<SetRouterSplit>, data: SetRouterSplitArgs) -> Result<()> {
validate_fee_split(data.split_router)?;
ctx.accounts.config_router.router = *ctx.accounts.router.key;
ctx.accounts.config_router.split = data.split_router;
Ok(())
}
pub fn submit_bid(ctx: Context<SubmitBid>, data: SubmitBidArgs) -> Result<()> {
if data.deadline < Clock::get()?.unix_timestamp {
return err!(ErrorCode::DeadlinePassed);
}
if get_stack_height() > TRANSACTION_LEVEL_STACK_HEIGHT {
return err!(ErrorCode::InvalidCPISubmitBid);
}
let matching_ixs = get_matching_submit_bid_instructions(
ctx.accounts.sysvar_instructions.to_account_info(),
None,
)?;
if matching_ixs.len() > 1 {
return err!(ErrorCode::MultiplePermissions);
}
handle_bid_payment(ctx, data.bid_amount)
}
pub fn check_permission(ctx: Context<CheckPermission>) -> Result<u64> {
let (num_permissions, total_router_fees) = inspect_permissions_in_tx(
ctx.accounts.sysvar_instructions.clone(),
PermissionInfo {
permission: *ctx.accounts.permission.key,
router: *ctx.accounts.router.key,
config_router: ctx.accounts.config_router.to_account_info(),
express_relay_metadata: ctx.accounts.express_relay_metadata.to_account_info(),
},
)?;
if num_permissions == 0 {
return err!(ErrorCode::MissingPermission);
}
Ok(total_router_fees)
}
pub fn withdraw_fees(ctx: Context<WithdrawFees>) -> Result<()> {
let express_relay_metadata = &ctx.accounts.express_relay_metadata;
let fee_receiver_admin = &ctx.accounts.fee_receiver_admin;
let express_relay_metadata_account_info = express_relay_metadata.to_account_info();
let rent_express_relay_metadata =
Rent::get()?.minimum_balance(express_relay_metadata_account_info.data_len());
let amount = express_relay_metadata_account_info
.lamports()
.saturating_sub(rent_express_relay_metadata);
if amount == 0 {
return Ok(());
}
transfer_lamports(
&express_relay_metadata_account_info,
&fee_receiver_admin.to_account_info(),
amount,
)
}
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Copy, Debug)]
pub struct InitializeArgs {
pub split_router_default: u64,
pub split_relayer: u64,
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(init, payer = payer, space = RESERVE_EXPRESS_RELAY_METADATA, seeds = [SEED_METADATA], bump)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
pub admin: UncheckedAccount<'info>,
pub relayer_signer: UncheckedAccount<'info>,
pub fee_receiver_relayer: UncheckedAccount<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SetAdmin<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(mut, seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
pub admin_new: UncheckedAccount<'info>,
}
#[derive(Accounts)]
pub struct SetRelayer<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(mut, seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
pub relayer_signer: UncheckedAccount<'info>,
pub fee_receiver_relayer: UncheckedAccount<'info>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Copy, Debug)]
pub struct SetSplitsArgs {
pub split_router_default: u64,
pub split_relayer: u64,
}
#[derive(Accounts)]
pub struct SetSplits<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(mut, seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Copy, Debug)]
pub struct SetRouterSplitArgs {
pub split_router: u64,
}
#[derive(Accounts)]
pub struct SetRouterSplit<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(init_if_needed, payer = admin, space = RESERVE_EXPRESS_RELAY_CONFIG_ROUTER, seeds = [SEED_CONFIG_ROUTER, router.key().as_ref()], bump)]
pub config_router: Account<'info, ConfigRouter>,
#[account(seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
pub router: UncheckedAccount<'info>,
pub system_program: Program<'info, System>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Copy, Debug)]
pub struct SubmitBidArgs {
pub deadline: i64,
pub bid_amount: u64,
}
#[derive(Accounts)]
pub struct SubmitBid<'info> {
#[account(mut)]
pub searcher: Signer<'info>,
pub relayer_signer: Signer<'info>,
pub permission: UncheckedAccount<'info>,
#[account(mut)]
pub router: UncheckedAccount<'info>,
#[account(seeds = [SEED_CONFIG_ROUTER, router.key().as_ref()], bump)]
pub config_router: UncheckedAccount<'info>,
#[account(mut, seeds = [SEED_METADATA], bump, has_one = relayer_signer, has_one = fee_receiver_relayer)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
#[account(mut)]
pub fee_receiver_relayer: UncheckedAccount<'info>,
pub system_program: Program<'info, System>,
#[account(address = sysvar_instructions::ID)]
pub sysvar_instructions: UncheckedAccount<'info>,
}
#[derive(Accounts)]
pub struct CheckPermission<'info> {
#[account(address = sysvar_instructions::ID)]
pub sysvar_instructions: UncheckedAccount<'info>,
pub permission: UncheckedAccount<'info>,
pub router: UncheckedAccount<'info>,
#[account(seeds = [SEED_CONFIG_ROUTER, router.key().as_ref()], bump)]
pub config_router: UncheckedAccount<'info>,
#[account(seeds = [SEED_METADATA], bump)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
}
#[derive(Accounts)]
pub struct WithdrawFees<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(mut)]
pub fee_receiver_admin: UncheckedAccount<'info>,
#[account(mut, seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
}