Skip to content

Commit

Permalink
Add check for token amount on lock (#1111)
Browse files Browse the repository at this point in the history
  • Loading branch information
febo authored Jun 20, 2023
1 parent bc15a89 commit 0c082d2
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 5 deletions.
44 changes: 43 additions & 1 deletion token-metadata/js/test/lock.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getAccount } from '@solana/spl-token';
import { getAccount, getOrCreateAssociatedTokenAccount } from '@solana/spl-token';
import { PublicKey } from '@solana/web3.js';
import { BN } from 'bn.js';
import spok from 'spok';
Expand All @@ -15,6 +15,7 @@ import { amman, InitTransactions, killStuckProcess } from './setup';
import { spokSameBigint } from './utils';
import { createAndMintDefaultAsset } from './utils/digital-asset-manager';
import { findTokenRecordPda } from './utils/programmable';
import { createDefaultAsset } from './utils/digital-asset-manager';

killStuckProcess();

Expand Down Expand Up @@ -737,3 +738,44 @@ test('Lock: LockedTransfer delegate lock ProgrammableNonFungible asset', async (
lockedTransfer: PublicKey.default,
});
});

test('Lock: lock Fungible asset with empty token account', async (t) => {
const API = new InitTransactions();
const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer();

// creates a fungible asset

const manager = await createDefaultAsset(
t,
connection,
API,
handler,
payer,
TokenStandard.Fungible,
);

// creates an empty token account

const emptyTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
payer,
manager.mint,
payer.publicKey,
);

// lock asset with delegate (should fail since the account is empty)

const { tx: lockTx } = await API.lock(
payer,
manager.mint,
manager.metadata,
emptyTokenAccount.address,
payer,
handler,
null,
null,
manager.masterEdition,
);

await lockTx.assertError(t, /Token account does not have enough tokens/);
});
20 changes: 16 additions & 4 deletions token-metadata/program/src/processor/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use borsh::BorshSerialize;
pub use lock::*;
use mpl_utils::assert_signer;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, program_pack::Pack,
pubkey::Pubkey, system_program, sysvar,
account_info::AccountInfo, entrypoint::ProgramResult, program::invoke,
program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, system_program, sysvar,
};
use spl_token::{
instruction::{freeze_account, thaw_account},
Expand Down Expand Up @@ -56,6 +56,7 @@ pub(crate) fn toggle_asset_state(

assert_owned_by(accounts.metadata_info, program_id)?;
assert_owned_by(accounts.mint_info, &spl_token::ID)?;
assert_owned_by(accounts.token_info, &spl_token::ID)?;

// key match

Expand All @@ -74,10 +75,14 @@ pub(crate) fn toggle_asset_state(
}

let token = Account::unpack(&accounts.token_info.try_borrow_data()?)?;
// mint must match mint account key
// token mint must match mint account key
if token.mint != *accounts.mint_info.key {
return Err(MetadataError::MintMismatch.into());
}
// and must have balance greater than 0 if we are locking
if matches!(to, TokenState::Locked) && token.amount == 0 {
return Err(MetadataError::InsufficientTokenBalance.into());
}

// authority – this can be either:
// 1. token delegate (programmable non-fungible): valid token_record.delegate
Expand Down Expand Up @@ -161,7 +166,14 @@ pub(crate) fn toggle_asset_state(
accounts.mint_info,
accounts.token_info,
)
.map_err(|_| MetadataError::InvalidAuthorityType)?;
.map_err(|error| {
let custom: ProgramError = MetadataError::InvalidDelegate.into();
if error == custom {
MetadataError::InvalidAuthorityType.into()
} else {
error
}
})?;

match to {
TokenState::Locked => {
Expand Down

0 comments on commit 0c082d2

Please sign in to comment.