Skip to content
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion toolbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "tensor-toolbox"
description = "Toolbox of useful Rust utilities for Tensor Foundation's Solana programs"
repository = "https://github.com/tensor-foundation/toolbox"
homepage = "https://github.com/tensor-foundation/toolbox"
version = "0.8.0"
version = "0.8.1"
edition = "2021"
readme = "../README.md"
license = "Apache-2.0"
Expand Down
74 changes: 53 additions & 21 deletions toolbox/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ use anchor_lang::{
prelude::*,
solana_program::{program::invoke, pubkey::Pubkey, system_instruction, system_program},
};
use anchor_spl::{associated_token::AssociatedToken, token_interface::TokenInterface};
use anchor_spl::{
associated_token::AssociatedToken,
token::spl_token,
token_2022::spl_token_2022,
token_interface::{TokenAccount, TokenInterface},
};
use mpl_token_metadata::types::TokenStandard;
use std::slice::Iter;
use tensor_vipers::prelude::*;
Expand Down Expand Up @@ -61,6 +66,8 @@ macro_rules! shard_num {
};
}

pub const SPL_TOKEN_IDS: [Pubkey; 2] = [spl_token::ID, spl_token_2022::ID];

pub struct CalcFeesArgs {
pub amount: u64,
pub total_fee_bps: u64,
Expand Down Expand Up @@ -297,7 +304,7 @@ pub fn transfer_creators_fee<'a, 'info>(
let pct = creator.share as u64;
let creator_fee = unwrap_checked!({ pct.checked_mul(creator_fee)?.checked_div(100) });

let current_creator_ata_info = match mode {
let current_creator_ta_info = match mode {
CreatorFeeMode::Sol { from: _ } => {
// Prevents InsufficientFundsForRent, where creator acc doesn't have enough fee
// https://explorer.solana.com/tx/vY5nYA95ELVrs9SU5u7sfU2ucHj4CRd3dMCi1gWrY7MSCBYQLiPqzABj9m8VuvTLGHb9vmhGaGY7mkqPa1NLAFE
Expand All @@ -323,6 +330,7 @@ pub fn transfer_creators_fee<'a, 'info>(
};

remaining_fee = unwrap_int!(remaining_fee.checked_sub(creator_fee));

if creator_fee > 0 {
match mode {
CreatorFeeMode::Sol { from } => match from {
Expand Down Expand Up @@ -352,32 +360,56 @@ pub fn transfer_creators_fee<'a, 'info>(
system_program,
currency,
from,
from_token_acc: from_ata,
from_token_acc: from_ta,
rent_payer,
} => {
let creator_ata_info =
unwrap_opt!(current_creator_ata_info, "missing creator ata");

anchor_spl::associated_token::create_idempotent(CpiContext::new(
associated_token_program.to_account_info(),
anchor_spl::associated_token::Create {
payer: rent_payer.to_account_info(),
associated_token: creator_ata_info.to_account_info(),
authority: current_creator_info.to_account_info(),
mint: currency.to_account_info(),
system_program: system_program.to_account_info(),
token_program: token_program.to_account_info(),
},
))?;
let creator_ta_info =
unwrap_opt!(current_creator_ta_info, "missing creator ata");

// Creators can change the owner of their ATA to someone else, causing the instruction calling this
// function to fail.
// To prevent this, we don't idempotently create the ATA. Instead we check if the passed in token
// account exists, and if it is the correct mint and owner, otherwise we create the ATA.

if creator_ta_info.data_is_empty()
&& creator_ta_info.owner == &system_program::ID
{
anchor_spl::associated_token::create(CpiContext::new(
associated_token_program.to_account_info(),
anchor_spl::associated_token::Create {
payer: rent_payer.to_account_info(),
associated_token: creator_ta_info.to_account_info(),
authority: current_creator_info.to_account_info(),
mint: currency.to_account_info(),
system_program: system_program.to_account_info(),
token_program: token_program.to_account_info(),
},
))?;
} else {
// Validate the owner is a SPL token program.
require!(
SPL_TOKEN_IDS.contains(creator_ta_info.owner),
ErrorCode::InvalidProgramId
);
// Validate the mint and owner.
let creator_ta =
TokenAccount::try_deserialize(&mut &creator_ta_info.data.borrow()[..])?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this check that the account is owned by token program?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but the transfer will fail if it's not owned by a token program. However, explicit checks are better than implicit so I added an account owner check here.


require!(creator_ta.mint == currency.key(), TensorError::InvalidMint);
require!(
creator_ta.owner == current_creator_info.key(),
TensorError::InvalidOwner
);
}

match token_program.key() {
anchor_spl::token::ID => {
anchor_spl::token::transfer(
CpiContext::new(
token_program.to_account_info(),
anchor_spl::token::Transfer {
from: from_ata.to_account_info(),
to: creator_ata_info.to_account_info(),
from: from_ta.to_account_info(),
to: creator_ta_info.to_account_info(),
authority: from.to_account_info(),
},
),
Expand All @@ -392,9 +424,9 @@ pub fn transfer_creators_fee<'a, 'info>(
CpiContext::new(
token_program.to_account_info(),
anchor_spl::token_interface::TransferChecked {
from: from_ata.to_account_info(),
from: from_ta.to_account_info(),
mint: currency.to_account_info(),
to: creator_ata_info.to_account_info(),
to: creator_ta_info.to_account_info(),
authority: from.to_account_info(),
},
),
Expand Down
6 changes: 6 additions & 0 deletions toolbox/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ pub enum TensorError {

#[msg("invalid edition")]
InvalidEdition = 9012,

#[msg("invalid mint")]
InvalidMint = 9013,

#[msg("invalid owner")]
InvalidOwner = 9014,
}