Skip to content

Commit

Permalink
Merge pull request #96 from cardinal-labs/transfer
Browse files Browse the repository at this point in the history
Enable transfer
  • Loading branch information
jpbogle authored Mar 12, 2022
2 parents 995a526 + 09d33a2 commit 916cbd9
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 26 deletions.
5 changes: 4 additions & 1 deletion programs/cardinal-token-manager/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum ErrorCode {
InvalidTokenManagerTokenAccount,
#[msg("Token account not owned by issuer")]
InvalidIssuerTokenAccount,

#[msg("Token account not owned by recipient")]
InvalidRecipientTokenAccount,
#[msg("Token account not owned by invalidator")]
Expand Down Expand Up @@ -49,5 +50,7 @@ pub enum ErrorCode {
#[msg("Invalid receipt mint owner")]
InvalidReceiptMintOwner,
#[msg("Invalid receipt mint")]
InvalidReceiptMint
InvalidReceiptMint,
#[msg("Invalid current holder token account")]
InvalidCurrentTokenAccount,
}
2 changes: 1 addition & 1 deletion programs/cardinal-token-manager/src/instructions/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct ClaimCtx<'info> {
#[account(mut, constraint =
recipient_token_account.owner == recipient.key()
&& recipient_token_account.mint == token_manager.mint
@ ErrorCode::InvalidIssuerTokenAccount
@ ErrorCode::InvalidRecipientTokenAccount
)]
recipient_token_account: Box<Account<'info, TokenAccount>>,
token_program: Program<'info, Token>,
Expand Down
2 changes: 2 additions & 0 deletions programs/cardinal-token-manager/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod claim_receipt_mint;
pub mod issue;
pub mod unissue;
pub mod claim;
pub mod transfer;
pub mod invalidate;
pub mod create_mint_manager;
pub mod close_mint_manager;
Expand All @@ -26,6 +27,7 @@ pub use claim_receipt_mint::*;
pub use issue::*;
pub use unissue::*;
pub use claim::*;
pub use transfer::*;
pub use invalidate::*;
pub use create_mint_manager::*;
pub use close_mint_manager::*;
86 changes: 62 additions & 24 deletions programs/cardinal-token-manager/src/instructions/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use {
crate::{state::*, errors::ErrorCode},
anchor_lang::{prelude::*},
anchor_spl::{token::{self, Token, TokenAccount, Mint, Transfer, FreezeAccount, Approve}}
anchor_lang::{prelude::*, solana_program::program::invoke_signed, AccountsClose},
anchor_spl::{token::{self, Token, TokenAccount, Mint, Transfer, FreezeAccount, ThawAccount, Approve}},
mpl_token_metadata::{instruction::{freeze_delegated_account, thaw_delegated_account}, utils::assert_derivation},
};

#[derive(Accounts)]
Expand All @@ -21,8 +22,8 @@ pub struct TransferCtx<'info> {

// current
#[account(mut, constraint =
&& recipient_token_account.key() == token_manager.recipient_token_account
@ ErrorCode::InvalidIssuerTokenAccount
recipient_token_account.key() == token_manager.recipient_token_account
@ ErrorCode::InvalidCurrentTokenAccount
)]
current_holder_token_account: Box<Account<'info, TokenAccount>>,

Expand All @@ -32,14 +33,14 @@ pub struct TransferCtx<'info> {
#[account(mut, constraint =
recipient_token_account.owner == recipient.key()
&& recipient_token_account.mint == token_manager.mint
@ ErrorCode::InvalidIssuerTokenAccount
@ ErrorCode::InvalidRecipientTokenAccount
)]
recipient_token_account: Box<Account<'info, TokenAccount>>,

token_program: Program<'info, Token>,
}

pub fn handler(ctx: Context<TransferCtx>) -> Result<()> {
pub fn handler<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, 'remaining, 'info, TransferCtx<'info>>) -> Result<()> {
let token_manager = &mut ctx.accounts.token_manager;
token_manager.recipient_token_account = ctx.accounts.recipient_token_account.key();

Expand All @@ -49,18 +50,35 @@ pub fn handler(ctx: Context<TransferCtx>) -> Result<()> {
let token_manager_seeds = &[TOKEN_MANAGER_SEED.as_bytes(), token_manager.mint.as_ref(), &[token_manager.bump]];
let token_manager_signer = &[&token_manager_seeds[..]];

// transfer amount to recipient token account
let cpi_accounts = Transfer {
from: ctx.accounts.token_manager_token_account.to_account_info(),
to: ctx.accounts.recipient_token_account.to_account_info(),
authority: token_manager.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(token_manager_signer);
token::transfer(cpi_context, token_manager.amount)?;

// if this is a managed token, this means we will revoke it at the end of life, so we need to delegate and freeze
if token_manager.kind == TokenManagerKind::Managed as u8 {
let mint_manager_info = next_account_info(remaining_accs)?;
let mint = ctx.accounts.mint.key();
let path = &[MINT_MANAGER_SEED.as_bytes(), mint.as_ref()];
let bump_seed = assert_derivation(ctx.program_id, mint_manager_info, path)?;
let mint_manager_seeds = &[MINT_MANAGER_SEED.as_bytes(), mint.as_ref(), &[bump_seed]];
let mint_manager_signer = &[&mint_manager_seeds[..]];

// thaw recipient account
let cpi_accounts = ThawAccount {
account: ctx.accounts.current_holder_token_account.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),
authority: mint_manager_info.clone(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(mint_manager_signer);
token::thaw_account(cpi_context)?;

// transfer amount to recipient token account
let cpi_accounts = Transfer {
from: ctx.accounts.current_holder_token_account.to_account_info(),
to: ctx.accounts.recipient_token_account.to_account_info(),
authority: token_manager.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(token_manager_signer);
token::transfer(cpi_context, token_manager.amount)?;

// set account delegate of recipient token account to token manager PDA
let cpi_accounts = Approve {
to: ctx.accounts.recipient_token_account.to_account_info(),
Expand All @@ -71,13 +89,6 @@ pub fn handler(ctx: Context<TransferCtx>) -> Result<()> {
let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
token::approve(cpi_context, token_manager.amount)?;

let mint_manager_info = next_account_info(remaining_accs)?;
let mint = ctx.accounts.mint.key();
let path = &[MINT_MANAGER_SEED.as_bytes(), mint.as_ref()];
let bump_seed = assert_derivation(ctx.program_id, mint_manager_info, path)?;
let mint_manager_seeds = &[MINT_MANAGER_SEED.as_bytes(), mint.as_ref(), &[bump_seed]];
let mint_manager_signer = &[&mint_manager_seeds[..]];

// freeze recipient token account
let cpi_accounts = FreezeAccount {
account: ctx.accounts.recipient_token_account.to_account_info(),
Expand All @@ -89,13 +100,40 @@ pub fn handler(ctx: Context<TransferCtx>) -> Result<()> {
token::freeze_account(cpi_context)?;
} else if token_manager.kind == TokenManagerKind::Edition as u8 {
let remaining_accs = &mut ctx.remaining_accounts.iter();
let current_edition_info = next_account_info(remaining_accs)?;
let edition_info = next_account_info(remaining_accs)?;
let metadata_program = next_account_info(remaining_accs)?;

// edition will be validated by metadata_program
// assert_keys_eq!(metadata_program.key, mpl_token_metadata::id());
if metadata_program.key() != mpl_token_metadata::id() { return Err(error!(ErrorCode::PublicKeyMismatch)); }

invoke_signed(
&thaw_delegated_account(
*metadata_program.key,
token_manager.key(),
ctx.accounts.current_holder_token_account.key(),
*current_edition_info.key,
ctx.accounts.mint.key(),
),
&vec![
token_manager.to_account_info(),
ctx.accounts.current_holder_token_account.to_account_info(),
current_edition_info.to_account_info(),
ctx.accounts.mint.to_account_info(),
],
&[token_manager_seeds],
)?;

// transfer amount to recipient token account
let cpi_accounts = Transfer {
from: ctx.accounts.current_holder_token_account.to_account_info(),
to: ctx.accounts.recipient_token_account.to_account_info(),
authority: token_manager.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(token_manager_signer);
token::transfer(cpi_context, token_manager.amount)?;

// set account delegate of recipient token account to token manager PDA
let cpi_accounts = Approve {
to: ctx.accounts.recipient_token_account.to_account_info(),
Expand Down
4 changes: 4 additions & 0 deletions programs/cardinal-token-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ pub mod cardinal_token_manager {
claim::handler(ctx)
}

pub fn transfer<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, 'remaining, 'info, TransferCtx<'info>>) -> Result<()> {
transfer::handler(ctx)
}

pub fn invalidate<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, 'remaining, 'info, InvalidateCtx<'info>>) -> Result<()> {
invalidate::handler(ctx)
}
Expand Down
132 changes: 132 additions & 0 deletions src/idl/cardinal_token_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,47 @@ export type CardinalTokenManager = {
];
args: [];
},
{
name: "transfer";
accounts: [
{
name: "tokenManager";
isMut: true;
isSigner: false;
},
{
name: "tokenManagerTokenAccount";
isMut: true;
isSigner: false;
},
{
name: "mint";
isMut: true;
isSigner: false;
},
{
name: "currentHolderTokenAccount";
isMut: true;
isSigner: false;
},
{
name: "recipient";
isMut: true;
isSigner: true;
},
{
name: "recipientTokenAccount";
isMut: true;
isSigner: false;
},
{
name: "tokenProgram";
isMut: false;
isSigner: false;
}
];
args: [];
},
{
name: "invalidate";
accounts: [
Expand Down Expand Up @@ -629,6 +670,10 @@ export type CardinalTokenManager = {
name: "bump";
type: "u8";
},
{
name: "mint";
type: "publicKey";
},
{
name: "count";
type: "u64";
Expand All @@ -644,6 +689,14 @@ export type CardinalTokenManager = {
{
name: "mintCount";
type: "u64";
},
{
name: "tokenManager";
type: "publicKey";
},
{
name: "target";
type: "publicKey";
}
];
};
Expand All @@ -656,6 +709,14 @@ export type CardinalTokenManager = {
{
name: "mintCount";
type: "u64";
},
{
name: "tokenManager";
type: "publicKey";
},
{
name: "target";
type: "publicKey";
}
];
};
Expand Down Expand Up @@ -857,6 +918,11 @@ export type CardinalTokenManager = {
code: 6023;
name: "InvalidReceiptMint";
msg: "Invalid receipt mint";
},
{
code: 6024;
name: "InvalidCurrentTokenAccount";
msg: "Invalid current holder token account";
}
];
};
Expand Down Expand Up @@ -1273,6 +1339,47 @@ export const IDL: CardinalTokenManager = {
],
args: [],
},
{
name: "transfer",
accounts: [
{
name: "tokenManager",
isMut: true,
isSigner: false,
},
{
name: "tokenManagerTokenAccount",
isMut: true,
isSigner: false,
},
{
name: "mint",
isMut: true,
isSigner: false,
},
{
name: "currentHolderTokenAccount",
isMut: true,
isSigner: false,
},
{
name: "recipient",
isMut: true,
isSigner: true,
},
{
name: "recipientTokenAccount",
isMut: true,
isSigner: false,
},
{
name: "tokenProgram",
isMut: false,
isSigner: false,
},
],
args: [],
},
{
name: "invalidate",
accounts: [
Expand Down Expand Up @@ -1492,6 +1599,10 @@ export const IDL: CardinalTokenManager = {
name: "bump",
type: "u8",
},
{
name: "mint",
type: "publicKey",
},
{
name: "count",
type: "u64",
Expand All @@ -1508,6 +1619,14 @@ export const IDL: CardinalTokenManager = {
name: "mintCount",
type: "u64",
},
{
name: "tokenManager",
type: "publicKey",
},
{
name: "target",
type: "publicKey",
},
],
},
},
Expand All @@ -1520,6 +1639,14 @@ export const IDL: CardinalTokenManager = {
name: "mintCount",
type: "u64",
},
{
name: "tokenManager",
type: "publicKey",
},
{
name: "target",
type: "publicKey",
},
],
},
},
Expand Down Expand Up @@ -1721,5 +1848,10 @@ export const IDL: CardinalTokenManager = {
name: "InvalidReceiptMint",
msg: "Invalid receipt mint",
},
{
code: 6024,
name: "InvalidCurrentTokenAccount",
msg: "Invalid current holder token account",
},
],
};

0 comments on commit 916cbd9

Please sign in to comment.