You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.
This is a proposal for a new GraphQL API for Mesh which will replace the existing JSON-RPC API in Mesh version 10.0.0. The JSON-RPC API will be removed in Mesh v10 and users who upgrade to v10 will need to use the new GraphQL API.
Background and Motivation
There are several motivating factors for why we are introducing a new API:
Improve Queryability of Mesh
Currently, Mesh does not offer much in the way of querying or filtering orders in its JSON-RPC API. Part of the reason for this is that JSON-RPC does not lend itself well to complicated queries.
Because of this, if you need to query orders, you will need to follow a somewhat complicated process for syncing Mesh with your own database and then running queries from there. Practically speaking, there is no other way to get only the orders you care about and make sure they are up-to-date. This can create an unnecessary burden for developers, especially in the browser where there are limited database options.
The new API will make it possible for many use cases to avoid syncing with their own database entirely. Instead, Mesh can be thought of as the database for orders. For example, you can query Mesh directly to get all ETH-DAI orders that expire no sooner than 60 seconds from now while filtering out dust orders.
Some users may still wish to sync their database (for example, if they want to do SQL joins with application-specific data), but it is an option rather than a requirement. Even for users that do want to sync their own database, improved querying capabilities at the API layer can still make it easier to do so by filtering out orders you don't care about earlier on in the pipeline.
Unify and Simplify our Tooling
In the current 0x tech stack, there are many different APIs and libraries that effectively do the same thing: get a set of orders that can be filled by anyone.
All of these APIs and tools work differently (sometimes in subtle ways) and generally one cannot be used as a drop in replacement for the other. Some of them are compatible with one another and others are not. For example, @0x/orderbook works with SRA or the Mesh JSON-RPC API but doesn't work when running Mesh in the browser.
One of the goals for the new GraphQL API is to provide a single API for getting orders whether you are talking to 0x API, running your own Mesh node, or even running Mesh in the browser.
We are still in the process of planning the timeline for this transition. One thing that's clear is that SRA will not disappear right away. A lot of users and bots rely on SRA, so our goal is to keep it up and running for the near future, although it may be marked as deprecated. In addition, any endpoints in 0x API not related to SRA (e.g. /swap and /meta_transaction) will be unaffected.
Better Documentation and Support for More Languages
The documentation for the existing JSON-RPC API is hand-written and must be manually updated whenever we make changes. Writing clients for new languages is also a largely manual process. While JSON-RPC is a widely accepted standard, it is not as structured or type safe as something like GraphQL. Additionally, Mesh relies on the subscriptions feature of JSON-RPC 2.0 which is not universally supported by clients.
Why GraphQL?
GraphQL was selected as a standard for the new API based on several important characteristics:
Transport layer agnostic (e.g. HTTP, WebSockets, or calling a function).
Type-safe, well-structured schemas.
Wide support across many programming languages.
Great tooling (including automatic doc generation and playground environments).
Built-in support for subscriptions.
Ability to only receive the fields that you need.
Playground Environment
We have deployed a playground environment which you can use to familiarize yourself with the new API. It supports autocompletion, syntax highlighting, subscriptions, and inline documentation. You can access the playground at https://meshmock.spaceship.0x.org/. See the Example Queries section below for some queries to try.
Some caveats to keep in mind:
The playground uses mock data which is structurally correct (i.e. all the fields have the correct type) but may be semantically incorrect (e.g. orders may have the wrong signature).
Some fields are not sorted in the correct order. For example, BigNumber fields are sorted alphanumerically instead of numerically. This also affects the results from filters that depend on ordering such as GREATER or LESS. This will not be the case in the final production API.
Subscriptions are supported but can cause issues with scrolling in the playground. Refreshing the page usually fixes this.
GraphQL Schema
Here is the proposed GraphQL schema in its entirety:
"""A 32-byte Keccak256 hash encoded as a hexadecimal string."""scalarHash"""An Ethereum address encoded as a hexadecimal string."""scalarAddress"""A BigNumber or uint256 value encoded as a numerical string."""scalarBigNumber"""An array of arbitrary bytes encoded as a hexadecimal string."""scalarBytes"""A time encoded as a string using the RFC3339 standard."""scalarTimestamp"""An arbitrary set of key-value pairs. Encoded as a JSON object."""scalarObject"""A signed 0x order according to the [protocol specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#order-message-format.)"""typeOrder {
chainId: Int!exchangeAddress: Address!makerAddress: Address!makerAssetData: Bytes!makerAssetAmount: BigNumber!makerFeeAssetData: Bytes!makerFee: BigNumber!takerAddress: Address!takerAssetData: Bytes!takerAssetAmount: BigNumber!takerFeeAssetData: Bytes!takerFee: BigNumber!senderAddress: Address!feeRecipientAddress: Address!expirationTimeSeconds: BigNumber!salt: BigNumber!signature: Bytes!
}
"""A signed 0x order along with some additional metadata about the order which is not part of the 0x protocol specification."""typeOrderWithMetadata {
chainId: Int!exchangeAddress: Address!makerAddress: Address!makerAssetData: Bytes!makerAssetAmount: BigNumber!makerFeeAssetData: Bytes!makerFee: BigNumber!takerAddress: Address!takerAssetData: Bytes!takerAssetAmount: BigNumber!takerFeeAssetData: Bytes!takerFee: BigNumber!senderAddress: Address!feeRecipientAddress: Address!expirationTimeSeconds: BigNumber!salt: BigNumber!signature: Bytes!""" The hash, which can be used to uniquely identify an order. """hash: Hash!""" The remaining amount of the maker asset which has not yet been filled. """remainingFillableTakerAssetAmount: BigNumber!
}
"""An enum containing all the order fields for which filters and/or sorting is supported."""enumOrderField {
hash chainId exchangeAddress makerAddress makerAssetData makerAssetAmount makerFeeAssetData makerFee takerAddress takerAssetData takerAssetAmount takerFeeAssetData takerFee senderAddress feeRecipientAddress expirationTimeSeconds salt remainingFillableTakerAssetAmount
}
"""The kind of comparison to be used in a filter."""enumFilterKind {
EQUAL NOT_EQUAL GREATER GREATER_OR_EQUAL LESS LESS_OR_EQUAL
}
"""The direction to sort in. Ascending means lowest to highest. Descending means highest to lowest."""enumSortDirection {
ASC DESC
}
"""The value to filter with. Must be the same type as the field you are filtering by."""scalarFilterValue"""A filter on orders. Can be used in queries to only return orders that meet certain criteria."""inputOrderFilter {
field: OrderField!kind: FilterKind!value: FilterValue!
}
"""A sort ordering for orders. Can be used in queries to control the order in which results are returned."""inputOrderSort {
field: OrderField!direction: SortDirection!
}
"""The block number and block hash for the latest block that has been processed by Mesh."""typeLatestBlock {
number: BigNumber!hash: Hash!
}
"""Contains configuration options and various stats for Mesh."""typeStats {
version: String!pubSubTopic: String!rendezvous: String!peerID: String!ethereumChainID: Int!latestBlock: LatestBlocknumPeers: Int!numOrders: Int!numOrdersIncludingRemoved: Int!startOfCurrentUTCDay: Timestamp!ethRPCRequestsSentInCurrentUTCDay: Int!ethRPCRateLimitExpiredRequests: Int!maxExpirationTime: BigNumber!
}
typeQuery {
""" Returns the order with the specified hash, or null if no order is found with that hash. """order(hash: Hash!): OrderWithMetadata""" Returns an array of orders that satisfy certain criteria. """orders(
""" Determines the order of the results. If more than one sort option is provided, results we be sorted by the first option first, then by any subsequent options. By default, orders are sorted by hash in ascending order. """sort: [OrderSort!] = [{ field: hash, direction: ASC }]
""" A set of filters. Only the orders that match all filters will be included in the results. By default no filters are used. """filters: [OrderFilter!] = []
""" The maximum number of orders to be included in the results. Defaults to 20. """limit: Int = 20
): [OrderWithMetadata!]!""" Returns the current stats. """stats: Stats!
}
"""A signed 0x order according to the [protocol specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#order-message-format)."""inputNewOrder {
chainId: Int!exchangeAddress: Address!makerAddress: Address!makerAssetData: Bytes!makerAssetAmount: BigNumber!makerFeeAssetData: Bytes!makerFee: BigNumber!takerAddress: Address!takerAssetData: Bytes!takerAssetAmount: BigNumber!takerFeeAssetData: Bytes!takerFee: BigNumber!senderAddress: Address!feeRecipientAddress: Address!expirationTimeSeconds: BigNumber!salt: BigNumber!signature: Bytes!
}
"""The results of the addOrders mutation. Includes which orders were accepted and which orders where rejected."""typeAddOrdersResults {
""" The set of orders that were accepted. Accepted orders will be watched and order events will be emitted if their status changes. """accepted: [AcceptedOrderResult!]!""" The set of orders that were rejected, including the reason they were rejected. Rejected orders will not be watched. """rejected: [RejectedOrderResult!]!
}
typeAcceptedOrderResult {
""" The order that was accepted, including metadata. """order: OrderWithMetadata!""" Whether or not the order is new. Set to true if this is the first time this Mesh node has accepted the order and false otherwise. """isNew: Boolean!
}
typeRejectedOrderResult {
""" The hash of the order. May be null if the hash could not be computed. """hash: Hash""" The order that was rejected. """order: Order!""" A machine-readable code indicating why the order was rejected. This code is designed to be used by programs and applications and will never change without breaking backwards-compatibility. """code: RejectedOrderCode!""" A human-readable message indicating why the order was rejected. This message may change in future releases and is not covered by backwards-compatibility guarantees. """message: String!
}
"""A set of all possible codes included in RejectedOrderResult. Note that more codes will be addedto the final spec. See the [current Mesh docs](https://godoc.org/github.com/0xProject/0x-mesh/zeroex/ordervalidator#pkg-variables)for a list of all codes currently in use."""enumRejectedOrderCode {
ETH_RPC_REQUEST_FAILED INVALID_MAKER_ASSET_AMOUNT INVALID_TAKER_ASSET_AMOUNT
}
typeMutation {
""" Adds one or more orders to Mesh. """addOrders(orders: [NewOrder!]!, pinned: Boolean = true): AddOrdersResults!
}
typeOrderEvent {
""" The order that was affected. """order: OrderWithMetadata!""" A way of classifying the effect that the order event had on the order. You can think of different end states as different "types" of order events. """endState: OrderEndState!""" The timestamp for the order event, which can be used for bookkeeping purposes. If the order event was generated as a direct result of on-chain events (e.g., FILLED, UNFUNDED, CANCELED), then it is set to the latest block timestamp at which the order was re-validated. Otherwise (e.g., for ADDED, STOPPED_WATCHING), the timestamp corresponds when the event was generated on the server side. """timestamp: Timestamp!""" Contains all the contract events that triggered the order to be re-validated. All events that _may_ have affected the state of the order are included here. It is gauranteed that at least one of the events included here will have affected the order's state, but there may also be some false positives. """contractEvents: [ContractEvent!]!
}
enumOrderEndState {
""" The order was successfully validated and added to the Mesh node. The order is now being watched and any changes to the fillability will result in subsequent order events. """ ADDED""" The order was filled for a partial amount. The order is still fillable up to the remainingFillableTakerAssetAmount. """ FILLED""" The order was fully filled and its remaining fillableTakerAssetAmount is 0. The order is no longer fillable. """ FULLY_FILLED""" The order was cancelled and is no longer fillable. """ CANCELLED""" The order expired and is no longer fillable. """ EXPIRED""" The order was previously expired, but due to a block re-org it is no longer considered expired (should be rare). """ UNEXPIRED""" The order has become unfunded and is no longer fillable. This can happen if the maker makes a transfer or changes their allowance. """ UNFUNDED""" The fillability of the order has increased. This can happen if a previously processed fill event gets reverted due to a block re-org, or if a maker makes a transfer or changes their allowance. """ FILLABILITY_INCREASED""" The order is potentially still valid but was removed for a different reason (e.g. the database is full or the peer that sent the order was misbehaving). The order will no longer be watched and no further events for this order will be emitted. In some cases, the order may be re-added in the future. """ STOPPED_WATCHING
}
"""An on-chain contract event."""typeContractEvent {
""" The hash of the block where the event was generated. """blockHash: Hash!""" The hash of the transaction where the event was generated. """txHash: Hash!""" The index of the transaction where the event was generated. """txIndex: Int!""" The index of the event log. """logIndex: Int!""" True when this was an event that was removed due to a block-reorg. False otherwise. """isRemoved: Boolean!""" The address of the contract that generated the event. """address: Address!""" The kind of event (e.g. "ERC20TransferEvent"). """kind: String!""" The parameters for the event. The parameters are different for each event kind. """parameters: Object!
}
typeSubscription {
""" Subscribe to all order events. Events are emitted whenever the status of a watched order changes. """orderEvents: [OrderEvent!]!
}
Example Queries
This section includes some example queries which you can copy and paste in the playground. Of course, you would typically write queries programmatically with a GraphQL client, not by manually writing them. This is just for illustrative purposes.
Getting a Specific Order
You can get the details for any order by its hash:
The orders query supports many different options. Here's an example of how to get orders with a minimum expirationTimeSeconds and minimum remainingFillableAssetAmount. You can use this to exclude dust orders and orders which may expire too soon.
You can add orders using a mutation. Note that in the playground orders will not actually be added and will not show up in subsequent queries. Also in the playground every other order will be rejected in order to provide an example of what the rejected field in the response looks like.
We recommend paginating through orders by using filters and limit. So for example, if you want to sort orders by their hash (which is the default), you first send a query without any filters:
The orders in the response will be sorted by hash (which is the default). Look at the last order you received, which in this case has a hash of 0x75d2b56b11f21235ec8faec8be9d081090678cf62f5c69fa118236d829424719. Send the next request by using the last hash you received in a filter:
This will return any orders with a hash greater than 0x75d2b56b11f21235ec8faec8be9d081090678cf62f5c69fa118236d829424719. Repeat this process, changing the hash each time, until there are no orders left.
There may be orders added or removed while you are in the process of paginating. Following this method guarantees that:
No order will be included more than once.
Any order which was present at the start of pagination and at the end of pagination will be included.
Any order which was added or removed after pagination started may or may not be included.
Query Fragments
GraphQL requires you to specify all the fields that you want included in the response. However, you can use query fragments to avoid repeating the same fields over and over. Here's an example of a query fragment that includes all the fields of an order.
The API described above is what we are planning to ship with Mesh version 10.0.0. Our goal is to ship with a minimally viable API that improves the experience of interacting with Mesh for common use cases. However, this will not be the final state of the API and we are planning to follow up with a few specific improvements. Because of the way GraphQL requires specifying the fields you want to receive in the response, we can add new fields (e.g. to the OrderWithMetadata type) without increasing payload sizes for users who don't need those fields. These improvements will be backwards compatible.
Add a price field to OrderWithMetadata. This will allow sorting orders by price or using a filter to only return orders with a certain price or better.
Add parsed asset data fields to OrderWithMetadata. This could allow you to more easily e.g. only receive ERC20 orders or find all orders for a specific ERC721 contract address with any token ID. The variety of asset datas and the existence of the MultiAssetDataProxy make it challenging to structure the data in an efficient way and expose a querying interface that is easy to use. That said, it should be possible and we know this is something users have been asking for.
Support filters in the orderEvents subscription. The current subscription returns all order events, since it is much easier to support on the backend. It should be possible, though somewhat challenging, to allow filtering similar to the orders query.
If you have any other ideas for improvements or new features, let us know 😄
The text was updated successfully, but these errors were encountered:
0x Mesh GraphQL API Specification
This is a proposal for a new GraphQL API for Mesh which will replace the existing JSON-RPC API in Mesh version 10.0.0. The JSON-RPC API will be removed in Mesh v10 and users who upgrade to v10 will need to use the new GraphQL API.
Background and Motivation
There are several motivating factors for why we are introducing a new API:
Improve Queryability of Mesh
Currently, Mesh does not offer much in the way of querying or filtering orders in its JSON-RPC API. Part of the reason for this is that JSON-RPC does not lend itself well to complicated queries.
Because of this, if you need to query orders, you will need to follow a somewhat complicated process for syncing Mesh with your own database and then running queries from there. Practically speaking, there is no other way to get only the orders you care about and make sure they are up-to-date. This can create an unnecessary burden for developers, especially in the browser where there are limited database options.
The new API will make it possible for many use cases to avoid syncing with their own database entirely. Instead, Mesh can be thought of as the database for orders. For example, you can query Mesh directly to get all ETH-DAI orders that expire no sooner than 60 seconds from now while filtering out dust orders.
Some users may still wish to sync their database (for example, if they want to do SQL joins with application-specific data), but it is an option rather than a requirement. Even for users that do want to sync their own database, improved querying capabilities at the API layer can still make it easier to do so by filtering out orders you don't care about earlier on in the pipeline.
Unify and Simplify our Tooling
In the current 0x tech stack, there are many different APIs and libraries that effectively do the same thing: get a set of orders that can be filled by anyone.
@0x/mesh-browser
@0x/mesh-rpc-client
@0x/connect
@0x/orderbook
All of these APIs and tools work differently (sometimes in subtle ways) and generally one cannot be used as a drop in replacement for the other. Some of them are compatible with one another and others are not. For example,
@0x/orderbook
works with SRA or the Mesh JSON-RPC API but doesn't work when running Mesh in the browser.One of the goals for the new GraphQL API is to provide a single API for getting orders whether you are talking to 0x API, running your own Mesh node, or even running Mesh in the browser.
We are still in the process of planning the timeline for this transition. One thing that's clear is that SRA will not disappear right away. A lot of users and bots rely on SRA, so our goal is to keep it up and running for the near future, although it may be marked as deprecated. In addition, any endpoints in 0x API not related to SRA (e.g.
/swap
and/meta_transaction
) will be unaffected.Better Documentation and Support for More Languages
The documentation for the existing JSON-RPC API is hand-written and must be manually updated whenever we make changes. Writing clients for new languages is also a largely manual process. While JSON-RPC is a widely accepted standard, it is not as structured or type safe as something like GraphQL. Additionally, Mesh relies on the subscriptions feature of JSON-RPC 2.0 which is not universally supported by clients.
Why GraphQL?
GraphQL was selected as a standard for the new API based on several important characteristics:
Playground Environment
We have deployed a playground environment which you can use to familiarize yourself with the new API. It supports autocompletion, syntax highlighting, subscriptions, and inline documentation. You can access the playground at https://meshmock.spaceship.0x.org/. See the Example Queries section below for some queries to try.
Some caveats to keep in mind:
BigNumber
fields are sorted alphanumerically instead of numerically. This also affects the results from filters that depend on ordering such asGREATER
orLESS
. This will not be the case in the final production API.GraphQL Schema
Here is the proposed GraphQL schema in its entirety:
Example Queries
This section includes some example queries which you can copy and paste in the playground. Of course, you would typically write queries programmatically with a GraphQL client, not by manually writing them. This is just for illustrative purposes.
Getting a Specific Order
You can get the details for any order by its hash:
Querying and Filtering Orders
You can get all orders via the
orders
query. By default, it will return up to 20 orders at a time sorted by their hash.The
orders
query supports many different options. Here's an example of how to get orders with a minimumexpirationTimeSeconds
and minimumremainingFillableAssetAmount
. You can use this to exclude dust orders and orders which may expire too soon.Here's an example of sorting orders by the
remainingFillableAssetAmount
. You can combine any numberof filters and sorts in an
orders
query.Adding Orders
You can add orders using a
mutation
. Note that in the playground orders will not actually be added and will not show up in subsequent queries. Also in the playground every other order will be rejected in order to provide an example of what therejected
field in the response looks like.Subscribing to Order Events
You can subscribe to order events via a
subscription
. The playground will send a mock order event every second.Getting Stats
You can get some stats about your Mesh node via the
stats
query.Pagination
We recommend paginating through orders by using
filters
andlimit
. So for example, if you want to sort orders by their hash (which is the default), you first send a query without any filters:The orders in the response will be sorted by
hash
(which is the default). Look at the last order you received, which in this case has a hash of0x75d2b56b11f21235ec8faec8be9d081090678cf62f5c69fa118236d829424719
. Send the next request by using the last hash you received in a filter:This will return any orders with a hash greater than
0x75d2b56b11f21235ec8faec8be9d081090678cf62f5c69fa118236d829424719
. Repeat this process, changing thehash
each time, until there are no orders left.There may be orders added or removed while you are in the process of paginating. Following this method guarantees that:
Query Fragments
GraphQL requires you to specify all the fields that you want included in the response. However, you can use query fragments to avoid repeating the same fields over and over. Here's an example of a query fragment that includes all the fields of an order.
Future Improvements
The API described above is what we are planning to ship with Mesh version 10.0.0. Our goal is to ship with a minimally viable API that improves the experience of interacting with Mesh for common use cases. However, this will not be the final state of the API and we are planning to follow up with a few specific improvements. Because of the way GraphQL requires specifying the fields you want to receive in the response, we can add new fields (e.g. to the
OrderWithMetadata
type) without increasing payload sizes for users who don't need those fields. These improvements will be backwards compatible.price
field toOrderWithMetadata
. This will allow sorting orders by price or using a filter to only return orders with a certain price or better.OrderWithMetadata
. This could allow you to more easily e.g. only receive ERC20 orders or find all orders for a specific ERC721 contract address with any token ID. The variety of asset datas and the existence of the MultiAssetDataProxy make it challenging to structure the data in an efficient way and expose a querying interface that is easy to use. That said, it should be possible and we know this is something users have been asking for.orderEvents
subscription. The current subscription returns all order events, since it is much easier to support on the backend. It should be possible, though somewhat challenging, to allow filtering similar to theorders
query.If you have any other ideas for improvements or new features, let us know 😄
The text was updated successfully, but these errors were encountered: