Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apps/deposit program #21

Merged
merged 4 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/deposit-program/programs/deposit-program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ pub enum ErrorCode {
InsufficientBalance,
#[msg("Unauthorized Withdrawal")]
UnauthorizedWithdrawal,
#[msg("Invalid Master Wallet")]
InvalidMasterWallet,
#[msg("Already subscribed")]
AlreadySubscribed,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use anchor_lang::prelude::*;

use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{Mint, TokenAccount, TokenInterface},
};

use crate::{send_tokens, UserInfo, USER_INFO_SEED};

#[derive(Accounts)]
pub struct DepositToVault<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(mint::token_program = token_program)]
pub token: InterfaceAccount<'info, Mint>,
#[account(
mut,
associated_token::mint = token,
associated_token::authority = user,
associated_token::token_program = token_program,
)]
pub user_token_account: InterfaceAccount<'info, TokenAccount>,
#[account(
init_if_needed,
payer = user,
space = 8 + UserInfo::INIT_SPACE,
seeds = [USER_INFO_SEED, user.key().as_ref()],
bump
)]
user_info: Account<'info, UserInfo>,
#[account(
init_if_needed,
payer = user,
associated_token::mint = token,
associated_token::authority = user_info,
associated_token::token_program = token_program,
)]
pub vault: InterfaceAccount<'info, TokenAccount>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub token_program: Interface<'info, TokenInterface>,
pub system_program: Program<'info, System>,
}

pub fn deposit_to_vault_handler(ctx: Context<DepositToVault>, amount: u64) -> Result<()> {
send_tokens(
ctx.accounts.user_token_account.to_account_info(),
ctx.accounts.token.to_account_info(),
ctx.accounts.vault.to_account_info(),
ctx.accounts.user.to_account_info(),
ctx.accounts.token_program.to_account_info(),
ctx.accounts.token.decimals,
amount,
)?;
save_user_info(ctx, amount)?;
msg!("Deposited {} tokens to vault.", amount);
Ok(())
}

fn save_user_info(ctx: Context<DepositToVault>, amount: u64) -> Result<()> {
ctx.accounts.user_info.available_balance += amount;
ctx.accounts.user_info.bump = ctx.bumps.user_info;
Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ pub use subscribe::*;

pub mod refund_balance;
pub use refund_balance::*;

pub mod deposit_to_vault;
pub use deposit_to_vault::*;

pub mod subscribe_with_vault;
pub use subscribe_with_vault::*;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ pub struct RefundBalance<'info> {
pub user: Signer<'info>, // User must sign the transaction
#[account(mint::token_program = token_program)]
pub token: InterfaceAccount<'info, Mint>,
#[account(mut,
#[account(
mut,
seeds = [USER_INFO_SEED, user.key().as_ref()],
bump = user_info.bump
)]
Expand All @@ -36,7 +37,7 @@ pub struct RefundBalance<'info> {
pub system_program: Program<'info, System>,
}

pub fn handler(ctx: Context<RefundBalance>) -> Result<()> {
pub fn refund_balance_handler(ctx: Context<RefundBalance>) -> Result<()> {
let amount = ctx.accounts.user_info.available_balance;
if amount == 0 {
return Err(ErrorCode::InsufficientBalance.into());
Expand All @@ -49,7 +50,7 @@ pub fn handler(ctx: Context<RefundBalance>) -> Result<()> {
Ok(())
}

pub fn send_to_user(ctx: &Context<RefundBalance>, amount: u64) -> Result<()> {
fn send_to_user(ctx: &Context<RefundBalance>, amount: u64) -> Result<()> {
let user_key = ctx.accounts.user.key();
let seeds = &[
USER_INFO_SEED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ use anchor_lang::prelude::*;

use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked},
token_interface::{Mint, TokenAccount, TokenInterface},
};

use crate::{error::ErrorCode, UserInfo, LOCAL_MASTER_WALLET, SUBSCRIPTION_LEVELS, USER_INFO_SEED};
use crate::{
error::ErrorCode, get_subscription_level, send_tokens, UserInfo, LOCAL_MASTER_WALLET,
USER_INFO_SEED,
};

#[derive(Accounts)]
pub struct Subscribe<'info> {
Expand Down Expand Up @@ -38,11 +41,10 @@ pub struct Subscribe<'info> {
pub vault: InterfaceAccount<'info, TokenAccount>,
/// See Anchor example:
/// https://github.com/solana-developers/anchor-examples/blob/main/account-constraints/address/programs/example/src/lib.rs
/// CHECK: Restricts `master_wallet` to the `MASTER_WALLET` address.
#[account(
address = LOCAL_MASTER_WALLET
)]
pub master_wallet: UncheckedAccount<'info>,
pub master_wallet: SystemAccount<'info>,
#[account(
mut,
associated_token::mint = token,
Expand All @@ -55,23 +57,18 @@ pub struct Subscribe<'info> {
pub system_program: Program<'info, System>,
}

pub fn handler(ctx: Context<Subscribe>, amount: u64) -> Result<()> {
let (subscription_cost, duration) = match SUBSCRIPTION_LEVELS
.iter()
.rev()
.find(|(cost, _)| amount >= *cost)
{
Some(level) => *level,
None => return Err(ErrorCode::InsufficientBalance.into()),
};
pub fn subscribe_handler(ctx: Context<Subscribe>, amount: u64) -> Result<()> {
let (subscription_cost, duration) = get_subscription_level(amount)?;
// Calculate the amount to send to the vault
let vault_amount = amount.saturating_sub(subscription_cost); // Ensure no negative value
let vault_amount = amount - subscription_cost;
let current_timestamp = Clock::get()?.unix_timestamp;
// Check if the user already has an active subscription
if ctx.accounts.user_info.expiration > current_timestamp {
msg!("Active subscription found. Updating user's available balance.");
send_to_vault(&ctx, amount)?; // Transfer entire amount to vault
update_user_info(ctx, amount)?;
msg!("User already has an active subscription.");
return Err(ErrorCode::AlreadySubscribed.into());
//msg!("Active subscription found. Updating user's available balance.");
//send_to_vault(&ctx, amount)?; // Transfer entire amount to vault
//update_user_info(ctx, amount)?;
//return Err(Error::from(SubscribeErrorCode::AlreadySubscribed));
} else {
msg!("No active subscription found. Proceeding with new subscription.");
Expand All @@ -85,49 +82,40 @@ pub fn handler(ctx: Context<Subscribe>, amount: u64) -> Result<()> {
Ok(())
}

pub fn update_user_info(ctx: Context<Subscribe>, additional_balance: u64) -> Result<()> {
ctx.accounts.user_info.available_balance += additional_balance;
msg!("Subscription is active. Funds have been added to the vault, and your available balance has been updated.");
Ok(())
}
//fn update_user_info(ctx: Context<Subscribe>, additional_balance: u64) -> Result<()> {
// ctx.accounts.user_info.available_balance += additional_balance;
// msg!("Subscription is active. Funds have been added to the vault, and your available balance has been updated.");
// Ok(())
//}

pub fn send_to_master_wallet(ctx: &Context<Subscribe>, amount: u64) -> Result<()> {
let transfer_accounts = TransferChecked {
from: ctx.accounts.user_token_account.to_account_info(),
mint: ctx.accounts.token.to_account_info(),
to: ctx.accounts.master_wallet_token_account.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_context = CpiContext::new(
fn send_to_master_wallet(ctx: &Context<Subscribe>, amount: u64) -> Result<()> {
send_tokens(
ctx.accounts.user_token_account.to_account_info(),
ctx.accounts.token.to_account_info(),
ctx.accounts.master_wallet_token_account.to_account_info(),
ctx.accounts.user.to_account_info(),
ctx.accounts.token_program.to_account_info(),
transfer_accounts,
);
transfer_checked(cpi_context, amount, ctx.accounts.token.decimals)?;
ctx.accounts.token.decimals,
amount,
)?;
msg!("Transferred {} tokens to master wallet.", amount);
Ok(())
}

pub fn send_to_vault(ctx: &Context<Subscribe>, amount: u64) -> Result<()> {
let transfer_accounts = TransferChecked {
from: ctx.accounts.user_token_account.to_account_info(),
mint: ctx.accounts.token.to_account_info(),
to: ctx.accounts.vault.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_context = CpiContext::new(
fn send_to_vault(ctx: &Context<Subscribe>, amount: u64) -> Result<()> {
send_tokens(
ctx.accounts.user_token_account.to_account_info(),
ctx.accounts.token.to_account_info(),
ctx.accounts.vault.to_account_info(),
ctx.accounts.user.to_account_info(),
ctx.accounts.token_program.to_account_info(),
transfer_accounts,
);
transfer_checked(cpi_context, amount, ctx.accounts.token.decimals)?;
msg!("Transferred {} tokens to vault.", amount);
ctx.accounts.token.decimals,
amount,
)?;
Ok(())
}

pub fn save_user_info(
ctx: Context<Subscribe>,
available_balance: u64,
duration: u64,
) -> Result<()> {
fn save_user_info(ctx: Context<Subscribe>, available_balance: u64, duration: u64) -> Result<()> {
let current_timestamp = Clock::get()?.unix_timestamp;
let expiration = current_timestamp + duration as i64;
let bump = ctx.bumps.user_info;
Expand All @@ -136,6 +124,5 @@ pub fn save_user_info(
expiration,
bump,
});
msg!("New subscription saved with updated expiration.");
Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use anchor_lang::prelude::*;

use anchor_spl::{
associated_token::AssociatedToken,
token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked},
};

use crate::{
error::ErrorCode, get_subscription_level, UserInfo, LOCAL_MASTER_WALLET, USER_INFO_SEED,
};

#[derive(Accounts)]
pub struct SubscribeWithVault<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(mint::token_program = token_program)]
pub token: InterfaceAccount<'info, Mint>,
#[account(
mut,
seeds = [USER_INFO_SEED, user.key().as_ref()],
bump = user_info.bump
)]
pub user_info: Account<'info, UserInfo>,
#[account(
address = LOCAL_MASTER_WALLET
)]
pub master_wallet: SystemAccount<'info>,
#[account(
mut,
associated_token::mint = token,
associated_token::authority = master_wallet,
associated_token::token_program = token_program
)]
pub master_wallet_token_account: InterfaceAccount<'info, TokenAccount>,
#[account(
mut,
associated_token::mint = token,
associated_token::authority = user_info,
associated_token::token_program = token_program,
)]
pub vault: InterfaceAccount<'info, TokenAccount>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub token_program: Interface<'info, TokenInterface>,
pub system_program: Program<'info, System>,
}

pub fn subscribe_with_vault_handler(ctx: Context<SubscribeWithVault>, amount: u64) -> Result<()> {
let current_timestamp = Clock::get()?.unix_timestamp;
if ctx.accounts.user_info.expiration > current_timestamp {
return Err(ErrorCode::AlreadySubscribed.into());
} else if ctx.accounts.user_info.available_balance < amount {
return Err(ErrorCode::InsufficientBalance.into());
} else {
let (subscription_cost, duration) = get_subscription_level(amount)?;
send_to_master_wallet(&ctx, subscription_cost)?;
msg!(
"Subscription cost of {} tokens has been sent to the master wallet.",
subscription_cost
);
update_user_info(ctx, amount, duration)?;
}
msg!("Subscription successful.");
Ok(())
}

fn send_to_master_wallet(ctx: &Context<SubscribeWithVault>, amount: u64) -> Result<()> {
let seeds = &[
USER_INFO_SEED,
ctx.accounts.user.key.as_ref(),
&[ctx.accounts.user_info.bump],
];
let signer_seeds = &[&seeds[..]];
let transfer_accounts = TransferChecked {
from: ctx.accounts.vault.to_account_info(),
mint: ctx.accounts.token.to_account_info(),
to: ctx.accounts.master_wallet_token_account.to_account_info(),
authority: ctx.accounts.user_info.to_account_info(),
};
let cpi_context = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
transfer_accounts,
signer_seeds,
);
transfer_checked(cpi_context, amount, ctx.accounts.token.decimals)?;
Ok(())
}

fn update_user_info(ctx: Context<SubscribeWithVault>, amount: u64, duration: u64) -> Result<()> {
let current_timestamp = Clock::get()?.unix_timestamp;
let expiration = current_timestamp + duration as i64;
ctx.accounts.user_info.available_balance -= amount;
ctx.accounts.user_info.expiration = expiration;
Ok(())
}
14 changes: 12 additions & 2 deletions apps/deposit-program/programs/deposit-program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
pub mod constants;
pub mod error;
pub mod instructions;
pub mod reusable;
pub mod state;

use anchor_lang::prelude::*;

pub use constants::*;
pub use instructions::*;
pub use reusable::*;
pub use state::*;

declare_id!("6vsijynzE22W8A4kkvv2Kq7a36ZEFPvJP9kBgNtN43mK");
Expand All @@ -16,10 +18,18 @@ pub mod deposit_program {
use super::*;

pub fn subscribe(ctx: Context<Subscribe>, amount: u64) -> Result<()> {
instructions::subscribe::handler(ctx, amount)
instructions::subscribe::subscribe_handler(ctx, amount)
}

pub fn refund_balance(ctx: Context<RefundBalance>) -> Result<()> {
instructions::refund_balance::handler(ctx)
instructions::refund_balance::refund_balance_handler(ctx)
}

pub fn deposit_to_vault(ctx: Context<DepositToVault>, amount: u64) -> Result<()> {
instructions::deposit_to_vault::deposit_to_vault_handler(ctx, amount)
}

pub fn subscribe_with_vault(ctx: Context<SubscribeWithVault>, amount: u64) -> Result<()> {
instructions::subscribe_with_vault::subscribe_with_vault_handler(ctx, amount)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::{error::ErrorCode, SUBSCRIPTION_LEVELS};

pub fn get_subscription_level(amount: u64) -> Result<(u64, u64), ErrorCode> {
match SUBSCRIPTION_LEVELS
.iter()
.rev()
.find(|(cost, _)| amount >= *cost)
{
Some(level) => Ok(*level),
None => Err(ErrorCode::InsufficientBalance),
}
}
Loading