> For the complete documentation index, see [llms.txt](https://docs.tramplin.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tramplin.io/fairness-and-transparency.md).

# Fairness and Transparency

Tramplin is designed so that **no single party** - not the team, not the oracle, not any participant - can predict or manipulate distribution outcomes. This page explains the cryptographic mechanisms that guarantee fairness.

## The Trust Problem

In any redistribution system, three critical questions must be answered:

1. **Can the operator rig the results?** - Can whoever runs the protocol choose or influence the selected recipients?
2. **Can participants verify the results?** - Can anyone independently confirm the selection was fair?
3. **Can the participant set be manipulated?** - Can someone be added or removed after the round starts?

Tramplin answers all three through a combination of **commit-reveal cryptography**, **verifiable random functions (VRF)**, and **Merkle proofs**.

## Commit-Reveal Scheme

The core anti-manipulation mechanism. Each distribution round happens in two onchain transactions:

### Phase 1: Commit (`draw` instruction)

Before any randomness is generated, the Dealer publishes:

* **`secret_hash`** = `keccak256(secret)` - a commitment to a secret value
* **`seed`** = `keccak256(merkle_root || secret)` - binds the secret to the current participant set

These values are recorded onchain and visible to everyone. At this point:

* The Dealer knows the secret but doesn't know the VRF output
* The Oracle doesn't know the secret
* Nobody can predict the final randomness

### Phase 2: Reveal (`reveal` instruction)

After the ORAO VRF delivers randomness, the Dealer reveals the original secret. The smart contract verifies:

```
keccak256(secret) == secret_hash           ✓ Secret matches commitment
keccak256(merkle_root || secret) == seed   ✓ Seed is correctly derived
```

If either check fails, the transaction is rejected. The Dealer **cannot change the secret** after seeing the VRF output because the commitment was already published onchain.

### Why This Works

| Threat                                           | Prevention                                                                                                               |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| Dealer picks a favorable secret after seeing VRF | Impossible - secret\_hash is committed before VRF request                                                                |
| Dealer tries multiple secrets                    | Each secret produces a different seed, which produces different VRF output. The committed seed is fixed.                 |
| Oracle colludes with Dealer                      | Oracle doesn't know the secret. Even with known VRF output, the final randomness depends on XOR with the unknown secret. |

## Verifiable Random Function (ORAO VRF)

Tramplin uses [ORAO Network](https://orao.network)'s onchain VRF for randomness generation.

### What is VRF?

A Verifiable Random Function produces a random output that is:

* **Deterministic** - given the same seed, the same output is always produced
* **Unpredictable** - without the VRF private key, the output cannot be predicted
* **Verifiable** - anyone can verify the output was correctly computed for the given seed

### How Tramplin Uses VRF

1. The `draw` instruction sends a `seed` to ORAO's VRF program
2. ORAO's off-chain fulfillment network computes the randomness using their private key
3. The 64-byte randomness is written to a Solana account that anyone can read
4. The `reveal` instruction reads this randomness and combines it with the dealer's secret

### Final Randomness Construction

The final randomness is not the raw VRF output. It is computed as:

```
final_randomness[i] = VRF_lower_32[i] XOR VRF_upper_32[i] XOR secret[i]
```

This three-way XOR ensures:

* **The Dealer alone** cannot determine the result (VRF output is unknown at commit time)
* **The Oracle alone** cannot determine the result (secret is unknown)
* **Even if they collude**, the committed seed constrains the VRF output, and the committed secret\_hash constrains the secret

## Recipient Selection Algorithm

After computing final randomness, the smart contract selects recipients deterministically. The selection logic differs by draw type, reflecting the different fairness objectives of each mechanism.

### Regular Draw (Equal Odds)

The Regular Draw selects 1 winner per round (\~144 rounds per epoch, approximately every 20 minutes) with equal probability - every eligible staker has the same chance regardless of stake size.

```
P(win) = 1 / N(stakers)
```

The final randomness is used to produce a uniform random index into the list of eligible participants:

```
winner_index = final_randomness_compressed % N(stakers)
```

No stake weighting is applied. A staker with 5 SOL has the exact same chance as a staker with 50,000 SOL.

### Epoch Draw (Linear Effective Stake)

The Epoch Draw selects 7 unique winners per epoch, weighted linearly by effective stake.

Effective stake incorporates both raw SOL and the boost from Tramplin Points:

```
effective_stake = stake × min(1 + √(points / stake / K), MAX_BOOST)
```

Where `MAX_BOOST = 5x` and `K = 150`.

Each participant occupies a range in the cumulative effective-stake space proportional to their effective stake:

```
Participant A: effective_stake=100 → occupies positions [0, 99]
Participant B: effective_stake=250 → occupies positions [100, 349]
Participant C: effective_stake=50  → occupies positions [350, 399]
```

The selection uses the **KISS** (Keep It Simple, Stupid) pseudo-random number generator seeded from the final randomness:

```
KISS seeds: 4 × u32 derived from XOR-compressed final randomness
For each of 7 winners:
    recipient_id = KISS.next_u64() % total_effective_stake
    (if collision with already-selected winner, increment and retry)
```

Winners are selected without replacement - each winner is unique within a single Epoch Draw. Recipients are stored in sorted order. The algorithm is deterministic - anyone with the same inputs will produce the same results.

Prize distribution is top-heavy:

| Rank      | Share of Prize Pool |
| --------- | ------------------- |
| 1st place | 61.5%               |
| 2nd place | 18.5%               |
| 3rd place | 6.2%                |
| 4th place | 6.2%                |
| 5th place | 3.1%                |
| 6th place | 3.1%                |
| 7th place | 1.5%                |

### Big Draw (Square Root Weighting)

The Big Draw selects 1 winner every 15 epochs from the accumulated jackpot pool.

Each participant's selection weight is calculated using the square root of their stake:

```
weight = √(stake) × 10
```

This compresses whale advantage dramatically. Under linear weighting, 5,000 SOL would yield a 5,000× advantage over 1 SOL. Under square root weighting, the advantage drops to \~70×.

The weighted cumulative range is built from √stake-derived weights:

```
Participant A: stake=100,   weight=√100×10=100   → occupies positions [0, 99]
Participant B: stake=10000, weight=√10000×10=1000 → occupies positions [100, 1099]
Participant C: stake=1,     weight=√1×10=10     → occupies positions [1100, 1109]
```

Selection uses XOR-compressed final randomness:

```
recipient_id = u64_from_bytes(compressed_randomness) % total_weight
```

### Selection Summary

| Draw Type        | Selection Weight          | Method                    | Winners                   |
| ---------------- | ------------------------- | ------------------------- | ------------------------- |
| **Regular Draw** | Equal (1 per staker)      | Uniform random index      | 1 per round (\~144/epoch) |
| **Epoch Draw**   | Linear (effective stake)  | KISS PRNG, no replacement | 7 per epoch               |
| **Big Draw**     | Square root (√stake × 10) | XOR-compressed randomness | 1 per 15 epochs           |

## Merkle Proofs: Verifying Participation

### What Are Merkle Proofs?

A Merkle tree is a cryptographic data structure that allows compact verification of set membership. The tree's root hash (32 bytes) is committed onchain. Anyone can prove their inclusion in the set by providing a short proof (a few hashes) without revealing the entire set.

### How Tramplin Uses Merkle Trees

1. **Before each round**, the Snapshot Service constructs a Merkle tree of all eligible participants
2. Each leaf contains: `keccak256(stake_id || stake || withdrawer_address)`
3. Leaves are prefixed with `0x00` to prevent [second preimage attacks](https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack/)
4. The **Merkle root** is published onchain via `make_snapshot`
5. The **full snapshot** (with all leaves and proofs) is published at `snapshots.tramplin.io`

### Verification

**Anyone can verify their inclusion:**

1. Download the snapshot from `snapshots.tramplin.io`
2. Find your wallet address in the `tree_nodes`
3. Use the provided `proof` array to verify against the `merkle_root`
4. Compare the `merkle_root` with what's stored onchain

**The smart contract verifies claims:**

When a selected participant calls `claim`, the contract verifies:

```
verify(proof, merkle_root, hash(0x00 || hash(stake_id || stake || withdrawer)))
```

This proves the participant was legitimately part of the set at the time of the snapshot - they cannot be added after the fact.

### Immutability

Once the Merkle root is committed onchain:

* **Adding a participant** would change the root hash - the snapshot is frozen
* **Removing a participant** would change the root hash - impossible without a new `make_snapshot`
* **The `make_snapshot` instruction** can only be called once per epoch by the authorized operator

## Public Auditability

Every step of the protocol produces publicly verifiable data:

| Data                 | Where to find it                                            | How to verify                                     |
| -------------------- | ----------------------------------------------------------- | ------------------------------------------------- |
| Participant snapshot | `snapshots.tramplin.io/{epoch}-regular-{vote_account}.json` | Download and rebuild the Merkle tree              |
| Merkle root          | onchain `Participants` account                              | Deserialize the account data                      |
| Secret hash & seed   | onchain `Draw` account (after commit)                       | Read from Program Logs or deserialize             |
| VRF randomness       | onchain ORAO randomness account                             | Verify using ORAO's SDK                           |
| Secret               | onchain (revealed in `reveal` tx)                           | Check `keccak256(secret) == secret_hash`          |
| Final randomness     | Computed from VRF + secret                                  | XOR the values yourself                           |
| Selected recipients  | onchain `Draw` account (after reveal)                       | Re-run the selection algorithm with the same seed |
| Claims               | onchain `Claimed` accounts                                  | Query by recipient\_id and epoch                  |

## Deadlines as Safety Rails

Deadlines prevent timing-based manipulation:

* **`draw_deadline`**: The commit and reveal must both complete before this slot. If the Dealer misses it, the round is skipped - they cannot wait for a favorable moment.
* **`claim_deadline`**: Selected participants must claim before this slot. After it expires, unclaimed funds can be withdrawn - preventing indefinite fund lockup.

## Role Separation

No single key controls the entire protocol:

| Role         | Can do                             | Cannot do                            |
| ------------ | ---------------------------------- | ------------------------------------ |
| **Admin**    | Change config, enable/disable      | Conduct rounds, snapshot, claim      |
| **Operator** | Publish snapshots                  | Conduct rounds, change config, claim |
| **Dealer**   | Conduct rounds, withdraw unclaimed | Change config, publish snapshots     |

Even a compromised Dealer key cannot:

* Alter the participant set (that requires the Operator key)
* Change protocol parameters (that requires the Admin key)
* Predict VRF output (that requires ORAO's private key)
* Bypass the commit-reveal scheme (enforced by the smart contract)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.tramplin.io/fairness-and-transparency.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
