Skip to content

Commit

Permalink
add create donation v2 with multi currency support
Browse files Browse the repository at this point in the history
  • Loading branch information
makarychev committed Jun 9, 2024
1 parent c035269 commit bcda92c
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 0 deletions.
1 change: 1 addition & 0 deletions programs/donaproto/src/instructions/create_donation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub fn create_donation(
donation_data.recipient = ctx.accounts.recipient.key();
donation_data.creator_data = ctx.accounts.creator_data.key();
donation_data.donation_protocol = ctx.accounts.donation_protocol.key();
donation_data.donation_mint = ctx.accounts.donation_mint.key();
donation_data.holding_wallet = ctx.accounts.holding_wallet.key();
donation_data.holding_bump = holding_bump;
donation_data.ipfs_hash = ipfs_hash;
Expand Down
121 changes: 121 additions & 0 deletions programs/donaproto/src/instructions/create_donation_v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, TokenAccount};
use raydium_amm_v3::states::PoolState;

use crate::{
errors::DonationError,
states::{AuthorizedClmmPool, CreatorData, DonationData, DonationProtocolData, MAX_IPFS_HASH_LEN},
utils::calculate_amount, AUTHORIZED_CLMM_POOL_PREFIX, CREATOR_PREFIX, HOLDING_PREFIX,
};

#[derive(Accounts)]
pub struct CreateDonationV2<'info> {
#[account(init, payer = creator_wallet_address, space = DonationData::LEN)]
pub donation_data: Account<'info, DonationData>,
#[account(
constraint = donation_protocol.donation_mint == default_donation_mint.key(),
)]
pub donation_protocol: Account<'info, DonationProtocolData>,

#[account(
constraint = holding_wallet.owner == *holding_wallet_owner.key,
constraint = holding_wallet.mint == donation_mint.key(),
)]
pub holding_wallet: Account<'info, TokenAccount>,
#[account(
seeds = [
HOLDING_PREFIX.as_bytes(),
donation_data.to_account_info().key.as_ref(),
],
bump,
)]
/// CHECK: pda account ["holding", donation_data]
holding_wallet_owner: AccountInfo<'info>,

#[account(
constraint = recipient.mint == donation_mint.key(),
)]
pub recipient: Account<'info, TokenAccount>,

#[account(mut,
constraint = creator_data.donation_protocol.key() == donation_protocol.key(),
seeds = [
CREATOR_PREFIX.as_bytes(),
donation_protocol.to_account_info().key.as_ref(),
creator_wallet_address.key().as_ref(),
],
bump,
)]
pub creator_data: Account<'info, CreatorData>,
pub donation_mint: Account<'info, Mint>,
pub default_donation_mint: Account<'info, Mint>,
#[account(
seeds = [
AUTHORIZED_CLMM_POOL_PREFIX.as_bytes(),
donation_protocol.key().as_ref(),
pool_state.key().as_ref(),
],
bump,
constraint = authorized_clmm_pool.donation_protocol == donation_protocol.key(),
constraint = authorized_clmm_pool.pool_state == pool_state.key(),
)]
pub authorized_clmm_pool: Account<'info, AuthorizedClmmPool>,
#[account(
constraint = (pool_state.load()?.token_mint_0 == donation_protocol.donation_mint && pool_state.load()?.token_mint_1 == donation_mint.key())
|| (pool_state.load()?.token_mint_1 == donation_protocol.donation_mint && pool_state.load()?.token_mint_0 == donation_mint.key()),
)]
pub pool_state: AccountLoader<'info, PoolState>,
#[account(mut)]
pub creator_wallet_address: Signer<'info>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>
}

pub fn create_donation_v2(
ctx: Context<CreateDonationV2>,
amount: u64,
ipfs_hash: String,
ending_timestamp: u64,
holding_bump: u8,
) -> Result<()> {
let now_timestamp = Clock::get().expect("Time error").unix_timestamp as u64;
if ending_timestamp <= now_timestamp {
return Err(DonationError::InvalidEndingTimestamp.into());
}
if ipfs_hash.len() > MAX_IPFS_HASH_LEN {
return Err(DonationError::IpfsHashTooLong.into());
}
if amount < ctx.accounts.donation_protocol.min_amount_to_collect {
return Err(DonationError::DonationAmountTooLow.into());
}

let donation_data = &mut ctx.accounts.donation_data;
donation_data.amount_collecting = amount;
donation_data.ending_timestamp = ending_timestamp;
donation_data.is_closed = false;
donation_data.recipient = ctx.accounts.recipient.key();
donation_data.creator_data = ctx.accounts.creator_data.key();
donation_data.donation_protocol = ctx.accounts.donation_protocol.key();
donation_data.holding_wallet = ctx.accounts.holding_wallet.key();
donation_data.donation_mint = ctx.accounts.donation_mint.key();
donation_data.holding_bump = holding_bump;
donation_data.ipfs_hash = ipfs_hash;

let default_donation_mint = &ctx.accounts.default_donation_mint;
let donation_mint = &ctx.accounts.donation_mint;
let is_default_token_mint_0 = ctx.accounts.pool_state.load()?.token_mint_0 == ctx.accounts.donation_protocol.donation_mint;

let amount = calculate_amount(
default_donation_mint.decimals,
donation_mint.decimals,
amount,
ctx.accounts.pool_state.load()?.sqrt_price_x64,
is_default_token_mint_0,
);

let creator_data = &mut ctx.accounts.creator_data;
creator_data.total_amount_collecting = creator_data.total_amount_collecting.checked_add(amount).unwrap();
creator_data.donations_created_count = creator_data.donations_created_count.checked_add(1).unwrap();

Ok(())
}
2 changes: 2 additions & 0 deletions programs/donaproto/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ pub mod authorize_pool;
pub use authorize_pool::*;
pub mod authorize_clmm;
pub use authorize_clmm::*;
pub mod create_donation_v2;
pub use create_donation_v2::*;
11 changes: 11 additions & 0 deletions programs/donaproto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use instructions::*;

pub mod errors;
pub mod states;
pub mod utils;

declare_id!("HbNNG85aBuR9W5F8YobTeDRRmXWFbDhLDS6WbLzWbLhH");

Expand Down Expand Up @@ -58,4 +59,14 @@ pub mod donaproto {
pub fn authorize_clmm_pool(ctx: Context<AuthorizeClmmPool>) -> Result<()> {
instructions::authorize_clmm_pool(ctx)
}

pub fn create_donation_v2(
ctx: Context<CreateDonationV2>,
amount: u64,
ipfs_hash: String,
ending_timestamp: u64,
holding_bump: u8,
) -> Result<()> {
instructions::create_donation_v2(ctx, amount, ipfs_hash, ending_timestamp, holding_bump)
}
}
3 changes: 3 additions & 0 deletions programs/donaproto/src/states/donation_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct DonationData {
pub donation_protocol: Pubkey,
pub holding_wallet: Pubkey,
pub creator_data: Pubkey,
pub donation_mint: Pubkey,
pub holding_bump: u8,
pub ipfs_hash: String,
}
Expand All @@ -29,6 +30,7 @@ impl DonationData {
const DONATION_PROTOCOL_LEN: usize = mem::size_of::<Pubkey>();
const HOLDING_WALLET_LEN: usize = mem::size_of::<Pubkey>();
const CREATOR_DATA_LEN: usize = mem::size_of::<Pubkey>();
const DONATION_MINT_LEN: usize = mem::size_of::<Pubkey>();
const HOLDING_BUMP_LEN: usize = mem::size_of::<u8>();
const IPFS_HASH_LEN: usize = MAX_IPFS_HASH_LEN;

Expand All @@ -41,6 +43,7 @@ impl DonationData {
+ DonationData::DONATION_PROTOCOL_LEN
+ DonationData::HOLDING_WALLET_LEN
+ DonationData::CREATOR_DATA_LEN
+ DonationData::DONATION_MINT_LEN
+ DonationData::HOLDING_BUMP_LEN
+ DonationData::IPFS_HASH_LEN;
}
59 changes: 59 additions & 0 deletions programs/donaproto/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// A library for handling Q64.64 fixed point numbers
/// copied from `raydium-clmm` program library
// TODO: write tests for this module

pub const Q64: u128 = (u64::MAX as u128) + 1; // 2^64
pub const RESOLUTION: u8 = 64;

pub fn multipler(decimals: u8) -> f64 {
(10_i32).checked_pow(decimals.try_into().unwrap()).unwrap() as f64
}

pub fn from_x64_price(price: u128) -> f64 {
price as f64 / Q64 as f64
}

pub fn sqrt_price_x64_to_price(price: u128, decimals_0: u8, decimals_1: u8) -> f64 {
from_x64_price(price).powi(2) * multipler(decimals_0) / multipler(decimals_1)
}

pub fn identify_mint_decimals(
default_donation_mint_decimals: u8,
donation_mint_decimals: u8,
is_default_token_mint_0: bool,
) -> (u8, u8) {
if is_default_token_mint_0 {
return (default_donation_mint_decimals, donation_mint_decimals);
}

(donation_mint_decimals, default_donation_mint_decimals)
}

pub fn amount_from_price(
amount: u64,
price: f64,
is_default_token_mint_0: bool,
) -> u64 {
if is_default_token_mint_0 {
return (amount as f64 / price).round() as u64;
}

(amount as f64 * price).round() as u64
}

pub fn calculate_amount(
default_donation_mint_decimals: u8,
donation_mint_decimals: u8,
amount: u64,
sqrt_price_x64: u128,
is_default_token_mint_0: bool,
) -> u64 {
let (decimals_0, decimals_1) = identify_mint_decimals(
default_donation_mint_decimals,
donation_mint_decimals,
is_default_token_mint_0,
);
let price = sqrt_price_x64_to_price(sqrt_price_x64, decimals_0, decimals_1);

amount_from_price(amount, price, is_default_token_mint_0)
}

0 comments on commit bcda92c

Please sign in to comment.