Provably fair charts
Synthetic charts on Catapult are provably fair: the whole price path of a token is generated and locked by a public hash before anyone can trade it, it is not adjusted while the token is live, and anyone can check it against the hash after the token ends.
This applies to Synthetic Markets. 10000x Futures follows real exchange prices and has no generated chart.
How it works
The scheme is a commit and reveal: Catapult publishes a fingerprint of the chart first and the chart itself last.
- Commit, at creation. When a token is created, every price tick of its lifetime is computed up front. Catapult then draws a secret salt (16 random bytes, written as 32 hex characters) and publishes the hash of the ticks, the salt and the token's speed value. The hash exists before the first trade.
- Trading. The token's prices are the committed ticks, in order. Every trader on the token trades the same path. The hash and the speed value are public the whole time; the salt and the ticks stay hidden, so the hash tells nobody where the price goes next.
- Reveal, at the end. When the token's lifetime is over, the salt and the full list of ticks become public. Anyone can recompute the hash and compare it with the one published at creation.
A hash changes completely when any input changes by one character. If a single tick, the salt or the speed value were different from what was committed, the recomputed hash would not match. That is why the path cannot be changed once the hash is published.
What is public, and when
| Data | While the token is live | After the token ends |
|---|---|---|
| Hash | Public | Public |
| Speed value | Public | Public |
| Salt | Hidden | Public |
| Ticks (every price of the path) | Hidden | Public |
The formula
- ticks is every price of the path, in order, joined by commas with no spaces:
1,1.0213,0.9874. - salt is the 32-character hex string revealed at the end.
- speed is the speed value of the token's mode, written as a number. The three parts are joined as text with nothing between them.
- hashMessage is the standard Ethereum signed-message hash (EIP-191): Keccak-256 of the prefix
\x19Ethereum Signed Message:\n, the message length in bytes, and the message.hashMessagein ethers v6 and in viem both compute it.
The speed value is the number of price ticks per second for the token's speed mode:
| Speed mode | Speed value |
|---|---|
| Slow | 6 |
| Fast | 24 |
| Flash | 96 |
| Crack | 480 |
| Mayhem | 1440 |
Verify a chart yourself
In the app, the Audited by Hashlock, Halborn badge next to a token's countdown opens its Provably Fair window. It shows the hash at any time. Once the token has ended, it also shows the salt and the ticks, each with a copy button, and a Verify Fairness button that recomputes the hash in your browser and says whether it matches. After the end, a Chart Fairness card on the token page opens the same window. The Provably Fair page, linked in the app menu and footer, does it for any token ID: catapult.trade/synthetic/provably-fair.
To check without trusting the app's own button:
- Open an ended token's Provably Fair window and copy the hash, the salt and the ticks. Use the copy button for the ticks: the window adds a space after each comma for reading, and the hash input has none.
- Find the speed value for the token's mode in the table above.
- Run the script below with Node.js 18 or later, after
npm install ethers@6. - Compare the printed hash with the published one. The same hash means the ticks you copied are the path committed when the token was created.
// verify.mjs: run with `node verify.mjs`
import { hashMessage } from 'ethers';
const ticks = '1,1.0213,0.9874,1.0452'; // paste the copied ticks
const salt = '9f2c4e81a07b3d56e1c8f09a4b2d7e63'; // paste the copied salt
const speed = 24; // the speed value of the token's mode
const published = '0xadf5c3d12134b236e66b07d8e16fcfd45e801fbfb1696fadcc6c49a1869b4c9d'; // paste the hash
const hash = hashMessage(ticks + salt + speed);
console.log(hash);
console.log(hash === published ? 'Match: the chart is the committed one' : 'No match');
You can also compare the revealed ticks with the chart you watched and with the prices of your own trades on that token.
Worked example
A real token has thousands of ticks. This example uses four, a made-up salt and the Fast speed value, so you can run it as is:
- ticks:
1,1.0213,0.9874,1.0452 - salt:
9f2c4e81a07b3d56e1c8f09a4b2d7e63 - speed:
24
The message is 1,1.0213,0.9874,1.04529f2c4e81a07b3d56e1c8f09a4b2d7e6324, and hashMessage of it is:
0xadf5c3d12134b236e66b07d8e16fcfd45e801fbfb1696fadcc6c49a1869b4c9d
Change the last tick from 1.0452 to 1.0453, one digit, and the hash becomes:
0x54adb0b5157517b71daf3a48c47006e789507ec55d4fc25930e6bb6c08350204
Nothing in it resembles the first hash, so a path that differs by one tick from the committed one cannot pass the check.
Security audits
Two security firms audited Catapult. The reports are public:
| Auditor | Report |
|---|---|
| Hashlock | hashlock.com/audits/catapult |
| Halborn | halborn.com/audits/catapult-trade |
The badges of both auditors are in the Provably Fair window in the app and link to the same reports.
FAQ
Is a chart ever changed after its token starts? No. The hash of the full path is published when the token is created. A changed tick would give a different hash, and the check after the end would fail.
Do all traders see the same chart? Yes. A token has one committed path, and every trader on it trades that path.
Why can I only verify after the token ends? The salt and the ticks are revealed at the end. Before that the window shows the hash, so the commitment is already public but the path is not.
Do I need an account to check a token? No. The hash, the salt and the ticks are public, and the check runs on your own device.
What if the hashes do not match? First check that you copied the ticks with the copy button (no spaces) and used the speed value of the token's mode. If they still differ, contact support in the in-app chat with the token ID.