Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass remaining_accounts to auction house properly #1050

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auctioneer/program/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion auctioneer/program/src/execute_sale/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ pub fn auctioneer_execute_sale<'info>(

cpi_account_metas.append(&mut ctx.remaining_accounts.to_vec().to_account_metas(None));

let mut cpi_account_infos: Vec<AccountInfo> = cpi_accounts.to_account_infos();
cpi_account_infos.append(&mut ctx.remaining_accounts.to_vec());

let ix = solana_program::instruction::Instruction {
program_id: cpi_program.key(),
accounts: cpi_account_metas,
Expand All @@ -228,7 +231,7 @@ pub fn auctioneer_execute_sale<'info>(
&[auctioneer_authority_bump],
];

invoke_signed(&ix, &cpi_accounts.to_account_infos(), &[&auctioneer_seeds])?;
invoke_signed(&ix, &cpi_account_infos, &[&auctioneer_seeds])?;

// Close the Listing Config account.
let listing_config = &ctx.accounts.listing_config.to_account_info();
Expand Down
290 changes: 287 additions & 3 deletions auctioneer/program/tests/execute_sale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ use utils::setup_functions::*;

use anchor_lang::{InstructionData, ToAccountMetas};
use mpl_testing_utils::{solana::airdrop, utils::Metadata};
use solana_sdk::{compute_budget::ComputeBudgetInstruction, signer::Signer};
use solana_sdk::{
account::Account as SolanaAccount, compute_budget::ComputeBudgetInstruction, signer::Signer,
};

use std::{assert_eq, time::SystemTime};

use solana_program::{instruction::Instruction, system_program, sysvar};
use solana_program::{
instruction::{AccountMeta, Instruction},
system_program, sysvar,
};

use solana_program::program_pack::Pack;

Expand All @@ -20,7 +25,8 @@ use mpl_auction_house::pda::{
find_trade_state_address,
};
use mpl_auctioneer::pda::find_auctioneer_authority_seeds;
use solana_sdk::{signature::Keypair, transaction::Transaction};
use mpl_token_metadata::state::Creator;
use solana_sdk::{pubkey::Pubkey, signature::Keypair, transaction::Transaction};
use spl_associated_token_account::get_associated_token_address;
use spl_token::state::Account;

Expand Down Expand Up @@ -782,3 +788,281 @@ async fn execute_sale_two_bids_failure() {

assert_error!(result, NOT_HIGH_BIDDER)
}

#[tokio::test]
async fn execute_sale_one_creator() {
execute_sale_with_creators(vec![(Pubkey::new_unique(), 100)]).await;
}

#[tokio::test]
async fn execute_sale_two_creator() {
execute_sale_with_creators(vec![(Pubkey::new_unique(), 25), (Pubkey::new_unique(), 75)]).await;
}

async fn execute_sale_with_creators(metadata_creators: Vec<(Pubkey, u8)>) {
let mut context = auctioneer_program_test().start_with_context().await;
// Payer Wallet
let (ah, ahkey, authority) = existing_auction_house_test_context(&mut context)
.await
.unwrap();
let test_metadata = Metadata::new();
airdrop(&mut context, &test_metadata.token.pubkey(), 10_000_000_000)
.await
.unwrap();

for (creator, _) in &metadata_creators {
// airdrop 0.1 sol to ensure rent-exempt minimum
airdrop(&mut context, &creator, 100_000_000).await.unwrap();
}
test_metadata
.create(
&mut context,
"Test".to_string(),
"TST".to_string(),
"uri".to_string(),
Some(
metadata_creators
.clone()
.iter()
.map(|(address, share)| Creator {
address: *address,
verified: false,
share: *share,
})
.collect(),
),
1000,
false,
1,
)
.await
.unwrap();
let ((sell_acc, listing_config_address), sell_tx) = sell(
&mut context,
&ahkey,
&ah,
&test_metadata,
(SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
- 60) as i64,
(SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
+ 60) as i64,
None,
None,
None,
None,
None,
);
context
.banks_client
.process_transaction(sell_tx)
.await
.unwrap();

let buyer = Keypair::new();
airdrop(&mut context, &buyer.pubkey(), 10_000_000_000)
.await
.unwrap();
let (bid_acc, buy_tx) = buy(
&mut context,
&ahkey,
&ah,
&test_metadata,
&test_metadata.token.pubkey(),
&buyer,
&sell_acc.wallet,
&listing_config_address,
100_000_000,
);
context
.banks_client
.process_transaction(buy_tx)
.await
.unwrap();
let buyer_token_account =
get_associated_token_address(&buyer.pubkey(), &test_metadata.mint.pubkey());

context.warp_to_slot(120 * 400).unwrap();

let (auctioneer_authority, _aa_bump) = find_auctioneer_authority_seeds(&ahkey);
let (auctioneer_pda, _) = find_auctioneer_pda(&ahkey, &auctioneer_authority);
let mut accounts = mpl_auctioneer::accounts::AuctioneerExecuteSale {
auction_house_program: mpl_auction_house::id(),
listing_config: listing_config_address,
buyer: buyer.pubkey(),
seller: test_metadata.token.pubkey(),
authority: ah.authority,
auction_house: ahkey,
metadata: test_metadata.pubkey,
token_account: sell_acc.token_account,
seller_trade_state: sell_acc.seller_trade_state,
buyer_trade_state: bid_acc.buyer_trade_state,
token_program: spl_token::id(),
free_trade_state: sell_acc.free_seller_trade_state,
seller_payment_receipt_account: test_metadata.token.pubkey(),
buyer_receipt_token_account: buyer_token_account,
escrow_payment_account: bid_acc.escrow_payment_account,
token_mint: test_metadata.mint.pubkey(),
auction_house_fee_account: ah.auction_house_fee_account,
auction_house_treasury: ah.auction_house_treasury,
treasury_mint: ah.treasury_mint,
program_as_signer: sell_acc.program_as_signer,
system_program: system_program::id(),
ata_program: spl_associated_token_account::id(),
rent: sysvar::rent::id(),
auctioneer_authority,
ah_auctioneer_pda: auctioneer_pda,
}
.to_account_metas(None);
for (pubkey, _) in &metadata_creators {
accounts.push(AccountMeta {
pubkey: *pubkey,
is_signer: false,
is_writable: true,
});
}

let (_, free_sts_bump) = find_trade_state_address(
&test_metadata.token.pubkey(),
&ahkey,
&sell_acc.token_account,
&ah.treasury_mint,
&test_metadata.mint.pubkey(),
0,
1,
);
let (_, escrow_bump) = find_escrow_payment_address(&ahkey, &buyer.pubkey());
let (_, pas_bump) = find_program_as_signer_address();
let (_, aa_bump) = find_auctioneer_authority_seeds(&ahkey);

let instruction = Instruction {
program_id: mpl_auctioneer::id(),
data: mpl_auctioneer::instruction::ExecuteSale {
escrow_payment_bump: escrow_bump,
free_trade_state_bump: free_sts_bump,
program_as_signer_bump: pas_bump,
auctioneer_authority_bump: aa_bump,
token_size: 1,
buyer_price: 100_000_000,
}
.data(),
accounts,
};
airdrop(&mut context, &ah.auction_house_fee_account, 10_000_000_000)
.await
.unwrap();

let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(350_000);

let tx = Transaction::new_signed_with_payer(
&[compute_ix, instruction],
Some(&authority.pubkey()),
&[&authority],
context.last_blockhash,
);
let seller_before = context
.banks_client
.get_account(test_metadata.token.pubkey())
.await
.unwrap()
.unwrap();
let mut metadata_creators_before: Vec<SolanaAccount> = Vec::new();
for (creator, _) in &metadata_creators {
metadata_creators_before.push(
context
.banks_client
.get_account(*creator)
.await
.unwrap()
.unwrap(),
);
}
let buyer_token_before = &context
.banks_client
.get_account(buyer_token_account)
.await
.unwrap();
assert!(buyer_token_before.is_none());

let listing_config_account = context
.banks_client
.get_account(listing_config_address)
.await
.unwrap()
.unwrap();

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

let seller_after = context
.banks_client
.get_account(test_metadata.token.pubkey())
.await
.unwrap()
.unwrap();
let mut metadata_creators_after: Vec<SolanaAccount> = Vec::new();
for (creator, _) in &metadata_creators {
metadata_creators_after.push(
context
.banks_client
.get_account(*creator)
.await
.unwrap()
.unwrap(),
);
}
let buyer_token_after = Account::unpack_from_slice(
context
.banks_client
.get_account(buyer_token_account)
.await
.unwrap()
.unwrap()
.data
.as_slice(),
)
.unwrap();

let royalty = (test_metadata
.get_data(&mut context)
.await
.data
.seller_fee_basis_points as u64
* 100_000_000)
/ 10000;
let fee_minus: u64 =
100_000_000 - royalty - ((ah.seller_fee_basis_points as u64 * (100_000_000)) / 10000);
assert!(seller_before.lamports < seller_after.lamports);
assert_eq!(buyer_token_after.amount, 1);

let rent = context.banks_client.get_rent().await.unwrap();
let rent_exempt_min: u64 = rent.minimum_balance(listing_config_account.data.len());

for (((_, share), creator_before), creator_after) in metadata_creators
.iter()
.zip(metadata_creators_before.iter())
.zip(metadata_creators_after.iter())
{
assert_eq!(
creator_before.lamports + (royalty * (*share as u64)) / 100,
creator_after.lamports
);
}

assert_eq!(
seller_before.lamports + fee_minus + rent_exempt_min,
seller_after.lamports
);

let listing_config_closed = context
.banks_client
.get_account(listing_config_address)
.await
.unwrap();

assert!(listing_config_closed.is_none());
}