Provably Fair
Randomness provided by
This page is for developers and users who want to understand, inspect, and independently verify the fairness model used by GalaxyGrails.io.
It complements the high-level overview in the main GalaxyGrails page with concrete API details and example code.
Overview
GalaxyGrails.io uses a combination of:
- Chainlink VRF (Verifiable Random Function) for on-chain randomness
- A pre-committed deck snapshot and SHA-256 deck hash
- A deterministic fairness algorithm that uses the VRF seed to drive card selection
The key idea:
Once an event starts, the outcomes are fully determined by the deck snapshot, the VRF seed, and the fairness algorithm. No one (including GalaxyGrails) can change the result afterwards without detection.
This guide walks through the public fairness endpoint and shows how to:
- Fetch fairness data for a completed event
- Recompute the deck hash from the snapshot
- Use the VRF seed to drive a deterministic RNG
- Align that RNG with the published pull timeline
In addition to the API, you can browse completed events and their fairness reports in the UI at https://galaxygrails.io/events.
For more details on how Chainlink VRF itself works under the hood, see the official Chainlink VRF documentation.
Fairness Algorithm
At a high level, each GalaxyGrails pack event follows this pattern:
- Fix the deck – Before sales start, the full list of cards for the event is locked into a snapshot and hashed (the
deck_hash). - Fix the randomness – Chainlink VRF generates a single, verifiable random seed (
random_seed) for that event. - Derive a sequence – A versioned fairness algorithm (see
fairness_algo_version) combines the deck snapshot and seed to deterministically decide the order in which cards are pulled. - Reveal over time – As packs are opened, the system walks that pre-determined sequence and reveals one card at a time.
Because the deck snapshot, deck hash, VRF seed, and pulls are all exposed via the fairness API once the event ends, anyone can:
- Rebuild the same initial deck state.
- Start from the same seed.
- Implement the same deterministic selection rules.
- Confirm that the resulting pull order matches the published
pullstimeline.
That is what makes the system provably fair rather than just procedurally random.
Public Fairness Endpoint
URL
GET https://galaxygrails.io/api/pack-events/{PACK_EVENT_ID}/fairness
- Authentication: Not required (public)
- Path params:
PACK_EVENT_ID– UUID of the pack event (e.g.11655b60-f6cc-40a8-b8a8-0282c4e5d9f1)
Behaviour: Active vs Completed Events
The endpoint behaves differently depending on whether the event is still live:
-
While the event is active (
is_active = true):- The response includes VRF metadata and the deck hash, but hides:
- The actual
random_seed - The full
deck_snapshot - The full
pullsarray
- The actual
- This prevents leaking information about future pulls while still letting users verify the on-chain commitments.
- The response includes VRF metadata and the deck hash, but hides:
-
After the event ends (
is_active = false):- The response includes the full fairness payload:
random_seedrandom_seed_sourcedeck_snapshotpulls(ordered timeline)
- The response includes the full fairness payload:
The rest of the fields (event name, price, deck hash, VRF metadata) are always present.
Example Response (Completed Event)
{
"pack_event_id": "11655b60-f6cc-40a8-b8a8-0282c4e5d9f1",
"alpha": 1.0,
"pack_price": "200.00",
"stop_ev_buffer": 0.99,
"random_seed": "6a5f...c9d3", // VRF seed (hex, no 0x prefix)
"random_seed_source": "vrf",
"deck_hash": "f712...3b8e", // SHA-256 hash of deck_snapshot
"deck_snapshot": [
{
"pack_card_id": "...",
"card_inventory_id": "...",
"initial_price": "220.00"
}
// ... one entry per card in the event deck
],
"fairness_algo_version": "v1",
"pulls": [
{
"pull_id": "...",
"sequence_index": 1,
"user_id": "...",
"username": "collector123",
"pack_card_id": "...",
"card_token_id": "...",
"card": {
"id": "...",
"title": "2021 POKEMON ...",
"front_image_url": "https://...",
"back_image_url": "https://...",
"market_price": "250.00"
},
"pulled_at": "2025-11-24T03:12:45.123456+00:00"
}
// ... one entry per pull, ordered by sequence_index
],
"is_active": false,
"vrf_event_id": "0x...", // Event ID used in the VRF consumer contract
"vrf_request_id": "0x...", // Transaction hash that requested randomness
"vrf_consumer_address": "0x...", // VRF consumer contract address
"vrf_network": "polygon-mainnet" // Network label
}
Note: Field names and shapes are stable, but example values above are illustrative.
Key Fields Explained
alpha: Controls the weighting curve for card selection based on market value. Higher alpha = stronger preference for lower-value cards.stop_ev_buffer: The threshold (as a ratio of pack price) below which the event auto-stops to maintain fairness.fairness_algo_version: Version identifier for the selection algorithm. Currentlyv1.
Verifying the Deck Commitment
The deck_hash is a SHA-256 hash of the serialized deck_snapshot. You can recompute it as follows:
import hashlib
import json
# Given a fairness API response in `data` (e.g. from resp.json())
deck_snapshot = data["deck_snapshot"]
serialized = json.dumps(deck_snapshot, sort_keys=True, separators=(",", ":"))
local_deck_hash = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
assert local_deck_hash == data["deck_hash"], "Deck hash mismatch!"
If this assertion passes, you know the deck used for the event is exactly the one that was committed via deck_hash at event creation.
Verifying VRF-Based Randomness
The random_seed field is the output of Chainlink VRF for that event, represented as a hex string without the 0x prefix.
A simple way to use it is to seed a local PRNG and walk through a deterministic sequence of random values:
import random
seed_hex = data["random_seed"]
rng = random.Random()
rng.seed(int(seed_hex, 16))
# Example: get the first few random numbers
for i in range(5):
print(i + 1, rng.random())
GalaxyGrails.io uses this seed (plus the deck snapshot and a versioned fairness algorithm) to drive card selection for each pull.
If you build your own deck model and fairness function, you can:
- Start from the same
deck_snapshotandrandom_seed. - Apply your own deterministic selection logic per pull.
- Compare the resulting sequence of cards to the published
pullstimeline.
As long as you implement the same fairness algorithm and state updates, you should reproduce the exact same pull order.
Full End-to-End Verification Example
The UI includes a compact Python example, but here is a more explicit script you can adapt. It:
- Fetches fairness data for a completed event.
- Recomputes the deck hash and asserts it matches.
- Seeds a RNG from the VRF output.
- Prints the random sequence alongside each pulled card.
import hashlib
import json
import random
import requests
# 1) Configure your event ID
EVENT_ID = "11655b60-f6cc-40a8-b8a8-0282c4e5d9f1" # Replace with your event ID
# 2) Fetch fairness JSON
resp = requests.get(f"https://galaxygrails.io/api/pack-events/{EVENT_ID}/fairness")
resp.raise_for_status()
# This endpoint returns the fairness payload as the top-level JSON object
# (no nested {"data": ...} envelope)
data = resp.json()
# 3) Verify deck hash
deck_snapshot = data["deck_snapshot"]
serialized = json.dumps(deck_snapshot, sort_keys=True, separators=(",", ":"))
local_deck_hash = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
assert local_deck_hash == data["deck_hash"], "Deck hash mismatch!"
print("Deck hash verified.")
# 4) Seed PRNG from Chainlink VRF seed
seed_hex = data["random_seed"]
rng = random.Random()
rng.seed(int(seed_hex, 16))
pulls = data["pulls"]
# 5) Walk through pulls with a deterministic RNG stream
for i, pull in enumerate(pulls, start=1):
r = rng.random()
card_title = None
card = pull.get("card") or {}
if isinstance(card, dict):
card_title = card.get("title")
print(i, r, card_title)
# At this point, you can plug `r` into your own fairness logic
# and compare the resulting card selection to `pull["card"]`.
Tip: This script is intentionally conservative and does not depend on any GalaxyGrails-specific client libraries. It only uses
requests,hashlib,json, and Python's standardrandommodule.
Summary
- Use
GET https://galaxygrails.io/api/pack-events/{PACK_EVENT_ID}/fairnessto fetch fairness data. - Use
deck_snapshotanddeck_hashto verify the deck commitment. - Use
random_seedwith a deterministic algorithm to verify the pull order. - Use the published
pullstimeline to compare your reconstruction against what actually happened.
Together, these tools let you independently confirm that each GalaxyGrails event is consistent with its pre-committed deck and Chainlink VRF-powered randomness.