Tatum
Search…
How to create a peer-to-peer NFT marketplace like OpenSea
Open Sea fixed-priced assets listing in 5 API calls.
Peer-to-peer NFT marketplaces like OpenSea allow users to create NFTs with metadata (pictures, videos, songs, 3D art, etc.) and post listings to sell them to one another. OpenSea then takes a percentage of each sale.
If you want to build a backend for your own peer-to-peer NFT marketplace, you’ll definitely want to implement the same functionality.
Creating NFTs has always been super simple with Tatum. But now, you can also create NFT marketplaces and listings using just a few API calls! When the NFT is sold, the creator is automatically paid, the NFT is instantly transferred to the buyer, and you as the owner of the marketplace automatically receive a percentage of the transaction. Everything happens on the blockchain, so you don’t even need to create a complex, application-level backend to run the listings.
With this type of marketplace, your users can create listings to sell ERC-721 or ERC-1155 tokens. These tokens can be purchased with the native assets of the given blockchain (e.g. ETH on Ethereum or MATIC on Polygon) or any ERC-20 token available on your blockchain of choice.
Currently supported blockchains are Ethereum, Celo, Polygon, Binance Smart Chain, and Harmony.ONE.
All smart contracts are available here.

Import required libraries

If you are using our JavaScript SDK, the first step is to import all of the required JS libraries for the functions we will be using.
JavaScript
1
import {
2
deployMarketplaceListing,
3
sendMarketplaceApproveErc20Spending,
4
sendMarketplaceBuyListing,
5
sendMarketplaceCreateListing,
6
sendCeloSmartContractReadMethodInvocationTransaction,
7
sendAuctionApproveNftTransfer
8
} from '@tatumio/tatum';
Copied!

Deploy your own marketplace

Next, you must create your own marketplace smart contract. This is a one-time operation, and the marketplace you deploy will be used for every listing in your application. In this example, we'll deploy our marketplace on the Celo blockchain.
JavaScript
cURL + KMS
cURL + private key
1
const body = new DeployMarketplaceListing()
2
body.fromPrivateKey = '0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236'
3
body.feeRecipient = '0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA'
4
body.marketplaceFee = 150
5
body.feeCurrency = Currency.CUSD
6
body.chain = Currency.CELO
7
const test = await deployMarketplaceListing(true, body, 'https://alfajores-forno.celo-testnet.org')
8
console.log(test)
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"feeRecipient": "0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
7
"marketplaceFee": 150,
8
"chain": "CELO",
9
"signatureId": "26d3883e-4e17-48b3-a0ee-09a3e484ac83",
10
"index": 0,
11
"nonce": 1,
12
"fee": {
13
"gasLimit": "40000",
14
"gasPrice": "20"
15
}
16
}'
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"feeRecipient": "0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
7
"marketplaceFee": 150,
8
"chain": "CELO",
9
"fromPrivateKey": "0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236"
10
"fee": {
11
"gasLimit": "40000",
12
"gasPrice": "20"
13
}
14
}'
Copied!
The body of the API request should contain the following parameters:
  • chain - The blockchain on which the marketplace smart contract will be deployed.
  • fromPrivateKey - The private key for the address that will pay for the deployment of the marketplace smart contract.
  • feeRecipient - The address where the fees will be sent, i.e. your address as the marketplace owner.
  • marketplaceFee - This is a percentage of the price that will be charged to the buyer when they make a purchase. The value is the percentage charged * 100, so a 1.5% fee is written as 150. This percentage is the same for all sales made on your marketplace.
  • feeCurrency - The currency in which the gas fee for the operation will be paid (only for Celo).
  • fee - The gas price and limit for the gas fee to pay for the operation. If this fee is absent, the fee will be calculated automatically.
Response
1
{
2
"txId": "0x9ff62d44abaf65018081a6511c84ca8f89d7575d2a1ea058e93c1b7d57ff1807"
3
}
Copied!
The response is a transaction ID from which we can obtain the contract address.

Create a fixed price asset listing

Once the marketplace is ready, you can enable listings for your users. The NFT asset must be present at the address of the seller before they create an NFT listing. The seller can choose the price of the asset, whether they are selling an ERC-721 or ERC-1155 token, and whether they are selling it for ERC-20 tokens or for the native currency of the given blockchain (in our case CELO).
Every listing must have a unique listingId. This ID identifies the listing and is used for performing other operations.
The following API call will create a fixed-price NFT listing on your marketplace:
JavaScript
cURL + KMS
cURL + private key
1
const body = new CreateMarketplaceListing()
2
body.fromPrivateKey = '0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236'
3
body.contractAddress = '0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9'
4
body.nftAddress = '0x1214BEada6b25bc98f7494C7BDBf22C095FDCaBD'
5
body.tokenId = '1'
6
body.listingId = '1'
7
body.isErc721 = true
8
body.price = '1'
9
body.erc20Address = '0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA'
10
body.seller = '0x48d4bA7B2698A4b89635b9a2E391152350DB740f'
11
body.feeCurrency = Currency.CUSD
12
body.chain = Currency.CELO;
13
console.log(await sendMarketplaceCreateListing(true, body, 'https://alfajores-forno.celo-testnet.org'));
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing/sell \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"chain": "CELO",
7
"feeCurrency": "CELO",
8
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
9
"nftAddress": "0x1214BEada6b25bc98f7494C7BDBf22C095FDCaBD",
10
"seller": "0x48d4bA7B2698A4b89635b9a2E391152350DB740f",
11
"erc20Address": "0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
12
"listingId": "string",
13
"amount": "1",
14
"tokenId": "1",
15
"price": "1",
16
"isErc721": true,
17
"signatureId": "26d3883e-4e17-48b3-a0ee-09a3e484ac83",
18
"index": 0,
19
"nonce": 1,
20
"fee": {
21
"gasLimit": "40000",
22
"gasPrice": "20"
23
}
24
}'
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing/sell \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"chain": "CELO",
7
"feeCurrency": "CELO",
8
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
9
"nftAddress": "0x1214BEada6b25bc98f7494C7BDBf22C095FDCaBD",
10
"seller": "0x48d4bA7B2698A4b89635b9a2E391152350DB740f",
11
"erc20Address": "0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
12
"listingId": "string",
13
"amount": "1",
14
"tokenId": "1",
15
"price": "1",
16
"isErc721": true,
17
"fromPrivateKey": "0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236",
18
"nonce": 1,
19
"fee": {
20
"gasLimit": "40000",
21
"gasPrice": "20"
22
}
23
}'
Copied!
The seller can choose whether they are selling an ERC-721 or ERC-1155 token, and whether they are selling it for ERC-20 tokens or for the native currency of the given blockchain (in our case CELO). Every marketplace listing must have a unique id. This ID identifies the listing and is used for performing other operations.
The body of the API request should contain the following parameters:
  • chain - The chain on which the listing will be created (same as in the first call).
  • fromPrivateKey - The private key of the address that will pay for the creation of the NFT listing.
  • contractAddress - The address of the marketplace smart contract you deployed in the previous call.
  • nftAddress - The address of the smart contract of the NFT being sold in the listing.
  • tokenId - The ID of the NFT to be sold in the listing.
  • listingId - The ID of the listing to sell the NFT.
  • isErc721 - Set to "true" if you are selling an ERC-721 or equivalent token. Set to "false" if you are selling an ERC-1155 token.
  • price - The price of the NFT being sold.
  • erc20Address (optional) - If the seller is selling their NFT for an ERC-20 (or equivalent) token, this field should be included and contain the address of the smart contract of the ERC-20 token.
  • seller - The address that holds the NFT being sold and to which the funds used to purchase the NFT will be sent.
  • feeCurrency - The currency in which the gas fee for the operation will be paid (only for Celo).
  • fee - The gas price and limit for the gas fee to pay for the operation. If this fee is absent, the fee will be calculated automatically.
To use an ERC-20 token as a listing currency, the seller should add an erc20Address parameter to the call with the address of the smart contract of the ERC-20 token that is used for the listing.
Response
1
{
2
"txId": "0x9ff62d44abaf65018081a6511c84ca8f89d7575d2a1ea058e93c1b7d57ff1807"
3
}
Copied!
The response is a transaction ID. The listing is now available in the marketplace and the seller must now send approval for the NFT to be sold on the marketplace.

Send approval for the NFT to be sold on the marketplace

Next, we must give the marketplace permission to transfer the NFT we are selling.
JavaScript
cURL + KMS
cURL + private key
1
console.log(await sendAuctionApproveNftTransfer(true, {
2
fromPrivateKey: '0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236',
3
chain: Currency.CELO,
4
contractAddress: '0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9',
5
isErc721: true,
6
spender: '0x991dfc0db4cbe2480296eec5bcc6b3215a9b7038',
7
tokenId:'1'
8
}, 'https://alfajores-forno.celo-testnet.org'));
9
10
const endedAt = (await celoGetCurrentBlock()) + 9;
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/auction/approve \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"chain": "CELO",
7
"feeCurrency": "CELO",
8
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
9
"spender": "0x991dfc0db4cbe2480296eec5bcc6b3215a9b7038",
10
"isErc721": true,
11
"tokenId": "1",
12
"signatureId": "26d3883e-4e17-48b3-a0ee-09a3e484ac83",
13
"index": 0,
14
"nonce": 1,
15
"fee": {
16
"gasLimit": "40000",
17
"gasPrice": "20"
18
}
19
}'
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/auction/approve \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"chain": "CELO",
7
"feeCurrency": "CELO",
8
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
9
"spender": "0x991dfc0db4cbe2480296eec5bcc6b3215a9b7038",
10
"isErc721": true,
11
"tokenId": "1",
12
"fromPrivateKey": "0xa488a82b8b57c3ece4307525741fd8256781906c5fad948b85f1d63000948236",
13
"nonce": 1,
14
"fee": {
15
"gasLimit": "40000",
16
"gasPrice": "20"
17
}
18
}'
Copied!
The body of the API request should contain the following parameters:
  • fromPrivateKey - The private key of the address that will pay for the gas fees for the approval operation.
  • chain - The chain on which the operation will take place (the same as in the first two API requests).
  • contractAddress - The address of the ERC-20 token used to buy the NFT from the marketplace.
  • isErc721 - Set to "true" if you are selling an ERC-721 or equivalent token. Set to "false" if you are selling an ERC-1155 token.
  • spender - The address of the marketplace smart contract that will be transferring the token.
  • tokenId - The ID of the NFT being sold.
  • feeCurrency - The currency in which the gas fee for the operation will be paid (only for Celo).
  • fee - The gas price and limit for the gas fee to pay for the operation. If this fee is absent, the fee will be calculated automatically.

Approve ERC-20 for spending and for cashback

For a listing for an NFT being sold for ERC-20 tokens, the buyer must approve the marketplace to spend his ERC-20 tokens before the actual buy operation. Here is the call to approve the marketplace for spending the ERC-20 token:
JavaScript
cURL + KMS
cURL + private key
1
const approve = new ApproveErc20();
2
approve.contractAddress = '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1';
3
approve.spender = '0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA';
4
approve.chain = Currency.CELO;
5
approve.feeCurrency = Currency.CELO;
6
approve.amount = '0.001015';
7
approve.fromPrivateKey = '0x4874827a55d87f2309c55b835af509e3427aa4d52321eeb49a2b93b5c0f8edfb';
8
approve.fee = {gasPrice: '5', gasLimit: '300000'};
9
console.log(await sendAuctionApproveErc20Transfer(true, approve, 'https://alfajores-forno.celo-testnet.org'));
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/token/approve \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--header 'x-testnet-type: SOME_STRING_VALUE' \
6
--data '{
7
"chain": "CELO",
8
"amount": "0.001015",
9
"spender": "0x687422eEA2cB73B5d3e242bA5456b782919AFc85",
10
"contractAddress": "0x687422eEA2cB73B5d3e242bA5456b782919AFc85",
11
"signatureId": "26d3883e-4e17-48b3-a0ee-09a3e484ac83",
12
"nonce": 0,
13
"feeCurrency": "CELO"
14
"fee": {
15
"gasLimit": "30000",
16
"gasPrice": "5"
17
}
18
}'
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/token/approve \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--header 'x-testnet-type: SOME_STRING_VALUE' \
6
--data '{
7
"chain": "CELO",
8
"amount": "0.001015",
9
"spender": "0x687422eEA2cB73B5d3e242bA5456b782919AFc85",
10
"contractAddress": "0x687422eEA2cB73B5d3e242bA5456b782919AFc85",
11
"fromPrivateKey": "0x4874827a55d87f2309c55b835af509e3427aa4d52321eeb49a2b93b5c0f8edfb",
12
"feeCurrency": "CELO"
13
"fee": {
14
"gasLimit": "30000",
15
"gasPrice": "5"
16
}
17
}'
Copied!
The body of the API request should contain the following parameters:
  • chain - The chain on which the transaction will take place (same as in the previous calls).
  • contractAddress - The address of the ERC-20 smart contract to be approved for spending.
  • spender - The address of the marketplace smart contract that will spend the ERC-20 token.
  • amount - The amount of funds to be approved to spend. You need to approve amount to be paid for the listing + the marketplace fee.
  • fromPrivateKey - The private key of the address that will pay for the gas fees for the approval operation.
  • feeCurrency - The currency in which the gas fee for the operation will be paid (only for Celo).
  • fee - The gas price and limit for the gas fee to pay for the operation. If this fee is absent, the fee will be calculated automatically.
For NFTs to payout cashback as ERC-20 tokens, this same approve ERC-20 spending endpoint must be invoked.

Purchase an NFT asset from a listing

Now, the NFT can be purchased from the NFT listing by any buyer with enough funds in their account. The buyer must enter the price of the listing + the marketplace fee.
The listing in the example is for 1 CELO and the fee is 1.5%, so the buyer would have to spend 1.015 CELO to buy the asset.
Use the following API call to purchase an NFT from a listing on the marketplace:
JavaScript
cURL + KMS
cURL + private key
1
const body = new InvokeMarketplaceListingOperation()
2
body.fromPrivateKey = '0x4874827a55d87f2309c55b835af509e3427aa4d52321eeb49a2b93b5c0f8edfb'
3
body.contractAddress = '0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9'
4
body.listingId = '1'
5
body.amount = '1.015'
6
body.feeCurrency = Currency.CELO
7
body.chain = Currency.CELO;
8
console.log(await sendMarketplaceBuyListing(true, body, 'https://alfajores-forno.celo-testnet.org'));
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing/buy \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
7
"listingId": "1",
8
"amount": "1.015",
9
"chain": "CELO",
10
"signatureId": "26d3883e-4e17-48b3-a0ee-09a3e484ac83",
11
"index": 0,
12
"nonce": 1,
13
"fee": {
14
"gasLimit": "40000",
15
"gasPrice": "20"
16
}
17
}'
Copied!
1
curl --request POST \
2
--url https://api-eu1.tatum.io/v3/blockchain/marketplace/listing/buy \
3
--header 'content-type: application/json' \
4
--header 'x-api-key: REPLACE_KEY_VALUE' \
5
--data '{
6
"contractAddress": "0xd093bEd4BC06403bfEABB54667B42C48533D3Fd9",
7
"listingId": "1",
8
"amount": "1.015",
9
"chain": "CELO",
10
"fromPrivateKey": "0x4874827a55d87f2309c55b835af509e3427aa4d52321eeb49a2b93b5c0f8edfb"
11
"fee": {
12
"gasLimit": "40000",
13
"gasPrice": "20"
14
}
15
}'
Copied!
The body of the API request should contain the following parameters:
  • chain - The chain on which the transaction will take place (same as in the previous calls).
  • fromPrivateKey - The private key of the address that will pay for the NFT and the gas fees for the approval operation.
  • contractAddress - The address of the marketplace smart contract.
  • listingId - The ID of the NFT listing as specified when the listing was created.
  • amount - The price of the NFT listing + the marketplace fee to be transferred to purchase the NFT - only valid for listings in native - ETH | BSC | MATIC currencies. For ERC20 based listings, ommit this value.
  • feeCurrency - The currency in which the gas fee for the operation will be paid (only for Celo).
  • fee - The gas price and limit for the gas fee to pay for the operation. If this fee is absent, the fee will be calculated automatically.
To use an ERC-20 token as a listing currency, the seller should add an erc20Address parameter to the call with the address of the smart contract of the ERC-20 token that is used for the listing.
Response
1
{
2
"txId": "0x18117c5e8fdef378c449c06c9306150067b499f04cb8ced8c0de6189d4246f8f"
3
}
Copied!
The response is a transaction Id. In this operation, the NFT is transferred to the buyer, the amount is transferred to the seller, and the fee is transferred to the fee recipient of the marketplace.
You can take a look at an example transaction on the Mumbai Testnet. The buyer entered an even bigger amount to buy the NFT, so the rest was returned to him.
And that's it, just a few API calls and you can create a full peer-to-peer NFT marketplace for your users. For ERC-1155 listings, the only difference is the number of tokens that are being listed.

Workflow tutorials

For how to work with external NFTs, Tatum royalty NFTs, and selling NFTs for native assets or ERC-20 tokens, check out our tutorials: