From c842fa3f24bc61432e8ce4ac13fc2f294e57c2fb Mon Sep 17 00:00:00 2001 From: antey13 Date: Fri, 28 Oct 2022 12:16:25 +0200 Subject: [PATCH 1/6] Add partial directBuy --- .../operations/directBuy.ts | 19 ++++--- .../auctionHouseModule/directBuy.test.ts | 54 ++++++++++++++++--- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts index 27875162d..c1bda7ef4 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts @@ -1,4 +1,4 @@ -import { PublicKey } from '@solana/web3.js'; +import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; import { SendAndConfirmTransactionResponse } from '../../rpcModule'; import { AuctioneerAuthorityRequiredError } from '../errors'; import { AuctionHouse, Bid, Listing, Purchase } from '../models'; @@ -9,7 +9,7 @@ import { Operation, OperationHandler, OperationScope, - Signer, + Signer, sol, SolAmount, SplTokenAmount, toPublicKey, @@ -99,6 +99,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. @@ -199,7 +205,7 @@ export const directBuyBuilder = async ( auctionHouse, auctioneerAuthority, listing, - price = listing.price, + tokens: tokensToBuy, buyer = metaplex.identity(), authority = auctionHouse.authorityAddress, bookkeeper = metaplex.identity(), @@ -207,8 +213,9 @@ export const directBuyBuilder = async ( executeSaleInstructionKey, } = params; - const { tokens, asset, sellerAddress, receiptAddress } = listing; - + const { tokens: listingTokens, asset, sellerAddress, receiptAddress } = listing; + const tokens = tokensToBuy ? tokensToBuy : listingTokens; + const price = !tokens ? listing.price : sol(listing.price.basisPoints.mul(tokens.basisPoints).div(listing.tokens.basisPoints).toNumber()/LAMPORTS_PER_SOL); const printReceipt = (params.printReceipt ?? true) && Boolean(receiptAddress); if (auctionHouse.hasAuctioneer && !auctioneerAuthority) { @@ -222,7 +229,7 @@ export const directBuyBuilder = async ( authority, tokens, price, - mintAccount: asset.mint.address, + mintAccount: listing.asset.mint.address, seller: sellerAddress, buyer, printReceipt, diff --git a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts index bcd104093..8f5209202 100644 --- a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts @@ -1,17 +1,12 @@ import test, { Test } from 'tape'; -import spok, { Specifications } from 'spok'; -import { Keypair } from '@solana/web3.js'; import { - createNft, + createSft, createWallet, killStuckProcess, metaplex, - spokSameAmount, - spokSamePubkey, } from '../../helpers'; import { createAuctionHouse } from './helpers'; import { sol, token } from '@/types'; -import { Purchase } from '@/index'; killStuckProcess(); @@ -64,6 +59,53 @@ test('[auctionHouseModule] buy on an Auction House with minimum input', async (t } as unknown as Specifications); }); +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] 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(); From a53b839f9d96b21c03decf7795fb16326c3c4311 Mon Sep 17 00:00:00 2001 From: antey13 Date: Fri, 28 Oct 2022 12:26:11 +0200 Subject: [PATCH 2/6] Fix imports --- .../js/test/plugins/auctionHouseModule/directBuy.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts index 8f5209202..282219873 100644 --- a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts @@ -1,12 +1,16 @@ import test, { Test } from 'tape'; import { + createNft, createSft, createWallet, killStuckProcess, - metaplex, + metaplex, spokSameAmount, spokSamePubkey, } from '../../helpers'; import { createAuctionHouse } from './helpers'; import { sol, token } from '@/types'; +import spok, { Specifications } from 'spok'; +import { Purchase } from '@/plugins'; +import { Keypair } from '@solana/web3.js'; killStuckProcess(); From e3c421df4068fbcd534a9f10bcf0cf9961daad64 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 28 Oct 2022 15:27:55 +0100 Subject: [PATCH 3/6] Update lint.yml --- .github/workflows/lint.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9821cd510..b9cde6a3b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -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 From 27f0b452474b5c687e8fa1b07d9056b138095a2b Mon Sep 17 00:00:00 2001 From: Michael Sebastiyan Date: Fri, 28 Oct 2022 20:25:51 +0300 Subject: [PATCH 4/6] Fix direct buy --- .../operations/directBuy.ts | 30 ++++++++++++++----- .../auctionHouseModule/directBuy.test.ts | 4 +-- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts index c1bda7ef4..973c99c96 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts @@ -1,15 +1,17 @@ -import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import { SendAndConfirmTransactionResponse } from '../../rpcModule'; import { AuctioneerAuthorityRequiredError } from '../errors'; import { AuctionHouse, Bid, Listing, Purchase } from '../models'; import { ExecuteSaleBuilderContext } from './executeSale'; import { TransactionBuilder, TransactionBuilderOptions } from '@/utils'; import { + amount, + lamports, now, Operation, OperationHandler, OperationScope, - Signer, sol, + Signer, SolAmount, SplTokenAmount, toPublicKey, @@ -205,7 +207,7 @@ export const directBuyBuilder = async ( auctionHouse, auctioneerAuthority, listing, - tokens: tokensToBuy, + price, buyer = metaplex.identity(), authority = auctionHouse.authorityAddress, bookkeeper = metaplex.identity(), @@ -213,9 +215,21 @@ export const directBuyBuilder = async ( executeSaleInstructionKey, } = params; - const { tokens: listingTokens, asset, sellerAddress, receiptAddress } = listing; - const tokens = tokensToBuy ? tokensToBuy : listingTokens; - const price = !tokens ? listing.price : sol(listing.price.basisPoints.mul(tokens.basisPoints).div(listing.tokens.basisPoints).toNumber()/LAMPORTS_PER_SOL); + + 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); if (auctionHouse.hasAuctioneer && !auctioneerAuthority) { @@ -228,7 +242,7 @@ export const directBuyBuilder = async ( auctioneerAuthority, authority, tokens, - price, + price: finalPrice, mintAccount: listing.asset.mint.address, seller: sellerAddress, buyer, @@ -250,7 +264,7 @@ export const directBuyBuilder = async ( buyerAddress: buyer.publicKey, receiptAddress: receipt, purchaseReceiptAddress: null, - price, + price: finalPrice, tokens, canceledAt: null, createdAt: now(), diff --git a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts index 282219873..99eb68867 100644 --- a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts @@ -1,4 +1,6 @@ import test, { Test } from 'tape'; +import spok, { Specifications } from 'spok'; +import { Keypair } from '@solana/web3.js'; import { createNft, createSft, @@ -8,9 +10,7 @@ import { } from '../../helpers'; import { createAuctionHouse } from './helpers'; import { sol, token } from '@/types'; -import spok, { Specifications } from 'spok'; import { Purchase } from '@/plugins'; -import { Keypair } from '@solana/web3.js'; killStuckProcess(); From cd51db569b6f0cd92473da5030cae1fe83b9e992 Mon Sep 17 00:00:00 2001 From: Michael Sebastiyan Date: Fri, 28 Oct 2022 20:29:25 +0300 Subject: [PATCH 5/6] Add test --- .../operations/directBuy.ts | 2 +- .../auctionHouseModule/directBuy.test.ts | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts index 973c99c96..bf0220f17 100644 --- a/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts +++ b/packages/js/src/plugins/auctionHouseModule/operations/directBuy.ts @@ -243,7 +243,7 @@ export const directBuyBuilder = async ( authority, tokens, price: finalPrice, - mintAccount: listing.asset.mint.address, + mintAccount: asset.mint.address, seller: sellerAddress, buyer, printReceipt, diff --git a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts index 99eb68867..59b69bf92 100644 --- a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts @@ -110,6 +110,54 @@ test('[auctionHouseModule] partial buy on an Auction House', async (t: Test) => 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(); From 259c4f87da307fe86ea4b9edf618d96c8f1cc40d Mon Sep 17 00:00:00 2001 From: antey13 Date: Mon, 31 Oct 2022 13:18:17 +0100 Subject: [PATCH 6/6] Fix comments, remove unnecessary mint --- .../plugins/auctionHouseModule/directBuy.test.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts index 59b69bf92..79653371e 100644 --- a/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts +++ b/packages/js/test/plugins/auctionHouseModule/directBuy.test.ts @@ -64,7 +64,7 @@ test('[auctionHouseModule] buy on an Auction House with minimum input', async (t }); test('[auctionHouseModule] partial buy on an Auction House', async (t: Test) => { - // Given we have an Auction House and an NFT. + // Given we have an Auction House and an SFT. const mx = await metaplex(); const seller = await createWallet(mx); const auctionHouse = await createAuctionHouse(mx); @@ -73,12 +73,6 @@ test('[auctionHouseModule] partial buy on an Auction House', async (t: Test) => 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, @@ -111,7 +105,7 @@ test('[auctionHouseModule] partial buy on an Auction House', async (t: Test) => }); test('[auctionHouseModule] partial buy on an Auction House with exact price', async (t: Test) => { - // Given we have an Auction House and an NFT. + // Given we have an Auction House and an SFT. const mx = await metaplex(); const seller = await createWallet(mx); const auctionHouse = await createAuctionHouse(mx); @@ -120,12 +114,6 @@ test('[auctionHouseModule] partial buy on an Auction House with exact price', as 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,