Marketplace (Secondary Sales)

NFT Marketplace

The Lootex SDK provides a comprehensive set of functions for interacting with NFT marketplaces. These functions allow you to list NFTs for sale, make offers, buy NFTs, and manage orders.

Core Functions

List NFTs for Sale

The list function allows you to create listing orders for your NFTs:

import { list, encodeBulkOrderSignature } from 'lootex/order';
 
const params = {
  client: lootexClient,
  chainId: 1, // Ethereum mainnet
  accountAddress: '0x...', // Your wallet address
  data: [
    {
      tokenAddress: '0x...', // NFT contract address
      tokenId: '1', // Token ID
      tokenType: 'ERC721', // or 'ERC1155'
      quantity: '1',
      pricePerToken: '1_000_000_000_000_000_000_000', // 1 ETH
      currency: '0x...', // leave it empty for using native currency1
      startTime: Math.floor(Date.now() / 1000),
      endTime: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days
    }
  ],
  enableBulkOrder: false // Set to true for multiple listings in one transaction
};
 
const { steps } = await list(params);
 
// Execute the steps
for (const step of steps) {
  if (step.id === 'approve-tokens') {
    console.log('Approving tokens...');
    for (const item of step.items) {
      if (item.type === 'transaction') {
        const hash = await walletClient.sendTransaction(item.data);
        console.log(`Approval transaction sent: ${hash}`);
      }
    }
  } else if (step.id === 'create-orders') {
    console.log('Creating order...');
    // Sign the orders
    const signTypedDataItems = step.items.filter(
      (item) => item.type === 'signTypedData'
    );
    const signatures = await Promise.all(
      signTypedDataItems.map((item) =>
        walletClient.signTypedData(item.data)
      )
    );
 
    // Submit the orders
    const postItem = step.items.find((item) => item.type === 'post');
    if (postItem) {
      const body = postItem.body.map((order, index) => ({
          ...order,
          signature: step.needEncodeProofAndSignature
            ? encodeBulkOrderSignature(index, order.proof, signatures[index])
            : signatures[index],
        }))
        
 
      const result = await apiClient.request({
        method: 'POST',
        path: postItem.endpoint,
        body,
      });
    }
  }
}

Buy NFTs

The buy function allows you to purchase listed NFTs:

import { buy } from 'lootex/order';
 
const params = {
  client: lootexClient,
  chainId: 1,
  accountAddress: '0x...', // Buyer's wallet address
  orders: [order], // LootexOrder object(s)
};
 
const { steps } = await buy(params);
 
// Execute the steps
for (const step of steps) {
  if (step.id === 'approve-tokens') {
    console.log('Approving tokens...');
    for (const item of step.items) {
      if (item.type === 'transaction') {
        const hash = await walletClient.sendTransaction({
          ...item.data,
          value: item.data.value ? BigInt(item.data.value) : undefined,
        });
        console.log(`Approval transaction sent: ${hash}`);
      }
    }
  } else if (step.id === 'approve-aggregator') {
    console.log('Approving aggregator...');
    for (const item of step.items) {
      if (item.type === 'transaction') {
        const hash = await walletClient.sendTransaction({
          ...item.data,
          value: item.data.value ? BigInt(item.data.value) : undefined,
        });
        console.log(`Aggregator approval transaction sent: ${hash}`);
      }
    }
  } else if (step.id === 'exchange') {
    console.log('Executing exchange...');
    for (const item of step.items) {
      if (item.type === 'transaction') {
        const hash = await walletClient.sendTransaction({
          ...item.data,
          value: item.data.value ? BigInt(item.data.value) : undefined,
        });
        console.log(`Exchange transaction sent: ${hash}`);
      }
    }
  }
}

Make Offers

The makeOffer function allows you to make offers on NFTs:

import { makeOffer } from 'lootex/order';
 
const params = {
  client: lootexClient,
  chainId: 1,
  accountAddress: '0x...', // Your wallet address
  data: {
    tokenAddress: '0x...', // NFT contract address
    tokenId: '1', // Token ID (optional for collection offers)
    tokenType: 'ERC721',
    quantity: '1',
    price: '1.0', // Offer price
    currency: '0x...', // Currency contract address
    startTime: Math.floor(Date.now() / 1000),
    endTime: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 7 days
  }
};
 
const { steps } = await makeOffer(params);
 
// Execute the steps (similar to list function)
// 1. Handle approve-tokens step if needed
// 2. Handle create-orders step for signing and submitting the offer

Accept Offers

The acceptOffer function allows NFT owners to accept offers:

import { acceptOffer } from 'lootex/order';
 
const params = {
  client: lootexClient,
  chainId: 1,
  accountAddress: '0x...', // NFT owner's wallet address
  order: offerOrder, // LootexOrder object
};
 
const { steps } = await acceptOffer(params);
 
// Execute the steps (similar to buy function)
// 1. Handle approve-tokens step if needed
// 2. Handle approve-aggregator step if needed
// 3. Handle exchange step for completing the transaction

Understanding Steps

All marketplace functions return a steps array that needs to be executed in sequence. Each step can contain different types of items:

Common Step Types

  1. approve-tokens

    • Handles token approvals for ERC20, ERC721, or ERC1155 tokens
    • Contains transaction items that need to be sent to the blockchain
  2. approve-aggregator

    • Handles approvals for the Lootex aggregator contract
    • Required for some operations involving multiple tokens or orders
  3. create-orders

    • Used when creating new listings or offers
    • Contains items for signing orders (signTypedData)
    • Contains a post item for submitting the signed orders to Lootex API
  4. exchange

    • Handles the actual exchange of tokens
    • Contains transaction items for executing the trade

Step Items

Each step can contain different types of items:

  1. transaction

    {
      type: 'transaction';
      data: {
        to: `0x${string}`;
        data: `0x${string}`;
        value?: string;
      };
    }
  2. signTypedData

    {
      type: 'signTypedData';
      data: {
        domain: object;
        types: object;
        message: object;
        primaryType: string;
      };
    }
  3. post

    {
      type: 'post';
      endpoint: string;
      body: object | object[];
    }

Order Types

The SDK uses the LootexOrder type to represent orders in the marketplace. Here’s the structure of a LootexOrder:

type LootexOrder = {
  id: string;
  hash: `0x${string}`;
  chainId: number;
  exchangeAddress: `0x${string}`;
  category: 'listing' | 'offer';
  offerer: `0x${string}`;
  price: number;
  perPrice: number;
  priceSymbol: string;
  startTime: number;
  endTime: number;
  // ... other properties
};

Helper Functions

The SDK provides several helper functions for working with orders:

  • getOrderAvailableQuantity(order): Get the available quantity for an order
  • getOrderQuantity(order): Get the total quantity of an order
  • getMaxAcceptOfferQuantity(order, availableAmount): Calculate the maximum quantity that can be accepted for an offer

Error Handling

All marketplace functions will throw errors if:

  • Required parameters are missing
  • The chain ID is not supported
  • The user has insufficient balance
  • The user has not approved the required tokens
  • The order is invalid or expired

Make sure to implement proper error handling in your application:

try {
  const result = await buy(params);
} catch (error) {
  console.error('Failed to buy NFT:', error);
}

Best Practices

  1. Always check order validity before executing transactions
  2. Implement proper approval flows for tokens
  3. Handle both ERC721 and ERC1155 token types appropriately
  4. Set reasonable expiration times for listings and offers
  5. Implement proper error handling and user feedback
  6. Execute all steps in the correct order
  7. Verify transaction success after each step
  8. Keep track of transaction hashes for reference

Advanced Features

Bulk Operations

The SDK supports bulk operations for listings and purchases:

// Bulk listing
const bulkListResult = await list({
  ...params,
  enableBulkOrder: true,
  data: [/* multiple NFT listing data */]
});
 
// Bulk purchase
const bulkBuyResult = await buy({
  ...params,
  orders: [/* multiple LootexOrder objects */]
});

Collection Offers

You can make offers on entire collections by omitting the tokenId when creating an offer:

const collectionOfferResult = await makeOffer({
  ...params,
  data: {
    tokenAddress: '0x...', // Collection address
    tokenType: 'ERC721',
    // tokenId is omitted for collection offers
    // ... other parameters
  }
});