UNPKG

opensea-js

Version:

TypeScript SDK for the OpenSea marketplace helps developers build new experiences using NFTs and our marketplace data

175 lines (162 loc) 5.84 kB
import { ItemType } from "@opensea/seaport-js/lib/constants"; import { ConsiderationInputItem, CreateInputItem, MatchOrdersFulfillment, Order, OrderWithCounter, } from "@opensea/seaport-js/lib/types"; import { isCurrencyItem } from "@opensea/seaport-js/lib/utils/item"; import { generateRandomSalt } from "@opensea/seaport-js/lib/utils/order"; import { ZeroAddress } from "ethers"; /** * Compute the native currency (ETH) value required to fulfill a private listing. * Sums all native currency consideration items not going to the taker. * @param order The private listing order * @param takerAddress The address of the private listing recipient * @returns The total native currency value as a bigint */ export const computePrivateListingValue = ( order: OrderWithCounter, takerAddress: string, ): bigint => { return order.parameters.consideration .filter( (item) => item.recipient.toLowerCase() !== takerAddress.toLowerCase() && item.token.toLowerCase() === ZeroAddress.toLowerCase() && item.itemType === ItemType.NATIVE, ) .reduce((sum, item) => sum + BigInt(item.startAmount), 0n); }; export const getPrivateListingConsiderations = ( offer: CreateInputItem[], privateSaleRecipient: string, ): ConsiderationInputItem[] => { return offer.map((item) => { return { ...item, recipient: privateSaleRecipient }; }); }; export const constructPrivateListingCounterOrder = ( order: OrderWithCounter, privateSaleRecipient: string, ): Order => { // Counter order offers up all the items in the private listing consideration // besides the items that are going to the private listing recipient const paymentItems = order.parameters.consideration.filter( (item) => item.recipient.toLowerCase() !== privateSaleRecipient.toLowerCase(), ); // Only validate payment items if there are any (zero-payment private listings are valid) if (paymentItems.length > 0) { if (!paymentItems.every((item) => isCurrencyItem(item))) { throw new Error( "The consideration for the private listing did not contain only currency items", ); } if ( !paymentItems.every((item) => item.itemType === paymentItems[0].itemType) ) { throw new Error("Not all currency items were the same for private order"); } } const { aggregatedStartAmount, aggregatedEndAmount } = paymentItems.reduce( ({ aggregatedStartAmount, aggregatedEndAmount }, item) => ({ aggregatedStartAmount: aggregatedStartAmount + BigInt(item.startAmount), aggregatedEndAmount: aggregatedEndAmount + BigInt(item.endAmount), }), { aggregatedStartAmount: 0n, aggregatedEndAmount: 0n, }, ); const counterOrder: Order = { parameters: { ...order.parameters, offerer: privateSaleRecipient, // Empty offer for zero-payment private listings, single aggregated item otherwise offer: paymentItems.length > 0 ? [ { itemType: paymentItems[0].itemType, token: paymentItems[0].token, identifierOrCriteria: paymentItems[0].identifierOrCriteria, startAmount: aggregatedStartAmount.toString(), endAmount: aggregatedEndAmount.toString(), }, ] : [], // The consideration here is empty as the original private listing order supplies // the taker address to receive the desired items. consideration: [], salt: generateRandomSalt(), totalOriginalConsiderationItems: 0, }, signature: "0x", }; return counterOrder; }; export const getPrivateListingFulfillments = ( privateListingOrder: OrderWithCounter, ): MatchOrdersFulfillment[] => { const nftRelatedFulfillments: MatchOrdersFulfillment[] = []; // For the original order, we need to match everything offered with every consideration item // on the original order that's set to go to the private listing recipient privateListingOrder.parameters.offer.forEach((offerItem, offerIndex) => { const considerationIndex = privateListingOrder.parameters.consideration.findIndex( (considerationItem) => considerationItem.itemType === offerItem.itemType && considerationItem.token === offerItem.token && considerationItem.identifierOrCriteria === offerItem.identifierOrCriteria, ); if (considerationIndex === -1) { throw new Error( "Could not find matching offer item in the consideration for private listing", ); } nftRelatedFulfillments.push({ offerComponents: [ { orderIndex: 0, itemIndex: offerIndex, }, ], considerationComponents: [ { orderIndex: 0, itemIndex: considerationIndex, }, ], }); }); const currencyRelatedFulfillments: MatchOrdersFulfillment[] = []; // For the original order, we need to match everything offered with every consideration item // on the original order that's set to go to the private listing recipient privateListingOrder.parameters.consideration.forEach( (considerationItem, considerationIndex) => { if (!isCurrencyItem(considerationItem)) { return; } // We always match the offer item (index 0) of the counter order (index 1) // with all of the payment items on the private listing currencyRelatedFulfillments.push({ offerComponents: [ { orderIndex: 1, itemIndex: 0, }, ], considerationComponents: [ { orderIndex: 0, itemIndex: considerationIndex, }, ], }); }, ); return [...nftRelatedFulfillments, ...currencyRelatedFulfillments]; };