Matchstick — What it is and how to use it 🔥


Matchstick is a unit testing framework, developed by LimeChain, that enables subgraph developers to test their mapping logic in a sandboxed environment and deploy their subgraphs with confidence!

Intro 📌

What are we trying to solve here?

It all started back in February 2021 with this forum post by Dennison from, where he laid out the problems of, or lack thereof, unit testing in subgraphs. And more precisely — testing mapping functions in isolation.

Before Matchstick came along, the only way you could properly test your mapping logic was to deploy your subgraph, wait for it to sync, test by hand (sending queries) and then if something is off — edit your code and repeat the whole cycle from scratch. As you can imagine, this is very cumbersome to say the least. Especially if your subgraph is large, indexing can take several days. An even more insane case is the Uniswap v2 subgraph, which takes over a month to fully sync.

You can imagine that introducing even the slightest bug in the mappings will be a pain to catch with such a drawn-out feedback loop. And this gets exponentially worse as the complexity of the subgraph and the quantity of datasources increases.

That’s enough diving in the reasons for the need for a tool like Matchstick, if anyone is interested they can always check out the original post and the comments on it.

Modus Operandi 🧰

How are we trying to solve it?

Control flow

In order to create a proper testing environment, meaning as close to graph-node as possible, we simply made Matchstick proxy through graph-node, swapping certain implementations with mocks. The most important change to be noted here is that Matchstick doesn’t persist the state of the subgraph store in a PostgreSQL DB, but rather in an indexed in-memory hashmap. Hence all of the store methods (get, set, remove, etc.) are tweaked and now pointing to the hashmap, instead of a database. Same goes for the Ethereum RPC calls and a lot of other smaller mocked components.

Of course, we still needed something to help the subgraph interface with the mocked implementations instead of the real ones. That missing piece is matchstick-as, which is distributed as an npm module. It essentially glues Matchstick on top of the mapping code in real time while the tests are running.

TLDR — Matchstick is just a fancy mocked graph-node.

Installation 🚀

At the time of writing this article, Matchstick is distributed as a standalone binary, but it will be integrated in graph-cli in the near future.

Since the installation script differs depending on your OS and the current latest version of Matchstick, please follow this installation guide.

You will also need to install matchstick-as in your subgraph project, like this: yarn add matchstick-as

Writing our first unit test 👨‍💻

Let’s jump straight to the whole point of this article — how to use Matchstick to write concise and independent unit tests!

Say you have your mapping functions in src/mapping.ts, you can create a src/mapping.test.ts, but of course, it’s totally up to you how to structure it.

To keep this example as simple as possible, let’s assume we’re working with a single entity in our schema that looks like this:

type CustomEntity @entity {
id: ID!
name: String!

And a single event:

NewCustomEntity(uint256, string)

We also have the following illustrative handler function (in src/mapping.ts)

Now, let’s start writing the actual tests in the src/mapping.test.ts file. In the gist provided below, you can see an example test where we construct an instance of the NewCustomEntity event and push some parameters (id and name) to it. After that, we call handleNewCustomEntity() with the event we’ve just created.

After that, we check if the entity is properly saved to the store and if it has the parameters and values we expect it to have. We achieve that with the assert.fieldEquals() function, which is provided by matchstick-as. And finally, we call clearStore() to start off the next test on a clean slate.

A few other things from the example above that are worth mentioning:
1. The test blocks need to be wrapped with the runTests() function.
2. Asserting the state of the store works like this — we’re passing a unique combination of Entity type and id. Then we check a specific field on that Entity and assert that it has the value we expect it to have.

Congratulations, you’ve just written your first subgraph unit test! 🎉 But don’t get excited just yet — we still can’t run the tests. Finally, we need to add this import in our src/mapping.ts file:

export { runTests } from "../mapping.test.ts";

We’re not actually using it in that file, but it has to be imported there so that it can get picked up by Matchstick when we’re running the tests.

Now in order to run our tests we simply need to run the following:

graph build && ./matchstick MyDataSource

Where MyDataSource is the name of the datasource that you want to test (it doesn’t matter if it’s a template datasource or a normal one). After running the tests you should see something like this in the terminal:

Common test scenarios 🧪

Now that we’ve jumped over that hurdle, we can explore some common test scenarios and learn about more functionalities provided by Matchstick.

1. Add entities to the store without going through a handler.

Sometimes you don’t want to go through an event handler just to load an entity to the store. In that case you can just do:

2. Mocking Ethereum smart contract calls

When you want to mock what a smart contract function call would return and test how your mappings act in response to different function return values you can simply mock what the function returns:

You can also force the mocked function to revert:

3. Interacting with Event metadata

Sometimes your mapping logic may depend on event metadata. Using Matchstick, you can use default transaction metadata, which will exist on any mock event object, as long as it is created using the newMockEvent() function.

The following example shows how you can read/write to those fields on the Event object:

4. Asserting variable equality

Another common case is to want to assert if var X and var Y are equal. You can do that with all supported Value types:

Soon, there will be more helper methods included in the assert class, e.g. assert.addressEqual(a,b) rather than assert.equals(ethereum.Value.fromAddress(a)),ethereum.Value.fromAddress(b));

5. Logging

Logging is not so different from graph-node, here are examples of using all non-critical log types:

Running those examples will print the following:

You could also simulate a critical failure, like so:

test("Blow everything up", () => {

Which will result in this error message:

6. Test duration

It’s very informative to know when your mapping logic is taking an exceptionally long time to execute. That’s why, as you’ve probably already noticed, we print out the test run time duration at the end of the output, e.g.

Jul 09 14:54:42.420 INFO Program execution time: 10.06022ms

Next steps 🎯

The Matchstick framework is currently live for beta testing. There is a lot of room for improvements to everything we’ve talked about above. We’re trying to gather as much feedback from subgraph developers as we can, to understand how we can solve the problems they face when building subgraphs, as well as how we can make the overall testing process as smooth and streamlined as possible.

There’s a GitHub project board where we keep track of day-to-day work which you can check out here.

Further reading and video tutotial 📚

If you liked this quick intro to Matchstick and want to learn more, please check out the official docs and check out full examples for everything we covered here & more in our demo subgraph. Enjoy the video below:

Feedback & Contact📝

If you have any questions, feedback, feature requests or just want to reach out, the best place would be The Graph Discord where we have a dedicated channel for Matchstick, called 🔥| matchstick-early-testers (name might very well change in the near future 😅). You can also open up an Issue on our issues page.

That’s enough for this one, happy hacking!