# Marketplace

# Why Marketplace?

While NRC-20 has built-in support for fungible token wrappers, which can wrap NRC-20 into NEP-141, and unwrap back, like $NEAT did (opens new window) (which will also be publicly accessible for all NRC-20 tokens shortly), we respect community's preference and interest of a native NRC-20 marketplace that is especially useful for NRC-20 tokens that are not yet ready to be listed on DEX or CEX.

Now the NRC-20 marketplace, a public inscription infrastructure, is available to all builders and users.

# Design

The core of the NRC-20 marketplace is its smart contract and indexer, built by NEAT team. We have implemented the marketplace in a way that makes the minimum change to the NRC-20 standard, which means we don't need to introduce another new operation to build the project.

The marketplace smart contract allows listing, canceling and purchase of orders.

  • When listing an order, the seller needs to set the listing price, and send the amount of token to marketplace, which will be verified by the NRC-20 indexer before the listing is successfully accepted. If the marketplace didn't receive the amount of token in certain period of time (e.g. 5 mins), the listing will be rejected.
  • Cancelling an order will return the sent token back to the seller.
  • When buying a listed order, the deposited NEAR must be sufficient to cover the order's value plus fees (e.g. with 1~2% fee rate). The fee rate is usually decided by the protocol and the deployer or the community of the NRC-20 token.

The marketplace indexer supports querying markets, orders, sellers and activities, which eases the development of a marketplace web application.

  • Markets: query all the markets that have listed orders.
  • Orders: query all orders in each market including pending, accepted, rejected, cancelled, and filled orders.
  • Sellers: query seller information such as total listed amount in one market.
  • Activities: query all order related events such as List, AcceptList, RejectList, Cancel and Buy.

nearscription (opens new window) is one marketplace web frontend built by community, on top of the above marketplace smart contract and indexer infrastructure.

# Contract

The marketplace smart contract allows listing, canceling and purchase of orders.

# Address

marketplace.nrc-20.near

# Operations

The 3 core operations of a marketplace are:

  1. list order
  2. cancel order
  3. buy order

# List Order

List an order, with ticker, price, and amount.

The order will be in pending status before approval or rejection by the indexer. If the seller sends enough tokens to the marketplace contract within 5 minutes, the pending order will be accepted. Otherwise, the pending order will be rejected.

  • method: list
  • args:
{
  ticker: string;   // ticker, e.g. neat
  price: string;    // raw price, U256
  amount: string;   // raw amount, U128
}
  • deposit: 0.01 NEAR to cover storage cost of an order

Notes:

  1. The order price of a given token is calculated as price_multiplier * price * (10 ^ near_decimals) / (10 ^ token_decimals). The price multiplier is 10 ^ 24. For example, if we want to list an NEAT order with price 0.1 NEAR, the price parameter will be (10 ^ 24) * 0.1 * (10 ^ 24) / (10 ^ 8) = 10 ^ 39
  2. A group of two functions, one list function and one inscribe transfer function, needs to be signed and called in a single approval in wallet. The inscribe transfer should transfer sufficient tokens to marketplace contract, otherwise the listing will be rejected.

# Cancel Order

Cancel an order, with ticker and ID.

The user can cancel an order in pending or listed status. If the order has already been rejected or purchased, it cannot be canceled any more.

  • method: cancel
  • args:
{
  ticker: string;   // ticker, e.g. neat
  order_id: string; // unique order id, U64
}
  • deposit: 1 yocto NEAR

# Buy Order

Buy an order, with ticker and ID

  • method: buy
  • args
{
  ticker: string;   // ticker, e.g. neat
  order_id: string; // unique order id, U64
}
  • deposit: deposit enough NEAR to cover order value plus fees. Extra paid NEAR will be refunded.

Notes:

  1. The order value can be calculated as raw_price * token_amount * (10 ^ token_decimals) / price_multiplier. The price multiplier is 10 ^ 24. For example, if we want to buy an order of 100 NEAT and price 0.1 NEAR, the order value will be (10 ^ 39) * 100 * (10 ^ 8) / (10 ^ 24) = 10 ^ 25.
  2. The fee for buying an order can be queried with get_buy_fee_rate(ticker). The fee rate is in basis points (100 means 1%).

# Views

# Markets

(1) Get the number of tickers / markets that have listed orders

  • method: total_ticker_num()
  • args: {}
  • return: number

(2) Query tickers of markets that have listed orders by pagination

  • method: get_tickers
  • args:
{
  from_index?: number;  // start index
  limit?: number;       // number of tickers to return
}
  • return string[]

(3) Get market buy fee rate, in basis points (100 means 1%)

  • method: get_buy_fee_rate
  • args:
{
  ticker: string;       // ticker, e.g. neat
}
  • return: number | null

(4) Get market sell fee rate, in basis points (100 means 1%)

  • method: get_sell_fee_rate
  • args:
{
  ticker: string;       // ticker, e.g. neat
}
  • return: number | null

(5) Get market info

  • method: get_market
  • args:
{
  ticker: string;       // ticker, e.g. neat
}
  • return: Market
Market {
  ticker: string;           // ticker, e.g. neat
  order_num: number;        // number of listed orders in market
  seller_num: number;       // number of sellers in market
  base_fee_rate: number;    // only for reference. you only need buy and sell fee rates in most cases.
  buy_fee_rate: number;     // buy fee rate in basis points (200 means 2%)
  sell_fee_rate: number;    // sell fee rate in basis points (200 means 2%)
}

(6) Get markets by pagination

  • method: get_markets
  • args:
{
  from_index?: number;  // start index
  limit?: number;       // number of markets to return
}
  • return: Market[]

# Orders

(1) Get the total number of listed orders of in one market

  • method: total_order_num
  • args
{
  ticker: string;       // ticker, e.g. neat
}
  • return: number

(2) Get one order by ticker and ID

  • method: get_order
  • args
{
  ticker: string;       // ticker, e.g. neat
  order_id: string;     // unique order id, U64
}
  • return: Order
Order {
  id: string;           // unique order id, U64
  ticker: string;       // ticker, e.g. neat
  seller: string;       // seller account id
  price: string;        // raw price, U256
  amount: string;       // raw amount, U128
  created_at: number;   // created timestamp, u64
  accepted_at?: number; // accepted timestamp, u64
}

(3) Get orders in one market by pagination

  • method: get_orders
  • args:
{
  ticker: string;       // ticker, e.g. neat
  from_index?: number;  // start index
  limit?: number;       // number of orders to return
}
  • return: Order[]

(4) Get the number of listed orders from one seller in a market

  • method: get_seller_order_num
  • args:
{
  ticker: string;       // ticker, e.g. neat
  seller: string;       // seller account id
}
  • return: number

(5) Get seller’s orders in one market by pagination

  • method: get_seller_orders
  • args:
{
  ticker: string;       // ticker, e.g. neat
  seller: string;       // seller account id
  from_index?: number;  // start index
  limit?: number;       // number of orders to return
}
  • return: Order[]

# Indexer

The marketplace indexer supports query markets, orders, sellers and activities.

# API URL

https://api.studio.thegraph.com/query/76896/nrc-20-marketplace/version/latest (opens new window)

# Markets

Query the first 1000 markets that have listed orders, ranked by total volume. Only the market that has ever listed orders will be returned.

  • skip: start from the nth market
  • first: the number of markets to return
{
  markets (
    first: 1000
    skip: 0
    orderBy: totalFilledValue
    orderDirection: desc
  ) {
    ticker
    totalFilledValue
    totalFilledAmount
    createdBlockTimestamp
  }
}

Example Response

{
  "data": {
    "markets": [
      {
        "createdBlockTimestamp": "1706077132794",
        "ticker": "1dragon",
        "totalFilledAmount": "49080475617345",
        "totalFilledValue": "2556922168832027000000000000"
      },
      {
        "createdBlockTimestamp": "1706245161957",
        "ticker": "ndao",
        "totalFilledAmount": "27128000000000",
        "totalFilledValue": "903867880000000000000000000"
      },
      {
        "createdBlockTimestamp": "1706077422678",
        "ticker": "neat",
        "totalFilledAmount": "179112345678",
        "totalFilledValue": "109433765424000000000000000"
      },
      ...
    ]
  }
}

# Orders

Query the first 1000 listed orders of NEAT, ranked by price in ascending order.

  • skip: start from the nth order
  • first: the number of orders to return
{
  orders (
    first: 1000,
    skip: 0,
    where: {
      ticker: "neat"
      status: Listed
    },
    orderBy: price,
    orderDirection: asc
  ) {
    orderId
    seller
    amount
    price
  }
}

Example Response

{
  "data": {
    "orders": [
      {
        "amount": "79000000000",
        "orderId": "1286",
        "price": "200000000000000000000000000000000000000",
        "seller": "f369b8952d3566ce2f9013ccf43cf29c0423aed7093934499da2ef6b3f7dbf83"
      },
      {
        "amount": "38900000000",
        "orderId": "1289",
        "price": "200000000000000000000000000000000000000",
        "seller": "df987f9c5298948efbc15a0c425f1a697a3499b133a62f8da5e9713fea7780db"
      },
      {
        "amount": "28800000000",
        "orderId": "1141",
        "price": "400000000000000000000000000000000000000",
        "seller": "eoou.near"
      },
      ...
    ]
  }
}

Query NEAT orders whose seller or buyer is eoou.near, ranked by updated timestamp in descending order.

  • skip: start from the nth order
  • first: the number of orders to return
{
  orders (
    first: 1000,
    skip: 0,
    where: {
      and: [
        { ticker: "neat" },
        { or: [
            { seller: "eoou.near"},
            { buyer: "eoou.near" }
          ]
        }
      ]
    },
    orderBy: updatedBlockTimestamp,
    orderDirection: desc
  ) {
    orderId
    seller
    buyer
    amount
    price
    status
    updatedBlockTimestamp
  }
}

Example Response

{
  "data": {
    "orders": [
      {
        "amount": "28800000000",
        "buyer": null,
        "orderId": "1141",
        "price": "400000000000000000000000000000000000000",
        "seller": "eoou.near",
        "status": "Listed",
        "updatedBlockTimestamp": "1710382927553"
      }
    ]
  }
}

# Sellers

Query the total listed amount by eoou.near of the first 1000 tokens in alphabetical order

{
  sellerInfos(
    first: 1000,
    where: { seller: "eoou.near"}
  ) {
    ticker
    totalListedAmount
  }
}

Example Response

{
  "data": {
    "sellerInfos": [
      {
        "ticker": "neat",
        "totalListedAmount": "28800000000"
      }
    ]
  }
}

# Activities

Query the the most recent 1,000 AcceptList, Cancel and Buy events of NEAT

  • skip: start from the nth activity
  • first: the number of activities to return

You may want to sort all the received events by block timestamp in descending order.

{
  acceptListEvents (
    first: 1000,
    where: { ticker: "neat"}
    orderBy: blockTimestamp
    orderDirection: desc
  ) {
    orderId
    seller
    ticker
    price
    amount
    blockTimestamp
  }
  cancelEvents (
    first: 1000,
    where: { ticker: "neat"}
    orderBy: blockTimestamp
    orderDirection: desc
  ) {
    orderId
    seller
    ticker
    price
    amount
    blockTimestamp
  }
  buyEvents (
    first: 1000,
    where: { ticker: "neat"}
    orderBy: blockTimestamp
    orderDirection: desc
  ) {
    orderId
    seller
    ticker
    price
    amount
    blockTimestamp
  }
}

Example Response

{
  "data": {
    "acceptListEvents": [
      {
        "amount": "1000000000",
        "blockTimestamp": "1718154074619",
        "orderId": "1292",
        "price": "10000000000000000000000000000000000000000",
        "seller": "617fcd1eed514342297e19b686664bc9a6b575821a0038e0b0c0b830db8f5527",
        "ticker": "neat"
      },
      {
        "amount": "505206735",
        "blockTimestamp": "1718088856323",
        "orderId": "1291",
        "price": "10000000000000000000000000000000000000000",
        "seller": "617fcd1eed514342297e19b686664bc9a6b575821a0038e0b0c0b830db8f5527",
        "ticker": "neat"
      },
      {
        "amount": "1000000000",
        "blockTimestamp": "1718088618567",
        "orderId": "1290",
        "price": "10000000000000000000000000000000000000000",
        "seller": "617fcd1eed514342297e19b686664bc9a6b575821a0038e0b0c0b830db8f5527",
        "ticker": "neat"
      },
      ...
    ]
  }
}