The order payload is a string of encode data containing the necessary information to create a new order. Follow the steps below to generate the payload.

Encode token data

// ERC-721
const data = ethers.utils.defaultAbiCoder.encode(
    [`tuple(address token, uint256 tokenId)[]`],
    [{ token: tokenAddress, tokenId: tokenId}]
)

// ERC-1155
const data = ethers.utils.defaultAbiCoder.encode(
    [`tuple(address token, uint256 tokenId, uint256 amount)[]`],
    [{ token: tokenAddress, tokenId: tokenId, amount: 1}] //ERC-1155 listing only accepts amount of 1.
)

Generate dataMask

The dataMask needs to be generated when creating a collection offer.
Otherwise, it should be 0x.

// Generate dataMask for EC-721 collection offer
const dataMask = ethers.utils.defaultAbiCoder.encode(
  [`tuple(address token, uint256 tokenId)[]`],
  [{ token: ethers.constants.AddressZero, tokenId: '0x' + '1'.repeat(64)}]
)
// Generate dataMask for EC-1155 collection offer
const dataMask = ethers.utils.defaultAbiCoder.encode(
  [`tuple(address token, uint256 tokenId, uint256 amount)[]`],
  [{ token: ethers.constants.AddressZero, tokenId: '0x' + '1'.repeat(64), amount: 1}]
)
// Otherwise
const dataMask = '0x'

Generate order hash

📘

Price limitation

Order price must be no less than 0.0001 ETH and not more than 100000000 ETH.

const items = [{
    price: '1000000000000000000', // String, price in wei, e.g. '1000000000000000000' for 1 ETH
    data
}]
const order = {
    salt, // Random string at a recommended length of 64 characters.
    user, // Order maker's address
    network: 1, // 1: Mainnet; 5: Goerli testnet
    intent: 1, // 1: Create listing or change listing price; 3: Create offer or collection offer
    delegateType: 1, // 1: ERC-721; 2: ERC-1155
    deadline: 1668738550, // number, the unix timestamp when the listing will expire, in seconds. Must be at least 15 minutes later from now.
    currency: '0x0000000000000000000000000000000000000000', // Use ETH for new listing, use WETH for new offer
    dataMask: dataMask,
    items,
    r: '',
    s: '',
    v: 0,
    signVersion: 1,
}
// encode order data
const orderData: string = ethers.utils.defaultAbiCoder.encode(
    [
        `uint256`,
        `address`,
        `uint256`,
        `uint256`,
        `uint256`,
        `uint256`,
        `address`,
        `bytes`,
        `uint256`,
        `tuple(uint256 price, bytes data)[]`
    ],
    [
        order.salt,
        order.user,
        order.network,
        order.intent,
        order.delegateType,
        order.deadline,
        order.currency,
        order.dataMask,
        order.items.length,
        order.items,
    ]
)
const orderHash = ethers.utils.keccak256(orderData)

Generate user signature and save it in order data

This step will ask the order maker's wallet to sign the order data.

const orderSig = (await web3Provider.send('personal_sign', [
    orderHash,
    order.user,
  ])) as string
order.r = `0x${orderSig.slice(2, 66)}`
order.s = `0x${orderSig.slice(66, 130)}`
order.v = parseInt(orderSig.slice(130, 132), 16)
// in geth its always 27/28, in ganache its 0/1. Change to 27/28 to prevent
// signature malleability if version is 0/1
// see https://github.com/ethereum/go-ethereum/blob/v1.8.23/internal/ethapi/api.go#L465
if (order.v < 27) {
    order.v = order.v + 27
}

Encode order data

const orderPayload = ethers.utils.defaultAbiCoder.encode(
    [`tuple(uint256 salt, address user, uint256 network, uint256 intent, uint256 delegateType, uint256 deadline, address currency, bytes dataMask, ${orderItemParamType}[] items, bytes32 r, bytes32 s, uint8 v, uint8 signVersion)`], 
    [order]
)