Contents

How to Build a NFT Market with Locksrare Protocol

Terminology

  • Maker: a passive order that exists off-chain
  • Taker: an order that is executed on-chain
  • Ask: the ask user sells NFT assets for fungible tokens
  • Bid: the bid user buys NFT assets by spending fungible tokens

Create Maker

Create MakerAsk

  • get global nonce from LooksRareProtocol

  • check that collection has approved the TransferManager

    • allow TransferManager to transfer NFT assets
  • check that TransferManager has approved LooksRareProtocol

  • import { utils, providers, Wallet } from 'ethers';
    import { LooksRare, CollectionType, StrategyType } from '@looksrare/sdk-v2';
    
    async function main() {
      const chainId: number = 2021;
      const provider = new providers.JsonRpcProvider(
        'https://ronin-saigon.core.chainstack.com/?', // 替换为你的实际 RPC 节点
        {
          name: 'saigon',
          chainId,
        }
      );
    
      const protocolAddress = '0xc8641627a4f01c1b9f4c6ecf5baa1da1a3dfb76a';
      const transferManagerAddress = '0x832690ec980e03890bfc5ad9ce31b2dbb77ea6d9';
      const wethAddress = '0x29C6F8349A028E1bdfC68BFa08BDee7bC5D47E16';
      const privateKey = '?'; // 替换为你的实际私钥
      const signer = new Wallet(privateKey, provider);
      const lr = new LooksRare(chainId, provider, signer, {
        LOOKS: '',
        EXCHANGE_V2: protocolAddress,
        TRANSFER_MANAGER_V2: transferManagerAddress,
        WETH: wethAddress,
        ORDER_VALIDATOR_V2: '',
        REVERSE_RECORDS: '',
        LOOKS_LP_V3: '',
        STAKING_POOL_FOR_LOOKS_LP: '',
        AGGREGATOR_UNISWAP_V3: '',
      });
    
      const { maker, isCollectionApproved, isTransferManagerApproved } = await lr.createMakerAsk({
        collection: '0xe9e87785559037ffd8d92c24eb8caaba8ffda5d3', // Collection address
        collectionType: CollectionType.ERC721,
        strategyId: StrategyType.standard,
        subsetNonce: 0, // keep 0 if you don't know what it is used for
        orderNonce: 0, // You need to retrieve this value from the API
        endTime: Math.floor(Date.now() / 1000) + 86400, // If you use a timestamp in ms, the function will revert
        price: utils.parseEther('0.5'), // Be careful to use a price in wei, this example is for 1 ETH
        itemIds: [200000138], // Token id of the NFT(s) you want to sell, add several ids to create a bundle
        // amounts: [1], // Use it for listing multiple ERC-1155 (Optional, Default to [1])
        startTime: Math.floor(Date.now() / 1000), // Use it to create an order that will be valid in the future (Optional, Default to now)
      });
    
      const { maxFeePerGas, maxPriorityFeePerGas } = await provider.getFeeData();
    
      // Grant the TransferManager the right the transfer assets on behalf od the LooksRareProtocol
      if (!isTransferManagerApproved) {
        const tx = await lr.grantTransferManagerApproval().call({
          maxFeePerGas: maxFeePerGas!.div(3).mul(40),
          maxPriorityFeePerGas: maxPriorityFeePerGas?.div(3).mul(40),
        });
        const res = await tx.wait();
      }
    
      // Approve the collection items to be transferred by the TransferManager
      if (!isCollectionApproved) {
        const tx = await lr.approveAllCollectionItems(maker.collection, true, {
          maxFeePerGas: maxFeePerGas!.div(3).mul(40),
          maxPriorityFeePerGas: maxPriorityFeePerGas?.div(3).mul(40),
        });
        const res = await tx.wait();
      }
    
      // Sign your maker order
      const signature = await lr.signMakerOrder(maker);
      console.log('signature', signature);
      console.log('maker', JSON.stringify(maker));
    }
    
    main();
    

Create MakerBid

  • get global nonce from LooksRareProtocol

  • check that currency has approved the LooksRareProtocol

    • allow LooksRareProtocol to transfer fungible tokens, allowance must be greater than the bid price
  • import { utils, providers, Wallet } from 'ethers';
    import { LooksRare, CollectionType, StrategyType } from '@looksrare/sdk-v2';
    
    async function main() {
      const chainId: number = 2021;
      const provider = new providers.JsonRpcProvider(
        'https://ronin-saigon.core.chainstack.com/?', // 替换为你的实际 RPC 节点
        {
          name: 'saigon',
          chainId,
        }
      );
    
      const protocolAddress = '0x84c3e3e964d88c986de92493922647136430937b';
      const transferManagerAddress = '0x832690ec980e03890bfc5ad9ce31b2dbb77ea6d9';
      const wethAddress = '0x29C6F8349A028E1bdfC68BFa08BDee7bC5D47E16';
      const privateKey = '?'; // 替换为你的实际私钥
      const signer = new Wallet(privateKey, provider);
      const lr = new LooksRare(chainId, provider, signer, {
        LOOKS: '',
        EXCHANGE_V2: protocolAddress,
        TRANSFER_MANAGER_V2: transferManagerAddress,
        WETH: wethAddress,
        ORDER_VALIDATOR_V2: '',
        REVERSE_RECORDS: '',
        LOOKS_LP_V3: '',
        STAKING_POOL_FOR_LOOKS_LP: '',
        AGGREGATOR_UNISWAP_V3: '',
      });
    
      const { maker, isCurrencyApproved, isBalanceSufficient } = await lr.createMakerBid({
        collection: '0xe9e87785559037ffd8d92c24eb8caaba8ffda5d3', // Collection address
        collectionType: CollectionType.ERC721,
        strategyId: StrategyType.standard,
        subsetNonce: 0, // keep 0 if you don't know what it is used for
        orderNonce: 0, // You need to retrieve this value from the API
        endTime: Math.floor(Date.now() / 1000) + 86400, // If you use a timestamp in ms, the function will revert
        price: utils.parseEther('0.05'), // Be careful to use a price in wei, this example is for 1 ETH
        itemIds: [200000138], // Token id of the NFT(s) you want to sell, add several ids to create a bundle
        // amounts: [1], // Use it for listing multiple ERC-1155 (Optional, Default to [1])
        startTime: Math.floor(Date.now() / 1000), // Use it to create an order that will be valid in the future (Optional, Default to now)
      });
    
      const { maxFeePerGas, maxPriorityFeePerGas } = await provider.getFeeData();
    
      // Approve spending of the currency used for bidding
      if (!isCurrencyApproved) {
        const tx = await lr.approveErc20(wethAddress, utils.parseEther('0.05'), {
          maxFeePerGas: maxFeePerGas!.div(3).mul(40),
          maxPriorityFeePerGas: maxPriorityFeePerGas?.div(3).mul(40),
        });
        await tx.wait();
      }
    
      // Checks if the WETH balance is enough to cover the bid
      if (!isBalanceSufficient) {
        throw new Error(`WETH balance too low.`);
      }
    
      // Sign your maker order
      const signature = await lr.signMakerOrder(maker);
      console.log('signature', signature);
      console.log('maker', JSON.stringify(maker));
    }
    
    main();
    

Sign Maker

  • // Sign your maker order
    const signature = await lr.signMakerOrder(maker);
    

Execute Maker

  • import { LooksRare, StrategyType, CollectionType, QuoteType } from '@looksrare/sdk-v2';
    import { providers, Wallet } from 'ethers';
    
    async function main() {
      const chainId: number = 2021;
      const provider = new providers.JsonRpcProvider(
        'https://ronin-saigon.core.chainstack.com/?', // 替换为你的实际 RPC 节点',
        {
          name: 'saigon',
          chainId,
        }
      );
    
      const protocolAddress = '0xc8641627a4f01c1b9f4c6ecf5baa1da1a3dfb76a';
      const transferManagerAddress = '0x832690ec980e03890bfc5ad9ce31b2dbb77ea6d9';
      const wethAddress = '0x29C6F8349A028E1bdfC68BFa08BDee7bC5D47E16';
      const privateKey = '?'; // 替换为你的实际私钥
      const signer = new Wallet(privateKey, provider);
      const lr = new LooksRare(chainId, provider, signer, {
        LOOKS: '',
        EXCHANGE_V2: protocolAddress,
        TRANSFER_MANAGER_V2: transferManagerAddress,
        WETH: wethAddress,
        ORDER_VALIDATOR_V2: '',
        REVERSE_RECORDS: '',
        LOOKS_LP_V3: '',
        STAKING_POOL_FOR_LOOKS_LP: '',
        AGGREGATOR_UNISWAP_V3: '',
      });
    
      const makerOrder = {
        // 替换为你的 maker 订单数据
      };
    
      const signature = '?'; // 替换为你的签名
    
      const takerOrder = lr.createTaker(makerOrder, await signer.getAddress());
    
      console.log(takerOrder);
    
      const { maxFeePerGas, maxPriorityFeePerGas } = await provider.getFeeData();
      const { call } = lr.executeOrder(makerOrder, takerOrder, signature);
      const tx = await call({
        maxFeePerGas: maxFeePerGas!.div(3).mul(40),
        maxPriorityFeePerGas: maxPriorityFeePerGas?.div(3).mul(40),
      });
      const receipt = await tx.wait();
      console.log(receipt);
    }
    
    main();