Skip to content

Commit

Permalink
Fix/allow double signer (#1059)
Browse files Browse the repository at this point in the history
* fix clippy lints

* remove double signer restriction and add test

* regen JS api
  • Loading branch information
samuelvanderwaal authored Mar 31, 2023
1 parent b5f1fdd commit 4021e11
Show file tree
Hide file tree
Showing 8 changed files with 2,255 additions and 78 deletions.
2,109 changes: 2,042 additions & 67 deletions auction-house/js/idl/auction_house.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions auction-house/program/src/bid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,8 @@ pub fn bid_logic<'info>(
],
)?;
}

#[allow(clippy::explicit_auto_deref)]
sol_memset(
*ts_info.try_borrow_mut_data()?,
trade_state_bump,
Expand Down Expand Up @@ -954,6 +956,7 @@ pub fn auctioneer_bid_logic<'info>(
],
)?;
}
#[allow(clippy::explicit_auto_deref)]
sol_memset(
*ts_info.try_borrow_mut_data()?,
trade_state_bump,
Expand Down
2 changes: 2 additions & 0 deletions auction-house/program/src/cancel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ fn cancel_logic<'c, 'info>(
.lamports()
.checked_add(curr_lamp)
.ok_or(AuctionHouseError::NumericalOverflow)?;

#[allow(clippy::explicit_auto_deref)]
sol_memset(*trade_state.try_borrow_mut_data()?, 0, TRADE_STATE_SIZE);

Ok(())
Expand Down
2 changes: 2 additions & 0 deletions auction-house/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//!
//! Full docs can be found [here](https://docs.metaplex.com/auction-house/definition).
#![allow(clippy::result_large_err)]

pub mod auctioneer;
pub mod bid;
pub mod cancel;
Expand Down
6 changes: 1 addition & 5 deletions auction-house/program/src/sell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,7 @@ fn sell_logic<'c, 'info>(
// 1. The wallet being a signer is the only condition in which an NFT can sell at a price of 0.
// If the user does list at 0 then auction house can change the sale price if the 'can_change_sale_price' option is true.
// 2. If the trade is not priced at 0, the wallet holder has to be a signer since auction house cannot sign if listing over 0.
// 3. There must be one and only one signer; there can never be zero signers or two signers.
// 4. Auction house should be the signer for changing the price instead of user wallet for cases when seller lists at 0.
// 3. Auction house should be the signer for changing the price instead of user wallet for cases when seller lists at 0.
if !wallet.to_account_info().is_signer
&& (buyer_price == 0
|| free_seller_trade_state.data_is_empty()
Expand All @@ -407,9 +406,6 @@ fn sell_logic<'c, 'info>(
{
return Err(AuctionHouseError::SaleRequiresSigner.into());
}
if wallet.to_account_info().is_signer && authority.to_account_info().is_signer {
return Err(AuctionHouseError::SaleRequiresExactlyOneSigner.into());
}

let auction_house_key = auction_house.key();

Expand Down
7 changes: 2 additions & 5 deletions auction-house/program/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,8 @@ pub fn close_account<'a>(
.checked_add(current_lamports)
.ok_or(AuctionHouseError::NumericalOverflow)?;

sol_memset(
&mut *source_account.try_borrow_mut_data()?,
0,
account_data_size,
);
#[allow(clippy::explicit_auto_deref)]
sol_memset(*source_account.try_borrow_mut_data()?, 0, account_data_size);

Ok(())
}
95 changes: 95 additions & 0 deletions auction-house/program/tests/sell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use solana_sdk::{signer::Signer, sysvar::clock::Clock};
use std::assert_eq;

use solana_program::borsh::try_from_slice_unchecked;

#[tokio::test]
async fn sell_success() {
let mut context = auction_house_program_test().start_with_context().await;
Expand Down Expand Up @@ -344,3 +345,97 @@ async fn auctioneer_sell_no_delegate_fails() {

assert_error!(error, ACCOUNT_NOT_INITIALIZED);
}

#[tokio::test]
async fn multiple_signers() {
let mut context = auction_house_program_test().start_with_context().await;
// Payer Wallet
let (ah, ahkey, ah_auth) = existing_auction_house_test_context(&mut context)
.await
.unwrap();

let test_metadata = Metadata::new();

let owner_pubkey = &test_metadata.token.pubkey();
airdrop(&mut context, owner_pubkey, TEN_SOL).await.unwrap();

airdrop(&mut context, &ah_auth.pubkey(), TEN_SOL)
.await
.unwrap();

airdrop(&mut context, &test_metadata.token.pubkey(), TEN_SOL)
.await
.unwrap();

airdrop(&mut context, &ah.auction_house_fee_account, TEN_SOL)
.await
.unwrap();

context.warp_to_slot(100).unwrap();

test_metadata
.create(
&mut context,
"Test".to_string(),
"TST".to_string(),
"uri".to_string(),
None,
10,
false,
1,
)
.await
.unwrap();
let ((acc, listing_receipt_acc), sell_tx) = sell_multiple_signers(
&mut context,
&ahkey,
&ah,
&test_metadata,
&test_metadata.token,
ah_auth,
1,
1,
);

context
.banks_client
.process_transaction(sell_tx)
.await
.unwrap();

let sts = context
.banks_client
.get_account(acc.seller_trade_state)
.await
.expect("Error Getting Trade State")
.expect("Trade State Empty");
assert_eq!(sts.data.len(), 1);

let timestamp = context
.banks_client
.get_sysvar::<Clock>()
.await
.unwrap()
.unix_timestamp;

let listing_receipt_account = context
.banks_client
.get_account(listing_receipt_acc.receipt)
.await
.expect("getting listing receipt")
.expect("empty listing receipt data");

let listing_receipt =
ListingReceipt::try_deserialize(&mut listing_receipt_account.data.as_ref()).unwrap();

assert_eq!(listing_receipt.auction_house, acc.auction_house);
assert_eq!(listing_receipt.metadata, acc.metadata);
assert_eq!(listing_receipt.seller, acc.wallet);
assert_eq!(listing_receipt.created_at, timestamp);
assert_eq!(listing_receipt.purchase_receipt, None);
assert_eq!(listing_receipt.canceled_at, None);
assert_eq!(listing_receipt.bookkeeper, *owner_pubkey);
assert_eq!(listing_receipt.seller, *owner_pubkey);
assert_eq!(listing_receipt.price, 1);
assert_eq!(listing_receipt.token_size, 1);
}
109 changes: 108 additions & 1 deletion auction-house/program/tests/utils/setup_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,110 @@ pub fn sell(
)
}

pub fn sell_multiple_signers(
context: &mut ProgramTestContext,
ahkey: &Pubkey,
ah: &AuctionHouse,
test_metadata: &Metadata,
signer: &Keypair,
authority: Keypair,
sale_price: u64,
token_size: u64,
) -> (
(
mpl_auction_house::accounts::Sell,
mpl_auction_house::accounts::PrintListingReceipt,
),
Transaction,
) {
let program_id = mpl_auction_house::id();

let token = get_associated_token_address(&signer.pubkey(), &test_metadata.mint.pubkey());

let (seller_trade_state, sts_bump) = find_trade_state_address(
&signer.pubkey(),
ahkey,
&token,
&ah.treasury_mint,
&test_metadata.mint.pubkey(),
sale_price,
token_size,
);
let (listing_receipt, receipt_bump) = find_listing_receipt_address(&seller_trade_state);

let (free_seller_trade_state, free_sts_bump) = find_trade_state_address(
&signer.pubkey(),
ahkey,
&token,
&ah.treasury_mint,
&test_metadata.mint.pubkey(),
0,
token_size,
);
let (pas, pas_bump) = find_program_as_signer_address();

let accounts = mpl_auction_house::accounts::Sell {
wallet: signer.pubkey(),
token_account: token,
metadata: test_metadata.pubkey,
authority: ah.authority,
auction_house: *ahkey,
auction_house_fee_account: ah.auction_house_fee_account,
seller_trade_state,
free_seller_trade_state,
token_program: spl_token::id(),
system_program: solana_program::system_program::id(),
program_as_signer: pas,
rent: sysvar::rent::id(),
};
let mut account_metas = accounts.to_account_metas(None);

for account in account_metas.iter_mut() {
if account.pubkey == ah.authority {
account.is_signer = true;
}
}

let data = mpl_auction_house::instruction::Sell {
trade_state_bump: sts_bump,
free_trade_state_bump: free_sts_bump,
program_as_signer_bump: pas_bump,
token_size,
buyer_price: sale_price,
}
.data();

let instruction = Instruction {
program_id,
data,
accounts: account_metas,
};

let listing_receipt_accounts = mpl_auction_house::accounts::PrintListingReceipt {
receipt: listing_receipt,
bookkeeper: signer.pubkey(),
system_program: system_program::id(),
rent: sysvar::rent::id(),
instruction: sysvar::instructions::id(),
};

let print_receipt_instruction = Instruction {
program_id,
data: mpl_auction_house::instruction::PrintListingReceipt { receipt_bump }.data(),
accounts: listing_receipt_accounts.to_account_metas(None),
};

(
(accounts, listing_receipt_accounts),
Transaction::new_signed_with_payer(
&[instruction, print_receipt_instruction],
Some(&signer.pubkey()),
&[signer, &authority],
context.last_blockhash,
),
)
}

pub fn sell_pnft(
context: &mut ProgramTestContext,
ahkey: &Pubkey,
Expand Down Expand Up @@ -1479,8 +1583,10 @@ pub async fn existing_auction_house_test_context(
let t_mint_key = spl_token::native_mint::id();
let tdw_ata = twd_key;
let seller_fee_basis_points: u16 = 100;

let authority = Keypair::new();
airdrop(context, &authority.pubkey(), 10_000_000_000).await?;

// Derive Auction House Key
let (auction_house_address, bump) =
find_auction_house_address(&authority.pubkey(), &t_mint_key);
Expand All @@ -1489,6 +1595,7 @@ pub async fn existing_auction_house_test_context(
// Derive Auction House Treasury Key
let (auction_house_treasury_key, treasury_bump) =
find_auction_house_treasury_address(&auction_house_address);

let auction_house = create_auction_house(
context,
&authority,
Expand Down Expand Up @@ -1554,7 +1661,7 @@ pub async fn create_sale_delegate_rule_set(
.rule_set_pda(ruleset_addr)
.payer(creator.pubkey())
.build(CreateOrUpdateArgs::V1 {
serialized_rule_set: serialized_rule_set,
serialized_rule_set,
})
.unwrap()
.instruction();
Expand Down

0 comments on commit 4021e11

Please sign in to comment.