List an NFT on Polygon

The following tutorial is done in Python.

Step 1: Define parameters

import time
import json
import requests

# Reach out to us for a token: https://forms.gle/VyCZPCBi4ECVQJ4U8
api_token = "ask-us-for-a-token"

# Maker is willing to sell #200 of 0xc9F07b9B359fA16e1af9df6fAC5540FbDa658e8E for 10 MATIC
maker_address = "0x487bFB4e3A69cf34adddA893EEC6fDdEF19aDAfA"
nft_address = "0xeC6F34d2052D9Da956295F0Bf8034b2B900C9D54"
nft_id = "200"

# Purchase price is 10 MATIC (MATIC has 18 decimals)
purchase_price = str(int(10 * 1e18))

# Expires in 1 hour
expiry_epoch = int(time.time() + 3600)

# TODO: add real API domain below.
acilia_api_domain = "api.acilia.llc"

Step 2: Request listing call-data

payload = json.dumps({
  "chain": "POLYGON",
  "maker": {
    "type": "ERC721",
    "owner": maker_address,
    "tokenId": nft_id,
    "tokenAddress": nft_address
  },
  "taker": {
    "type": "NATIVE",
    "baseUnitAmount": purchase_price,
  },
  "timeout": {
    "type": "ABSOLUTE",
    "value": expiry_epoch
  }
})
headers = {
  'Authorization': f'Bearer {api_token}',
  'Content-Type': 'application/json'
}

response = requests.request(
  "POST",
  f"https://{acilia_api_domain}/v1/order/list",
  headers=headers,
  data=payload
)

response = response.json()

Let's look at the data coming back from the API. Inline is an explanation of each field:

{
  // The `uid` field is a unique identifier for the Acilia API. You can use this UID field to load this same order
  // back into the API and generate useful calldata for the taker
  "aciliaId": "85LbktFBFtSyuQgaeE5gtoexjW33ysnuPdkYbLwzxJfaZew6mZxgf3JKXcNJvegNQHaBJZcvEHfmHSAbCGjKGKktwyEJDp3W5DfFPQdU2F4pzyzdzPDEsNFkNhJr2cVzBseagap4gzPe6G1zsUYzNpKXKbMEqXFbEyQpiScGcCFu8n19w5sm2A9os1AQ3e9FdFKMnC6DnFbSv7XYtZUoY3igYHmhpXTAZ92qFDdh8nqAhYu6NcPo9jpiJj3ojw2zoQEKXz4cMVsGai8iSDGDM1a",
  // The order field is the NFT V4 order generated by the API. See more here:
  // https://protocol.0x.org/en/latest/basics/orders.html#erc721-orders
  "order": {
    "direction": "SELL",
    "chain": "POLYGON",
    "maker": "0x487bFB4e3A69cf34adddA893EEC6fDdEF19aDAfA",
    "taker": "0x0000000000000000000000000000000000000000",
    "erc721TokenId": "2",
    "erc721Token": "0xeC6F34d2052D9Da956295F0Bf8034b2B900C9D54",
    "fees": [],
    "status": "FILLABLE",
    "erc20Token": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    "erc20TokenAmount": "20000000000000000000",
    "hash": "0x1216a1e07abfcbc223bdbc17c86dd770ef03f196599b4e830d57f756380239e8",
    "expiry": "1846785190",
    "nonce": "100131415900000000000000000000000000000108258336074355952321641756238660820183"
  },
  
  
  
  // Below are a list of transactions that are to be sent by the maker in order
  // to list this NFT on-chain.
  // Step 1 is SET_ALLOWANCE: allows the 0x protocol to swap NFTs for the address you specified
  // Step 2 is LIST_NFT: lists your NFT on-chain.
  "operation": {
    "success": true,
    
    // listingInstructions will only appear if success is true. These are to be executed in order
    "listingInstructions": [
      {
        "step": "SET_ALLOWANCE",
        "transaction": {
          "from": "0x487bFB4e3A69cf34adddA893EEC6fDdEF19aDAfA",
          "to": "0xeC6F34d2052D9Da956295F0Bf8034b2B900C9D54",
          "data": "0xa22cb465000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff0000000000000000000000000000000000000000000000000000000000000001",
          "value": "0"
        }
      },
      {
        "step": "LIST_NFT",
        "transaction": {
          "from": "0x487bFB4e3A69cf34adddA893EEC6fDdEF19aDAfA",
          "to": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF",
          "data": "0x462103af00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000487bFB4e3A69cf34adddA893EEC6fDdEF19aDAfA0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e13b4a6dd605f7d554cc445cede96d76a09099069e569173872ba2f87f3a6683828dcd7000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000001158e460913d000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000eC6F34d2052D9Da956295F0Bf8034b2B900C9D540000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
          "value": "0"
        }
      }
    ]
  }
}

Most of the data in the API response is not actionable and is meant for the developer to do spot-checks. The listingInstructions contain transactions that can be submitted on-chain to perform the listing.

Step 3: Execute the call-data as the maker

def send_transaction(tx_fields, private_key):
    """
    Sends a transaction for a user
    :param tx_fields: a dictionary containing fields "from", "to", "data", "value"
    :param private_key: the private key for the address at tx_fields["from"]
    """
    # Fetch new nonce for user
    nonce_field = web3_client.eth.getTransactionCount(from_address)
    
    # Fetch fields to, value, data from the transaction
    to_field = tx_fields['to']
    data_field = tx_fields['data']
    value_field = int(tx_fields['value'])
    
    # Generate the signed transaction
    signed_transaction = web3_client.eth.account.sign_transaction({   
        # For the purpose of this demo, the first few parameters are set as safe defaults
        'chainId': 137,
        'gasPrice': int(200 * 1e9),
        'from': web3.Web3.toChecksumAddress(from_address),
        'to': web3.Web3.toChecksumAddress(to_field),
        'value': value_field,
        'data': data_field,
        'nonce': nonce_field,
    }, private_key)
    
    # Wait for transaction to settle
    tx_receipt = web3_client.eth.sendRawTransaction(signed_transaction.rawTransaction)
    web3_client.eth.waitForTransactionReceipt(tx_receipt)
    print(f"Transaction {tx_receipt.hex()} was successfully mined.")


# NOTE: Remember always to spot-check the data coming back from the response before it is submitted!
# Check our guide on "Spot checks"
assert result['operation']['success'] == true
for operation in result['operation']['listingInstructions']:
    send_transaction(operation['transaction'], ALICE_PK)

Congratulations! your NFT was listed on-chain! You can now use the UID in the response to fill or cancel the listing!

Last updated