Skip to content
This repository was archived by the owner on Mar 26, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 6 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
5 changes: 0 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,3 @@ jobs:

- name: Lint test
run: yarn run lint:test

- name: Commit potential changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Fix styling
29 changes: 25 additions & 4 deletions packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { AuctionHouse, Bid, Listing, Purchase } from '../models';
import { ExecuteSaleBuilderContext } from './executeSale';
import { TransactionBuilder, TransactionBuilderOptions } from '@/utils';
import {
amount,
lamports,
now,
Operation,
OperationHandler,
Expand Down Expand Up @@ -99,6 +101,12 @@ export type DirectBuyInput = {
*/
price?: SolAmount | SplTokenAmount;

/**
* The amount of tokens to buy.
*
*/
tokens?: SplTokenAmount;

/**
* The Auctioneer authority key.
* It is required when Auction House has Auctioneer enabled.
Expand Down Expand Up @@ -199,15 +207,28 @@ export const directBuyBuilder = async (
auctionHouse,
auctioneerAuthority,
listing,
price = listing.price,
price,
buyer = metaplex.identity(),
authority = auctionHouse.authorityAddress,
bookkeeper = metaplex.identity(),
createBidInstructionKey,
executeSaleInstructionKey,
} = params;

const { tokens, asset, sellerAddress, receiptAddress } = listing;

const { asset, sellerAddress, receiptAddress } = listing;
const tokens = params.tokens ?? listing.tokens;

let finalPrice = price;

if(!finalPrice) {
const listingPricePerToken = listing.price.basisPoints.div(listing.tokens.basisPoints);
const finalPriceBasisPoints = listingPricePerToken.mul(tokens.basisPoints);

finalPrice = auctionHouse.isNative
? lamports(finalPriceBasisPoints)
: amount(finalPriceBasisPoints, auctionHouse.treasuryMint.currency)
}

const printReceipt = (params.printReceipt ?? true) && Boolean(receiptAddress);

Expand All @@ -221,7 +242,7 @@ export const directBuyBuilder = async (
auctioneerAuthority,
authority,
tokens,
price,
price: finalPrice,
mintAccount: asset.mint.address,
seller: sellerAddress,
buyer,
Expand All @@ -243,7 +264,7 @@ export const directBuyBuilder = async (
buyerAddress: buyer.publicKey,
receiptAddress: receipt,
purchaseReceiptAddress: null,
price,
price: finalPrice,
tokens,
canceledAt: null,
createdAt: now(),
Expand Down
102 changes: 98 additions & 4 deletions packages/js/test/plugins/auctionHouseModule/directBuy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import spok, { Specifications } from 'spok';
import { Keypair } from '@solana/web3.js';
import {
createNft,
createSft,
createWallet,
killStuckProcess,
metaplex,
spokSameAmount,
spokSamePubkey,
metaplex, spokSameAmount, spokSamePubkey,
} from '../../helpers';
import { createAuctionHouse } from './helpers';
import { sol, token } from '@/types';
import { Purchase } from '@/index';
import { Purchase } from '@/plugins';

killStuckProcess();

Expand Down Expand Up @@ -64,6 +63,101 @@ test('[auctionHouseModule] buy on an Auction House with minimum input', async (t
} as unknown as Specifications<Purchase>);
});

test('[auctionHouseModule] partial buy on an Auction House', async (t: Test) => {
// Given we have an Auction House and an NFT.
const mx = await metaplex();
const seller = await createWallet(mx);
const auctionHouse = await createAuctionHouse(mx);
const sft = await createSft(mx, {
tokenOwner: seller.publicKey,
tokenAmount: token(10)
});

await mx.tokens().mint({
mintAddress: sft.address,
amount: token(10),
toOwner: seller.publicKey
});

// And we listed that 5 SFTs for 1 SOL each.
const { listing } = await mx.auctionHouse().list({
auctionHouse,
seller,
mintAccount: sft.address,
price: sol(5),
tokens: token(5),
});

// When we execute direct buy with the given listing.
const { purchase } = await mx.auctionHouse().buy({
auctionHouse,
listing,
tokens: token(3)
});

// Then the user must receive 3 Tokens.
const buyerTokens = await mx
.nfts()
.findByToken({ token: purchase.asset.token.address });

t.equal(buyerTokens.token.amount.basisPoints.toNumber(), 3);

// And then the seller must have 2 Tokens on sale left.
const sellerTokens = await mx
.nfts()
.findByToken({ token: listing.asset.token.address });

t.equal(sellerTokens.token.delegateAmount.basisPoints.toNumber(), 2);
});

test('[auctionHouseModule] partial buy on an Auction House with exact price', async (t: Test) => {
// Given we have an Auction House and an NFT.
const mx = await metaplex();
const seller = await createWallet(mx);
const auctionHouse = await createAuctionHouse(mx);
const sft = await createSft(mx, {
tokenOwner: seller.publicKey,
tokenAmount: token(10)
});

await mx.tokens().mint({
mintAddress: sft.address,
amount: token(10),
toOwner: seller.publicKey
});

// And we listed that 5 SFTs for 1 SOL each.
const { listing } = await mx.auctionHouse().list({
auctionHouse,
seller,
mintAccount: sft.address,
price: sol(5),
tokens: token(5),
});

// When we execute direct buy with the given listing with 3 SOL.
const { purchase } = await mx.auctionHouse().buy({
auctionHouse,
listing,
tokens: token(3),
price: sol(3)
});

// Then the user must receive 3 Tokens.
const buyerTokens = await mx
.nfts()
.findByToken({ token: purchase.asset.token.address });

t.equal(buyerTokens.token.amount.basisPoints.toNumber(), 3);

// And then the seller must have 2 Tokens on sale left.
const sellerTokens = await mx
.nfts()
.findByToken({ token: listing.asset.token.address });

t.equal(sellerTokens.token.delegateAmount.basisPoints.toNumber(), 2);
});

test('[auctionHouseModule] buy on an Auction House with auctioneer with auctioneer', async (t: Test) => {
// Given we have an Auction House and an NFT.
const mx = await metaplex();
Expand Down