How to Build a NFT Market with Locksrare Protocol
Contents
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
noncefromLooksRareProtocol -
check that collection has approved the
TransferManager- allow
TransferManagerto transfer NFT assets
- allow
-
check that
TransferManagerhas approvedLooksRareProtocol -
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
noncefromLooksRareProtocol -
check that currency has approved the
LooksRareProtocol- allow
LooksRareProtocolto transfer fungible tokens, allowance must be greater than the bid price
- allow
-
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();