# 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:
- list order
- cancel order
- 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:
- The order price of a given token is calculated as
price_multiplier * price * (10 ^ near_decimals) / (10 ^ token_decimals)
. The price multiplier is10 ^ 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 - A group of two functions, one
list
function and oneinscribe
transfer function, needs to be signed and called in a single approval in wallet. Theinscribe
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:
- The order value can be calculated as
raw_price * token_amount * (10 ^ token_decimals) / price_multiplier
. The price multiplier is10 ^ 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. - 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"
},
...
]
}
}