Skip to content

Commit

Permalink
wip: new core auth rearchitecture
Browse files Browse the repository at this point in the history
  • Loading branch information
nhanphan committed Mar 8, 2024
1 parent 51a8584 commit 702c749
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 67 deletions.
15 changes: 9 additions & 6 deletions clients/js/src/generated/instructions/mintV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export type MintV2InstructionAccounts = {
payer?: Signer;
/** Minter account for validation and non-SOL fees. */
minter?: Signer;
/** Optionally mint to different owner */
owner?: PublicKey | Pda;
/**
* Mint account of the NFT. The account will be initialized if necessary.
*
Expand Down Expand Up @@ -171,25 +173,26 @@ export function mintV2(
},
payer: { index: 4, isWritable: true, value: input.payer ?? null },
minter: { index: 5, isWritable: true, value: input.minter ?? null },
asset: { index: 6, isWritable: true, value: input.asset ?? null },
collection: { index: 7, isWritable: true, value: input.collection ?? null },
owner: { index: 6, isWritable: false, value: input.owner ?? null },
asset: { index: 7, isWritable: true, value: input.asset ?? null },
collection: { index: 8, isWritable: true, value: input.collection ?? null },
mplCoreProgram: {
index: 8,
index: 9,
isWritable: false,
value: input.mplCoreProgram ?? null,
},
systemProgram: {
index: 9,
index: 10,
isWritable: false,
value: input.systemProgram ?? null,
},
sysvarInstructions: {
index: 10,
index: 11,
isWritable: false,
value: input.sysvarInstructions ?? null,
},
recentSlothashes: {
index: 11,
index: 12,
isWritable: false,
value: input.recentSlothashes ?? null,
},
Expand Down
49 changes: 49 additions & 0 deletions clients/js/test/mintV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,55 @@ test('it can mint from a candy guard with guards', async (t) => {
t.like(candyMachineAccount, <CandyMachine>{ itemsRedeemed: 1n });
});

test('it can mint from a candy guard with guards to different owner', async (t) => {
// Given a candy machine with some guards.
const umi = await createUmi();
const collection = (await createCollection(umi)).publicKey;
const owner = generateSigner(umi).publicKey;
const destination = generateSigner(umi).publicKey;
const candyMachineSigner = await createV2(umi, {
collection,
configLines: [{ name: 'Degen #1', uri: 'https://example.com/degen/1' }],
guards: {
botTax: { lamports: sol(0.01), lastInstruction: true },
solPayment: { lamports: sol(2), destination },
},
});
const candyMachine = candyMachineSigner.publicKey;

// When we mint from the candy guard.
const mint = generateSigner(umi);
const minter = generateSigner(umi);
const payer = await generateSignerWithSol(umi, sol(10));
await transactionBuilder()
.add(setComputeUnitLimit(umi, { units: 600_000 }))
.add(
mintV2(umi, {
candyMachine,
asset: mint,
payer,
minter,
owner,
collection,
mintArgs: {
solPayment: { destination },
},
})
)
.sendAndConfirm(umi);

// Then the mint was successful.
await assertSuccessfulMint(t, umi, { mint, owner, name: 'Degen #1' });

// And the payer was charged.
const payerBalance = await umi.rpc.getBalance(payer.publicKey);
t.true(isEqualToAmount(payerBalance, sol(8), sol(0.1)));

// And the candy machine was updated.
const candyMachineAccount = await fetchCandyMachine(umi, candyMachine);
t.like(candyMachineAccount, <CandyMachine>{ itemsRedeemed: 1n });
});

test('it can mint from a candy guard with groups', async (t) => {
// Given a candy machine with guard groups.
const umi = await createUmi();
Expand Down
6 changes: 2 additions & 4 deletions clients/js/test/setCollectionV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import {
import test from 'ava';

import { setComputeUnitLimit } from '@metaplex-foundation/mpl-toolbox';
import { PluginType, authority, removeCollectionPluginAuthority } from '@metaplex-foundation/mpl-core';
import { PluginType, revokeCollectionPluginAuthority } from '@metaplex-foundation/mpl-core';
import {
CandyMachine,
fetchCandyMachine,
findCandyMachineAuthorityPda,
mintAssetFromCandyMachine,
setCollectionV2,
} from '../src';
Expand Down Expand Up @@ -134,11 +133,10 @@ test.only('it can set the same collection of a candy machine when mint is in pro

// And we remove delegate authority from the collection.

await removeCollectionPluginAuthority(umi, {
await revokeCollectionPluginAuthority(umi, {
collection: collectionA.publicKey,
authority: collectionUpdateAuthorityA,
pluginType: PluginType.UpdateDelegate,
authorityToRemove: authority('Pubkey', {address: findCandyMachineAuthorityPda(umi, { candyMachine: candyMachine.publicKey })[0]})
}).sendAndConfirm(umi)


Expand Down
9 changes: 9 additions & 0 deletions idls/candy_guard.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@
"Minter account for validation and non-SOL fees."
]
},
{
"name": "owner",
"isMut": false,
"isSigner": false,
"isOptional": true,
"docs": [
"Optionally mint to different owner"
]
},
{
"name": "asset",
"isMut": true,
Expand Down
20 changes: 4 additions & 16 deletions programs/candy-guard/program/src/guards/freeze_sol_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use mpl_candy_machine_core_asset::CandyMachine;
use mpl_core::{
accounts::Asset,
instructions::{
AddPluginAuthorityCpiBuilder, AddPluginCpiBuilder, RemovePluginAuthorityCpiBuilder,
ApprovePluginAuthorityCpiBuilder, AddPluginCpiBuilder, RevokePluginAuthorityCpiBuilder,
UpdatePluginCpiBuilder,
},
types::{Authority, Freeze, Plugin, PluginType},
Expand Down Expand Up @@ -345,7 +345,7 @@ pub fn freeze_nft(
.plugin(Plugin::Freeze(Freeze { frozen: true }))
.invoke()?;

AddPluginAuthorityCpiBuilder::new(&ctx.accounts.mpl_core_program)
ApprovePluginAuthorityCpiBuilder::new(&ctx.accounts.mpl_core_program)
.authority(&ctx.accounts.minter)
.asset(&ctx.accounts.asset)
.collection(Some(&ctx.accounts.collection))
Expand All @@ -355,18 +355,9 @@ pub fn freeze_nft(
.plugin_type(PluginType::Freeze)
.payer(Some(&ctx.accounts.payer))
.system_program(&ctx.accounts.system_program)
.invoke()?;

// remove owner from authorities when freezing
RemovePluginAuthorityCpiBuilder::new(&ctx.accounts.mpl_core_program)
.authority(&ctx.accounts.minter)
.asset(&ctx.accounts.asset)
.plugin_type(PluginType::Freeze)
.authority_to_remove(Authority::Owner)
.payer(Some(&ctx.accounts.payer))
.system_program(&ctx.accounts.system_program)
.invoke()
.map_err(|error| error.into())

}

/// Helper function to initialize the freeze pda.
Expand Down Expand Up @@ -536,14 +527,11 @@ pub fn thaw_nft<'info>(
.system_program(system_program)
.invoke_signed(&[&signer])?;

RemovePluginAuthorityCpiBuilder::new(mpl_core_program)
RevokePluginAuthorityCpiBuilder::new(mpl_core_program)
.authority(freeze_pda)
.collection(Some(collection))
.asset(asset)
.plugin_type(PluginType::Freeze)
.authority_to_remove(Authority::Pubkey {
address: freeze_pda.key(),
})
.payer(Some(&ctx.accounts.payer))
.system_program(system_program)
.invoke_signed(&[&signer])?;
Expand Down
14 changes: 12 additions & 2 deletions programs/candy-guard/program/src/instructions/mint_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ use crate::{
utils::cmp_pubkeys,
};

use super::{MintAccounts, Token};
use super::{MintAccounts};

pub fn mint_v2<'info>(
ctx: Context<'_, '_, '_, 'info, MintV2<'info>>,
mint_args: Vec<u8>,
label: Option<String>,
) -> Result<()> {
let owner_info = if let Some(owner) = ctx.accounts.owner.as_ref() {
owner.to_account_info()
} else {
ctx.accounts.minter.to_account_info()
};

let accounts = MintAccounts {
candy_guard: &ctx.accounts.candy_guard,
candy_machine: &ctx.accounts.candy_machine,
Expand All @@ -26,6 +32,7 @@ pub fn mint_v2<'info>(
asset: ctx.accounts.asset.to_account_info(),
payer: ctx.accounts.payer.to_account_info(),
minter: ctx.accounts.minter.to_account_info(),
owner: owner_info,
recent_slothashes: ctx.accounts.recent_slothashes.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
sysvar_instructions: ctx.accounts.sysvar_instructions.to_account_info(),
Expand Down Expand Up @@ -130,7 +137,7 @@ fn cpi_mint(ctx: &EvaluationContext) -> Result<()> {
authority_pda: ctx.accounts.candy_machine_authority_pda.clone(),
mint_authority: candy_guard.to_account_info(),
payer: ctx.accounts.payer.clone(),
asset_owner: ctx.accounts.minter.clone(),
asset_owner: ctx.accounts.owner.clone(),
asset: ctx.accounts.asset.clone(),
collection: ctx.accounts.collection.clone(),
mpl_core_program: ctx.accounts.mpl_core_program.clone(),
Expand Down Expand Up @@ -194,6 +201,9 @@ pub struct MintV2<'info> {
#[account(mut)]
minter: Signer<'info>,

/// Optionally mint to different owner
owner: Option<UncheckedAccount<'info>>,

/// Mint account of the NFT. The account will be initialized if necessary.
///
/// Must be a signer if:
Expand Down
3 changes: 1 addition & 2 deletions programs/candy-guard/program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ pub(crate) struct MintAccounts<'b, 'c, 'info> {
pub(crate) candy_machine_authority_pda: AccountInfo<'info>,
pub(crate) payer: AccountInfo<'info>,
pub(crate) minter: AccountInfo<'info>,
pub(crate) owner: AccountInfo<'info>,
pub(crate) asset: AccountInfo<'info>,
pub(crate) collection: AccountInfo<'info>,
pub(crate) _candy_machine_program: AccountInfo<'info>,
// pub(crate) token_metadata_program: AccountInfo<'info>,
pub(crate) mpl_core_program: AccountInfo<'info>,
// pub(crate) spl_token_program: AccountInfo<'info>,
pub(crate) system_program: AccountInfo<'info>,
pub(crate) sysvar_instructions: AccountInfo<'info>,
pub(crate) recent_slothashes: AccountInfo<'info>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ pub fn initialize_v2(
ctx: Context<InitializeV2>,
data: CandyMachineData,
) -> Result<()> {
let required_length = data.get_space_for_candy()?;


let candy_machine_account = &mut ctx.accounts.candy_machine;

let mut candy_machine = CandyMachine {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ pub(crate) fn process_mint_asset(
return err!(CandyError::IncorrectOwner);
}

let (auths, _, _) = fetch_plugin::<Collection, UpdateDelegate>(&accounts.collection, PluginType::UpdateDelegate)?;
let (auth, _, _) = fetch_plugin::<Collection, UpdateDelegate>(&accounts.collection, PluginType::UpdateDelegate)?;

assert_plugin_pubkey_authority(&auths, &accounts.authority_pda.key())?;
assert_plugin_pubkey_authority(&auth, &accounts.authority_pda.key())?;

// (2) selecting an item to mint

Expand Down Expand Up @@ -213,13 +213,6 @@ fn create_and_mint(
.as_ref()
.ok_or(CandyError::MissingInstructionsSysvar)?;

msg!("authority_pda {:?}", accounts.authority_pda);

let pda = Pubkey::find_program_address(&[
AUTHORITY_SEED.as_bytes(),
candy_machine_key.as_ref(),], &crate::ID);
msg!("generated_pda {:?}", pda);

CreateCpiBuilder::new(&accounts.mpl_core_program)
.payer(&accounts.payer)
.asset(&accounts.asset)
Expand Down
40 changes: 15 additions & 25 deletions programs/candy-machine-core/program/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anchor_lang::prelude::*;
use arrayref::array_ref;
use mpl_core::{accounts::Collection, fetch_plugin, instructions::{AddCollectionPluginAuthorityCpiBuilder, AddCollectionPluginCpiBuilder, RemoveCollectionPluginAuthorityCpiBuilder }, types::{Authority, Plugin, PluginType, UpdateDelegate}};
use mpl_core::{accounts::Collection, fetch_plugin, instructions::{ApproveCollectionPluginAuthorityCpiBuilder, AddCollectionPluginCpiBuilder, RevokeCollectionPluginAuthorityCpiBuilder }, types::{Authority, Plugin, PluginType, UpdateDelegate}};
use mpl_token_metadata::{
accounts::Metadata,
instructions::{
Expand Down Expand Up @@ -276,18 +276,14 @@ pub fn revoke_collection_authority_helper(
}

pub fn assert_plugin_pubkey_authority(
auths: &Vec<Authority>,
auth: &Authority,
authority: &Pubkey,
) -> Result<()> {
if auths.iter().any(|auth| {
match auth {
Authority::Pubkey { address } => cmp_pubkeys(address, &authority),
_ => false
}
}) {
return Ok(())
if(*auth == Authority::Pubkey { address: *authority }) {
Ok(())
} else {
err!(CandyError::IncorrectPluginAuthority)
}
err!(CandyError::IncorrectPluginAuthority)
}


Expand All @@ -305,15 +301,12 @@ pub fn approve_asset_collection_delegate(accounts: ApproveAssetDelegateHelperAcc
}

// add CM authority to collection if it doesn't exist
let (auths, _, _) = fetch_plugin::<Collection, UpdateDelegate>(&accounts.collection, PluginType::UpdateDelegate)?;

if !auths.iter().any(|auth| {
match auth {
Authority::Pubkey { address } => cmp_pubkeys(address, &accounts.authority_pda.key()),
_ => false
}
}) {
AddCollectionPluginAuthorityCpiBuilder::new(&accounts.mpl_core_program)
let (auth, _, _) = fetch_plugin::<Collection, UpdateDelegate>(&accounts.collection, PluginType::UpdateDelegate)?;
let auth_to_add = Authority::Pubkey {
address: accounts.authority_pda.key()
};
if auth_to_add != auth {
ApproveCollectionPluginAuthorityCpiBuilder::new(&accounts.mpl_core_program)
.collection(&accounts.collection)
.authority(&accounts.collection_update_authority)
.plugin_type(PluginType::UpdateDelegate)
Expand All @@ -337,21 +330,18 @@ pub fn revoke_asset_collection_delegate(
let maybe_update_delegate_plugin = mpl_core::fetch_plugin::<Collection, UpdateDelegate>(&accounts.collection, PluginType::UpdateDelegate);

let has_auth = match maybe_update_delegate_plugin {
Ok((auths, _, _)) => auths.contains(&Authority::Pubkey {
Ok((auth, _, _)) => auth == Authority::Pubkey {
address: accounts.authority_pda.key()
}),
},
_ => false,
};

if has_auth {
RemoveCollectionPluginAuthorityCpiBuilder::new(&accounts.mpl_core_program)
RevokeCollectionPluginAuthorityCpiBuilder::new(&accounts.mpl_core_program)
.collection(&accounts.collection)
.authority(&accounts.authority_pda)
.plugin_type(PluginType::UpdateDelegate)
.system_program(&accounts.system_program)
.authority_to_remove(Authority::Pubkey {
address: accounts.authority_pda.key()
})
.payer(Some(&accounts.payer))
.invoke_signed(&[&[
AUTHORITY_SEED.as_bytes(),
Expand Down

0 comments on commit 702c749

Please sign in to comment.