Skip to main content

Filling an OpenSea Order

Learn how to find and fill a Seaport Protocol order#

Now anyone can fill sales orders made on OpenSea using the Zora API. This guide will walk you through how to fill a Seaport order with the following steps:

  • Get the Calldata for an Order
  • Verify the Order is Valid
  • Submit the Order to the Blockchain

Getting the Order#

First, we will need to make a request to the Zora API using the offchainOrders query. If we are looking for an order of a specific NFT we can provide the contract address and tokenId.

The most important pieces of data that we need from the response are:

  • calldata: The data that is needed to fill the order.
  • contractAddress: The contract that fills this order e.g. Seaport.
  • price: The price of NFT for the order. Decimal returns the value in Ether.

Single NFT Order#

In the case below we are looking for an order associated with the Zorbs NFT contract with tokenId 31790.

query OffChainZorbOrder {  offchainOrders(where: {tokens: {    address: "0xca21d4228cdcc68d4e23807e5e370c07577dd152",     tokenId: "31790"}    }) {    nodes {      offchainOrder {        calldata        contractAddress        price {          chainTokenPrice {            decimal          }        }      }      token {        collectionName        tokenId      }    }  }}

Collection Orders Sorted by ETH Price#

In addition, it is also possible to search for orders based on the price, seller address, and/or collection address. Below we are searching for orders created for Nouns and sorted by ascending ETH price.

query NounsOrder {  offchainOrders(  where: {collectionAddresses: "0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03"},   sort: {sortKey: CHAIN_TOKEN_PRICE, sortDirection: ASC}) {    nodes {      offchainOrder {        collectionAddress        tokenId        calldata        price {          nativePrice {            decimal          }        }      }    }  }}

Verifying#

Once you have the calldata for the NFT you would like to purchase, you can then use Zora's rest endpoint to confirm that the order is still valid.

Make a POST request to https://api.zora.co/validate-contract-call and provided the following JSON in the body of the request:

{  "caller_address": "YOUR_ADDRESS",  "contract_address": "0x00000000006c3852cbef3e08e8df289169ede581", // The contract that fills the orders e.g. Seaport  "calldata": "0xfb0f3ee10000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063c8117529600000000000000000000000000025659eefd7cae21566a257420bbcb0c8317ef94f000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c00000000000000000000000000ca21d4228cdcc68d4e23807e5e370c07577dd1520000000000000000000000000000000000000000000000000000000000007c2e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062e930e10000000000000000000000000000000000000000000000000000000063120f61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001538739839173460000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000002c0dbbfb370000000000000000000000000008de9c5a032463c561423387a9648c5c7bcc5bc9000000000000000000000000000000000000000000000000000079968bf2cb000000000000000000000000000d1d1d4e36117ab794ec5d4c78cbd3a8904e691d0000000000000000000000000000000000000000000000000000000000000004169d9d167a41f282ee33134ce0fa3041b458904ad1c85255f712dd6dd10f1ad8c65dd4534f4a5d1f5ee936224bc50fea910864530a3f06e534d89c6c2a1fdb5021b00000000000000000000000000000000000000000000000000000000000000",  "value": 0.031 // Price in Ether (Decimals Price)}

A successful response will have the following message:

{    "message": "successfully simulated contract call"}

This means that the order is still valid and can be filled.


Submitting#

Now that we have retrieved the calldata for the order and confirmed that it is valid, we can submit a transaction to the blockchain to fill the order.

// Uses an ethers.js signer instance: // You can get the signer from a wallet using web3modal/rainbowkit etc.// See: https://docs.ethers.io/v5/api/signer/
import { ethers } from "ethers";const provider = new ethers.providers.Web3Provider(web3.currentProvider, 1);const signer = await provider.getSigner();
const decimalPrice = "0.031"; // Price in ETHconst weiPrice = ethers.utils.parseEther(decimalPrice);
const tx = {  from: "YOUR_ADDRESS",  to: "0x00000000006c3852cbef3e08e8df289169ede581", // Contract that fills the order e.g. Seaport  value: weiPrice, // Price in Wei  data: "0xfb0f3ee10000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063c8117529600000000000000000000000000025659eefd7cae21566a257420bbcb0c8317ef94f000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c00000000000000000000000000ca21d4228cdcc68d4e23807e5e370c07577dd1520000000000000000000000000000000000000000000000000000000000007c2e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062e930e10000000000000000000000000000000000000000000000000000000063120f61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001538739839173460000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000002c0dbbfb370000000000000000000000000008de9c5a032463c561423387a9648c5c7bcc5bc9000000000000000000000000000000000000000000000000000079968bf2cb000000000000000000000000000d1d1d4e36117ab794ec5d4c78cbd3a8904e691d0000000000000000000000000000000000000000000000000000000000000004169d9d167a41f282ee33134ce0fa3041b458904ad1c85255f712dd6dd10f1ad8c65dd4534f4a5d1f5ee936224bc50fea910864530a3f06e534d89c6c2a1fdb5021b00000000000000000000000000000000000000000000000000000000000000" ,}
const result = await signer.sendTransaction(tx)

Once the transaction has confirmed, congrats you have completed your first OpenSea NFT purchase using the Zora API.

Filling an Order Start to Finish#

How to go from start to finish for finding an order to filling it client side. Note that this is example code and will need to be tweeked for your application.

// You can get the signer from a wallet using web3modal/rainbowkit etc.// See: https://docs.ethers.io/v5/api/signer/ and https://www.apollographql.com/docs/react/get-started/
import {ApolloClient, InMemoryCache, gql} from '@apollo/client';import axios from "axios";import { ethers } from "ethers";
// Ethers Signerconst provider = new ethers.providers.Web3Provider(web3.currentProvider, 1);const signer = await provider.getSigner();
// Apollo Clientconst client = new ApolloClient({  uri: 'https://api.zora.co/graphql',  cache: new InMemoryCache()})
// 1) Get Order// Sorts Noun orders by ascending ETH price and returns the cheapest Nounconst cheapestNounOrder = gql`  query CheapestNoun {  offchainOrders(    where: {collectionAddresses: "0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03"},     sort: {sortKey: CHAIN_TOKEN_PRICE, sortDirection: ASC},     pagination: {limit: 1}) {    nodes {      offchainOrder {        contractAddress        tokenId        calldata        price {          nativePrice {            decimal          }        }      }    }  }}`
const {data} = await client.query({query: cheapestNounOrder})const cheapestNoun = data.nodes[0].offchainOrders;

// 2) Verfiy const postObj = {  caller_address: "YOUR_ADDRESS",  contract_address: cheapestNoun.contractAddress, // The contract that fills the orders e.g. Seaport  calldata: cheapestNoun.calldata,  value: cheapestNoun.price.nativePrice.decimal, // Price in Ether (Decimals Price)};
try {  const response = await axios.post(    "https://api.zora.co/validate-contract-call",    postObj  );
  // Make a check to confirm that order is still valid  if (response.data.message !== "successfully simulated contract call")    return;} catch (error) {  console.log(error);}
// 3) Submitconst decimalPrice = cheapestNoun.price.nativePrice.decimal; // Price in ETHconst weiPrice = ethers.parseEther(decimalPrice.toString());
const tx = {  from: "YOUR_ADDRESS",  to: cheapestNoun.contractAddress, // Contract that fills the order e.g. Seaport  value: weiPrice, // Price in Wei  data: cheapestNoun.calldata,};
const result = await signer.sendTransaction(tx)