Create a listing
Creates one or more above-floor listings owned by the caller. Liquid and
dutch share the same struct; the discriminator is the duration field
checked against the active ListingConfig for the collection.
Prerequisites
- Collection created and initialized.
- Caller owns each listed NFT and has approved the Locker
(
setApprovalForAll(locker, true)). - Caller holds enough
nativeTokento pay tax up-front, and has approvedListingsfortaxRequired_. UseListings.getListingTaxRequired(listing, collection)to size the approval. - Listings is not paused (
Listings.paused()mirrorsLocker.paused()). - For each listing,
floorMultipleanddurationmust satisfy the collection’sListingConfig:floorMultiple > 100(above floor).floorMultiple <= maxFloorMultiple.durationfalls in either the dutch range[minDutchDuration, maxDutchDuration]or the liquid range[minLiquidDuration, maxLiquidDuration].
Format is inferred from the duration window — Listings.getListingType
returns the active enum. If a duration sits outside both ranges the call
reverts with InvalidListingType.
Inputs
struct Listing {
address payable owner;
uint40 created;
uint32 duration;
uint16 floorMultiple; // 100 = floor, 200 = 2x floor (2dp)
}
struct CreateListing {
address collection;
uint[] tokenIds;
Listing listing; // applied to every tokenId in the array
}
function createListings(CreateListing[] calldata _createListings) external;| Field | Type | Notes |
|---|---|---|
collection | address | ERC-721 |
tokenIds | uint[] | All take the same Listing parameters |
listing.owner | address payable | Receives sale proceeds; usually msg.sender |
listing.created | uint40 | Set by the contract — pass 0 |
listing.duration | uint32 | Seconds; format derives from this |
listing.floorMultiple | uint16 | 2dp price (e.g. 150 = 1.5x floor) |
Contract calls
| # | Caller | Call | Purpose |
|---|---|---|---|
| 1 | Owner | <ERC721>.setApprovalForAll(locker, true) | One-shot |
| 2 | Owner | <nativeToken>.approve(listings, taxRequired) | Per call (or use Permit2) |
| 3 | Owner | Listings.createListings([{collection, tokenIds, listing}]) | Pulls tax + NFTs in one tx |
Events: Listings.ListingsCreated(collection, tokenIds, listing, listingType, tokensRequired, taxRequired, sender).
Code
createListings.ts
import { listingsAbi } from "@nftx/contracts";
import { NFTX_CONTRACTS } from "@nftx/config";
const listings = NFTX_CONTRACTS[base.id].listings;
const collection = "0xCollection";
const tokenIds = [11n, 12n];
const listing = {
owner: account,
created: 0,
duration: 60 * 60 * 24 * 14, // 14 days = liquid
floorMultiple: 150, // 1.50x floor
} as const;
const taxRequired = await publicClient.readContract({
address: listings,
abi: listingsAbi,
functionName: "getListingTaxRequired",
args: [listing, collection],
});
await walletClient.writeContract({
address: nativeToken,
abi: erc20Abi,
functionName: "approve",
args: [listings, taxRequired],
});
await walletClient.writeContract({
address: listings,
abi: listingsAbi,
functionName: "createListings",
args: [[{ collection, tokenIds, listing }]],
});Failure modes
| Error | Cause |
|---|---|
CollectionNotInitialized | Collection’s pool isn’t bootstrapped yet |
FloorMultipleMustBeAbove100(_floorMultiple) | Tried to list at or below floor |
FloorMultipleExceedsMax(...) | Above the per-collection cap |
ListingDurationBelowMin(...) / ListingDurationExceedsMax(...) | Outside the active config window |
InvalidListingType | Duration falls between dutch and liquid bands |
InsufficientTax(taxPaid, taxRequired) | Approval too small / tax balance too low |
LockerIsNotTokenHolder | The Locker doesn’t think this NFT was approved/transferred |
Paused() | Listings paused |
Subgraph follow-through
For each new listing:
- A
Listingrow is created/updated in place (format = LIQUIDorDUTCH,taxCaptured = 0). - A
ListingActivity { activityType: CREATED }row is written. Collection.publicListingsincrements by 1.
query MyNewListings(
$chainId: Int!
$collection: String!
$owner: String!
) {
Listing(
where: {
chainId: { _eq: $chainId }
collection: { collection: { _eq: $collection } }
owner: { _eq: $owner }
format: { _in: [LIQUID, DUTCH] }
}
order_by: { createdAt: desc }
limit: 50
) {
tokenId
floorMultiple
duration
format
createdAt
}
}