Abstract
Torinox Protocol is a commit-reveal randomness oracle deployed on Robinhood Chain (chain ID 4663), an Arbitrum Nitro-based Layer 2 network. It provides onchain verifiable randomness through a hash-chain commitment scheme where a designated provider (the keeper) pre-commits to a sequence of random values and reveals them on-demand. The protocol combines user-contributed randomness with provider-revealed values using Keccak256, producing onchain-verifiable random numbers under the commit-reveal model delivered via callbacks to consuming smart contracts. Torinox Protocol operates as the commit-reveal randomness oracle on Robinhood Chain under a single-provider model, charging an exact fee of 0.000025 ETH. Unrevealed requests can be refunded after ~60-90s via refundRequest (refundDelayBlocks = 6).
1. Introduction
1.1 Problem Statement
Onchain applications - games, lotteries, NFT mints, fair randomized distribution mechanisms - require a source of randomness that is:
- Two-party: Neither requester nor provider unilaterally chooses a completed result. The provider can still withhold a reveal.
- Verifiable: The randomness can be independently verified after the fact.
- Available: The randomness is delivered reliably and within a predictable timeframe.
- Economically viable: The cost per request is low enough for widespread adoption.
Robinhood Chain applications need dedicated randomness infrastructure because deterministic contract execution and chain-controlled values are not suitable private randomness sources. Torinox Protocol provides a live commit-reveal option for this use case.
1.2 Solution
Torinox Protocol implements a commit-reveal scheme based on hash chains:
- A provider generates a chain of 500,000 random values by repeatedly hashing a secret seed with Keccak256.
- The provider commits the root (final hash) of this chain to the onchain contract during deployment.
- When a user requests randomness, they contribute their own random value.
- The provider reveals the next hash in the chain, which the contract verifies by hashing it and checking against the committed root.
- The final random number is
Keccak256(userRandomness ‖ providerContribution ‖ bytes32(0)), combining both parties' inputs with the fixed zero blockhash argument used by requestV2.
Agent-friendly: Immutable contract code, deterministic fees, and documented verification make Torinox Protocol straightforward for AI agents and automation systems to integrate. Agents still need key security, transaction simulation, receipt checks, timeout handling, and explicit refund logic. Agents can also use the live HTTP 402 surface (POST /x402/v1/random) at fixed $0.05 USDG via Primer on Robinhood Chain; the gateway sponsors the on-chain ETH fee. Direct requestV2 with exact ETH fee remains available. A complete SDK repository is published for agent consumption.
2. Architecture
2.1 System Components
┌─────────────────────────────────────────────────────────┐
│ Robinhood Chain L2 │
│ │
│ ┌──────────────────────┐ ┌────────────────────────┐ │
│ │ TorinoxEntropy.sol │ │ Consumer Contract │ │
│ │ (Oracle Contract) │◄──►│ (implements │ │
│ │ │ │ IEntropyConsumer) │ │
│ │ - Hash chain state │ │ │ │
│ │ - Fee accounting │ │ - requestV2() │ │
│ │ - Reveal verification│ │ - entropyCallback() │ │
│ │ - Callback dispatch │ │ │ │
│ └──────────┬───────────┘ └────────────────────────┘ │
│ │ │
└─────────────┼────────────────────────────────────────────┘
│
│ Watch for Requested events
│ Submit revealWithCallback txs
│
┌─────────────┼────────────────────────────────────────────┐
│ ▼ Off-chain (Keeper) │
│ ┌──────────────────────┐ │
│ │ Tyche Keeper │ │
│ │ (Rust service) │ │
│ │ │ │
│ │ - HTTP log polling │ │
│ │ - Hash chain compute│ │
│ │ - Reveal submission │ │
│ └──────────────────────┘ │
└──────────────────────────────────────────────────────────┘2.2 TorinoxEntropy Contract
The onchain contract is immutable (no proxy pattern) and handles:
- Provider registration: The constructor auto-registers the default provider with its hash chain commitment, chain length, and metadata.
- Request handling: Users call
requestV2()with a user random value and optional gas limit. - Reveal verification: The contract verifies the provider's contribution by hashing it and comparing to the stored commitment.
- Callback dispatch: After verification, calls the consumer's
entropyCallback()with the combined random number. - Fee accounting: Each request charges an exact fee (0.000025 ETH). Underpay/overpay revert. Unrevealed requests are refundable after ~60-90s via refundRequest. Fees accrue in the contract and are withdrawable by the admin.
2.3 Tyche Keeper
Tyche is a Rust-based off-chain service that:
- Monitors the blockchain for
Requestedevents through monitored Robinhood Chain RPC infrastructure. - Computes the reveal value for each request by traversing the precomputed hash chain.
- Submits
revealWithCallbacktransactions from the keeper wallet. - Maintains durable request and reveal state for recovery and observability.
2.4 Wallet Separation
| Role | Address | Key Type | Purpose |
|---|---|---|---|
| Admin | 0x4ACD... | Cold | Contract admin, fee management |
| Vault | 0x918E... | Cold | Fee recipient |
| Keeper / provider | 0x8741... | Hot | Submits reveals and may update provider URI, max hashes, and default callback gas. Cannot change protocol fee/admin or withdraw protocol fees. |
3. Cryptographic Design
3.1 Hash Chain Construction
The hash chain is an S/KEY-like construction using Keccak256:
- Start with a 32-byte secret seed
s. - Compute
h₁ = Keccak256(s). - For
i = 2..N: computehᵢ = Keccak256(hᵢ₋₁). - The chain is
[h₁, h₂, ..., h_N]whereh_Nis the root commitment. - Reveal order is reverse:
h_Nrevealed first (sequence 0), then h_(N-1) (sequence 1), etc.
3.2 Random Number Combination
randomNumber = Keccak256(userRandomness ‖ providerContribution ‖ bytes32(0))- userRandomness: Provided by the requester. Unknown to the provider at commitment time.
- providerContribution: The hash chain value at the current sequence number. Unknown to the user at request time.
For a completed reveal, neither requester nor provider can unilaterally choose the output. A provider can still withhold service; eligible requests become refundable after the configured delay.
4. Economic Model
4.1 Fee Structure
- Fee per request: exact 0.000025 ETH (25,000,000,000,000 wei)
- Refund delay: 6 L1 blocks (~60-90s) via refundRequest
- Fee destination: 100% to the contract's accrued fee pool
- Withdrawal: Admin calls
withdrawFees(), sending the full balance to the vault
The contract uses one protocol-level request fee with no provider fee split. Request and reveal transaction gas remain separate from that fee.
4.2 Failed-request economics
A refund returns the exact request fee, but transaction gas is not refunded.
- The requester pays gas for the original request and for
refundRequest. - Torinox retains no request fee and earns nothing from an unrevealed, refunded request.
- If no reveal transaction was submitted, Torinox has no direct reveal-transaction gas cost for that request.
- If a reveal transaction was submitted but reverted, Torinox bears that failed transaction's gas cost.
4.3 Gas Economics
| Operation | Gas Cost | ETH Cost |
|---|---|---|
| Request (consumer) | 500,000 | 0.0000025 |
| Reveal (keeper) | ~53,506 | 0.0000051 |
| Callback | ~500,000 | 0.0000026 |
| Total per request | ~53,506 | ~0.0000051 |
5. Smart Contract API
5.1 Core Functions
requestV2(address provider, bytes32 userRandomNumber, uint32 gasLimit) → uint64Request randomness. Caller must send ETH equal to the provider's fee. Callback gas is max(requested, provider.defaultGasLimit), rounded up to 10,000, bounded by MAX_GAS_LIMIT. A 0 request uses the live default (200,000). If defaultGasLimit is 0, stored callback gas is 0. Returns assigned sequence number.
revealWithCallback(address provider, uint64 sequenceNumber, bytes32 userContribution, bytes32 providerContribution)Called by keeper to reveal provider's contribution and trigger consumer's callback.
getFee(address provider) → uint128Returns the current fee for a provider.
withdrawFees(uint128 amount)Admin-only. Withdraws accrued fees to the vault address.
5.2 Consumer Interface
interface IEntropyConsumer {
function entropyCallback(
uint64 sequenceNumber,
address provider,
bytes32 randomNumber
) internal;
function getEntropy() internal view returns (address);
}6. Security Analysis
6.1 Attack Vectors
| Attack | Mitigation |
|---|---|
| Provider withholds reveal | No onchain penalty in v1; relies on service uptime. Future: slashing. |
| User front-runs reveal | User commits random value before provider reveals. |
| Callback gas griefing | Effective callback gas is max(requested, defaultGasLimit), rounded up to 10,000, bounded by MAX_GAS_LIMIT. Live default 200,000: a 0 request becomes 200,000; an explicit larger request is honored. If defaultGasLimit is 0, stored gas is 0. Uses excessivelySafeCall. |
| Chain exhaustion | OutOfRandomness revert when sequence exceeds chain length. |
| Private key compromise | Four-wallet separation limits blast radius. Keeper cannot steal fees. |
7. Integration Guide
7.1 Solidity Integration
import { IEntropyConsumer } from "@diceprotocol/sdk/solidity/IEntropyConsumer.sol";
import { IEntropy } from "@diceprotocol/sdk/solidity/IEntropy.sol";
contract MyGame is IEntropyConsumer {
IEntropy public immutable dice;
address public provider;
constructor(address _dice, address _provider) {
dice = IEntropy(_dice);
provider = _provider;
}
function roll(bytes32 userRandom) external payable {
// Generate userRandom client-side with a CSPRNG and keep it private.
dice.requestV2{value: msg.value}(provider, userRandom, 200000);
}
function entropyCallback(uint64 seq, address, bytes32 random) internal override {
uint256 result = uint256(random) % 6 + 1; // Torinox roll 1-6
}
function getEntropy() internal view override returns (address) {
return address(dice);
}
}7.2 TypeScript SDK
import { TorinoxProtocol } from '@diceprotocol/sdk';
const dice = new TorinoxProtocol({
rpcUrl: 'https://rpc.mainnet.chain.robinhood.com',
contractAddress: '0xd8a0680e7699526b57140ed4eafdcc7219dc0a0c',
});
const seqNum = await dice.requestRandom(
signer,
providerAddress,
userRandom,
200000
);8. Roadmap
- ✅ TorinoxEntropy contract deployed on Robinhood Chain mainnet
- ✅ Tyche keeper operational with auto-reveal
- ✅ TypeScript SDK
- ✅ Internal + automated security review (no third-party audit published yet)
- Multi-provider support (redundancy)
- Tyche monitoring dashboard
- Public reliability and latency metrics
- Slashing mechanism for provider non-responsiveness (requires new deployment)
- Variable fee tiers (basic/premium)
- Cross-chain expansion to other Nitro L2s
Appendix
0xd8a0680e7699526b57140ed4eafdcc7219dc0a0c4663https://rpc.mainnet.chain.robinhood.comhttps://robinhoodchain.blockscout.com25,000,000,000,000 wei (0.000025 ETH exact)https://diceprotocol.world/x402/v1/random · $0.05 USDG6 L1 blocks (~60-90s)500,000 entries, Keccak256200,000Torinox Protocol is developed and maintained as independent infrastructure for Robinhood Chain. No runtime dependency on another randomness oracle. Architecture and interfaces are adapted from Pyth Entropy under Apache-2.0; see NOTICE.