Skip to content

Commit

Permalink
fix: binding check
Browse files Browse the repository at this point in the history
  • Loading branch information
CanvasL committed Sep 12, 2024
1 parent 145862d commit d1db9a3
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 50 deletions.
4 changes: 4 additions & 0 deletions extensions/puppet/programs/puppet/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ pub enum ErrorCode {

#[msg("The provided nft does not match the binding.")]
NFTDoesNotMatch,

DeviceAssociatedTokenDoesNotMatch,

InvalidDeviceMintPDA,
}
64 changes: 44 additions & 20 deletions extensions/puppet/programs/puppet/src/instructions/bind.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::errors::ErrorCode;
use crate::state::{DeviceBinding, DeviceCollectionBinding, NFTBinding, NFTCollectionBinding};
use crate::state::{DeviceBinding, DeviceCollectionBinding, MplBinding, MplCollectionBinding};
use anchor_lang::prelude::*;
use anchor_spl::associated_token::get_associated_token_address_with_program_id;
use anchor_spl::token::TokenAccount;

#[derive(Accounts)]
Expand All @@ -9,33 +10,32 @@ use anchor_spl::token::TokenAccount;
)]
pub struct Bind<'info> {
#[account(
constraint = nft_account.key() == params.nft,
constraint = nft_account.owner == payer.key() @ ErrorCode::PayerDoesNotOwnNFT
constraint = mpl_associated_token.key() == params.mpl_ata,
constraint = mpl_associated_token.owner == payer.key() @ ErrorCode::PayerDoesNotOwnNFT
)]
pub nft_account: Account<'info, TokenAccount>,
pub mpl_associated_token: Account<'info, TokenAccount>,
#[account(
constraint = device_account.key() == params.device,
constraint = device_account.owner == payer.key() @ ErrorCode::PayerDoesNotOwnDevice
constraint = device_associated_token.owner == payer.key() @ ErrorCode::PayerDoesNotOwnDevice
)]
pub device_account: Account<'info, TokenAccount>,
pub device_associated_token: Account<'info, TokenAccount>,
#[account(
init_if_needed,
payer = payer,
space = 8 + 32 + 1,
seeds = [b"device_binding", device_account.key().as_ref()],
seeds = [b"device_binding", device_associated_token.key().as_ref()],
bump
)]
pub device_binding: Account<'info, DeviceBinding>,
#[account(
init_if_needed,
payer = payer,
space = 8 + 32 + 1,
seeds = [b"nft_binding", nft_account.key().as_ref()],
seeds = [b"mpl_binding", mpl_associated_token.key().as_ref()],
bump
)]
pub nft_binding: Account<'info, NFTBinding>,
pub mpl_binding: Account<'info, MplBinding>,
pub device_collection_binding: Account<'info, DeviceCollectionBinding>,
pub nft_collection_binding: Account<'info, NFTCollectionBinding>,
pub mpl_collection_binding: Account<'info, MplCollectionBinding>,
#[account(mut)]
pub payer: Signer<'info>,
pub rent: Sysvar<'info, Rent>,
Expand All @@ -44,40 +44,64 @@ pub struct Bind<'info> {

#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
pub struct BindParams {
pub dephy_id_program: Pubkey,
pub device: Pubkey,
pub nft: Pubkey,
pub device_mint_bump: u8,
pub mpl_ata: Pubkey,
}

pub fn bind(ctx: Context<Bind>, params: BindParams) -> Result<()> {
let device_binding = &mut ctx.accounts.device_binding;
let nft_binding = &mut ctx.accounts.nft_binding;
let mpl_binding = &mut ctx.accounts.mpl_binding;

let device_mint_pubkey = Pubkey::create_program_address(
&[
b"DePHY_ID-DEVICE",
ctx.accounts.mpl_collection_binding.device_collection.as_ref(),
params.device.as_ref(),
&[params.device_mint_bump],
],
&params.dephy_id_program,
).map_err(|_| ErrorCode::InvalidDeviceMintPDA)?;

let device_ata = get_associated_token_address_with_program_id(
&ctx.accounts.payer.key(),
&device_mint_pubkey,
&params.dephy_id_program,
);

require_keys_eq!(
ctx.accounts.device_associated_token.key(),
device_ata,
ErrorCode::DeviceAssociatedTokenDoesNotMatch
);

require_keys_eq!(
ctx.accounts.device_account.mint,
ctx.accounts.nft_collection_binding.device_collection,
ctx.accounts.device_associated_token.mint,
device_mint_pubkey,
ErrorCode::DeviceCollectionDoesNotMatch
);

require_keys_eq!(
ctx.accounts.nft_account.mint,
ctx.accounts.mpl_associated_token.mint,
ctx.accounts.device_collection_binding.nft_collection,
ErrorCode::NFTCollectionDoesNotMatch
);

require_keys_eq!(
device_binding.nft,
device_binding.mpl_ata,
Pubkey::default(),
ErrorCode::DeviceAlreadyBound
);

require_keys_eq!(
nft_binding.device,
mpl_binding.device_ata,
Pubkey::default(),
ErrorCode::NFTAlreadyBound
);

device_binding.nft = params.nft;
nft_binding.device = params.device;
device_binding.mpl_ata = params.mpl_ata;
mpl_binding.device_ata = params.device;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::errors::ErrorCode;
use crate::state::{DeviceCollectionBinding, NFTCollectionBinding};
use crate::state::{DeviceCollectionBinding, MplCollectionBinding};
use anchor_lang::prelude::*;
use anchor_spl::token::Mint;

Expand All @@ -10,32 +10,32 @@ use anchor_spl::token::Mint;
pub struct BindCollection<'info> {
/// CHECK:
#[account(
constraint = product_mint_account.key() == params.device_collection,
constraint = product_mint.key() == params.device_collection,
seeds = [b"DePHY_ID-PRODUCT", payer.key().as_ref(), params.product_metadata_name.as_ref()],
bump = params.product_mint_bump,
seeds::program = params.dephy_id_program.key()
)]
pub product_mint_account: AccountInfo<'info>,
pub product_mint: AccountInfo<'info>,
#[account(
constraint = nft_mint_account.key() == params.nft_collection,
constraint = mpl_mint.key() == params.nft_collection,
)]
pub nft_mint_account: Account<'info, Mint>,
pub mpl_mint: Account<'info, Mint>,
#[account(
init_if_needed,
payer = payer,
space = 8 + 32 + 1,
seeds = [b"device_collection_binding", product_mint_account.key().as_ref()],
seeds = [b"device_collection_binding", product_mint.key().as_ref()],
bump
)]
pub device_collection_binding: Account<'info, DeviceCollectionBinding>,
#[account(
init_if_needed,
payer = payer,
space = 8 + 32 + 1,
seeds = [b"nft_collection_binding", nft_mint_account.key().as_ref()],
seeds = [b"mpl_collection_binding", mpl_mint.key().as_ref()],
bump
)]
pub nft_collection_binding: Account<'info, NFTCollectionBinding>,
pub mpl_collection_binding: Account<'info, MplCollectionBinding>,
#[account(mut)]
pub payer: Signer<'info>,
pub rent: Sysvar<'info, Rent>,
Expand All @@ -53,7 +53,7 @@ pub struct BindCollectionParams {

pub fn bind_collection(ctx: Context<BindCollection>, params: BindCollectionParams) -> Result<()> {
let device_collection_binding = &mut ctx.accounts.device_collection_binding;
let nft_collection_binding = &mut ctx.accounts.nft_collection_binding;
let mpl_collection_binding = &mut ctx.accounts.mpl_collection_binding;

require_keys_eq!(
device_collection_binding.nft_collection,
Expand All @@ -62,13 +62,13 @@ pub fn bind_collection(ctx: Context<BindCollection>, params: BindCollectionParam
);

require_keys_eq!(
nft_collection_binding.device_collection,
mpl_collection_binding.device_collection,
Pubkey::default(),
ErrorCode::NFTCollectionAlreadyBound
);

device_collection_binding.nft_collection = params.nft_collection;
nft_collection_binding.device_collection = params.device_collection;
mpl_collection_binding.device_collection = params.device_collection;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::errors::ErrorCode;
use crate::state::{DeviceBinding, NFTBinding};
use crate::state::{DeviceBinding, MplBinding};
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct Bound<'info> {
pub device_binding: Account<'info, DeviceBinding>,
pub nft_binding: Account<'info, NFTBinding>,
pub mpl_binding: Account<'info, MplBinding>,
}

pub fn check_bound_by_device(ctx: Context<Bound>, device: Pubkey) -> Result<bool> {
if ctx.accounts.nft_binding.device != device {
if ctx.accounts.mpl_binding.device_ata != device {
return Err(ErrorCode::DeviceDoesNotMatch.into());
}
Ok(ctx.accounts.device_binding.nft != Pubkey::default())
Ok(ctx.accounts.device_binding.mpl_ata != Pubkey::default())
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use anchor_lang::prelude::*;
use super::Bound;

pub fn check_bound_by_nft(ctx: Context<Bound>, nft: Pubkey) -> Result<bool> {
if ctx.accounts.device_binding.nft != nft {
if ctx.accounts.device_binding.mpl_ata != nft {
return Err(ErrorCode::NFTDoesNotMatch.into());
}
Ok(ctx.accounts.nft_binding.device != Pubkey::default())
Ok(ctx.accounts.mpl_binding.device_ata != Pubkey::default())
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use anchor_lang::prelude::*;
use super::Bound;

pub fn get_device_by_nft(ctx: Context<Bound>, nft: Pubkey) -> Result<Pubkey> {
if ctx.accounts.device_binding.nft != nft {
if ctx.accounts.device_binding.mpl_ata != nft {
return Err(ErrorCode::NFTDoesNotMatch.into());
}
Ok(ctx.accounts.nft_binding.device)
Ok(ctx.accounts.mpl_binding.device_ata)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use anchor_lang::prelude::*;
use super::Bound;

pub fn get_nft_by_device(ctx: Context<Bound>, device: Pubkey) -> Result<Pubkey> {
if ctx.accounts.nft_binding.device != device {
if ctx.accounts.mpl_binding.device_ata != device {
return Err(ErrorCode::DeviceDoesNotMatch.into());
}
Ok(ctx.accounts.device_binding.nft)
Ok(ctx.accounts.device_binding.mpl_ata)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ use anchor_lang::prelude::*;

#[account]
pub struct DeviceBinding {
pub nft: Pubkey,
pub mpl_ata: Pubkey,
pub bump: u8,
}
8 changes: 4 additions & 4 deletions extensions/puppet/programs/puppet/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
pub mod device_collection_binding;
pub mod nft_collection_binding;
pub mod mpl_collection_binding;
pub mod device_binding;
pub mod nft_binding;
pub mod mpl_binding;

pub use device_collection_binding::*;
pub use nft_collection_binding::*;
pub use mpl_collection_binding::*;
pub use device_binding::*;
pub use nft_binding::*;
pub use mpl_binding::*;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;

#[account]
pub struct NFTBinding {
pub device: Pubkey,
pub struct MplBinding {
pub device_ata: Pubkey,
pub bump: u8,
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;

#[account]
pub struct NFTCollectionBinding {
pub struct MplCollectionBinding {
pub device_collection: Pubkey,
pub bump: u8,
}
2 changes: 1 addition & 1 deletion extensions/puppet/tests/puppet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe("puppet program", () => {
);

const [nftBindingPubkey] = PublicKey.findProgramAddressSync(
[Buffer.from("nft_binding"), nftAccount.toBuffer()],
[Buffer.from("mpl_binding"), nftAccount.toBuffer()],
program.programId
);

Expand Down

0 comments on commit d1db9a3

Please sign in to comment.