@rolly-dev/wasm-signer
v1.16.0
Published
Poseidon2 hashing & bet signing for Rolly ZK-Rollup (WASM, Goldilocks field)
Maintainers
Readme
@rolly-dev/wasm-signer
Client-side Poseidon2 hashing and bet authentication for the Rolly ZK-Rollup casino.
Built with Rust → WebAssembly (via wasm-pack), using the same Poseidon2 hasher
and Goldilocks field as the plonky2 proving circuit.
Build
# requires: cargo, wasm-pack
npm run build
# → dist/node/ (Node.js, CJS, sync init)
# → dist/web/ (Browser, ESM, async init)Usage
Node.js (CommonJS)
const {
poseidon2_hash,
derive_session_key,
session_public_key,
} = require('@rolly-dev/wasm-signer');
const hash = poseidon2_hash(BigUint64Array.from([1n, 2n, 3n]));
// → BigUint64Array(4)Node.js (ESM)
import {
poseidon2_hash,
derive_session_key,
session_public_key,
} from '@rolly-dev/wasm-signer';
const sessionKey = derive_session_key(metamaskSignatureBytes);
const pkHash = session_public_key(sessionKey, BigInt(expiryTimestamp));React
import { useRollyWasm } from '@rolly-dev/wasm-signer/react';
function SessionInfo({ sessionKey, expiry }) {
const { ready, session_public_key } = useRollyWasm();
if (!ready) return <span>Loading...</span>;
const pkHash = session_public_key(sessionKey, BigInt(expiry));
// register pkHash on-chain via key_register tx...
return <div>Session active until {new Date(Number(expiry) * 1000).toLocaleString()}</div>;
}Browser (vanilla ESM)
<script type="module">
import { init, poseidon2_hash } from '@rolly-dev/wasm-signer';
await init(); // loads .wasm, must be called once
const h = poseidon2_hash(BigUint64Array.from([1n, 2n]));
</script>Manual init (advanced)
import init, { poseidon2_hash } from '@rolly-dev/wasm-signer/init';
// custom wasm URL or ArrayBuffer
await init('/assets/rolly_wasm_signer_bg.wasm');
poseidon2_hash(BigUint64Array.from([1n]));API
| Function | Input | Output | Description |
|----------------------------|--------------------------------|-------------------|------------------------------------------------|
| poseidon2_hash | BigUint64Array | BigUint64Array(4) | Hash N field elements |
| poseidon2_two_to_one | BigUint64Array(4) × 2 | BigUint64Array(4) | Merkle hash: H(left‖right) |
| derive_session_key | Uint8Array(32) | BigUint64Array(4) | MetaMask sig → session key |
| session_public_key | (BigUint64Array(4), bigint) | BigUint64Array(4) | pk_hash = Poseidon2(session_key, expiry) |
| compute_server_seed_hash | BigUint64Array(8) | BigUint64Array(4) | Full hash of server seed |
| seed_hash_truncated | BigUint64Array(8) | BigUint64Array(3) | First 3 elements (circuit leaf format, 192-bit commitment) |
| goldilocks_modulus | — | bigint | Returns p = 2^64 - 2^32 + 1 |
| goldilocks_reduce | bigint | bigint | Reduce mod p |
Bundler notes
Vite — add to optimizeDeps.exclude:
optimizeDeps: { exclude: ['@rolly-dev/wasm-signer'] }Webpack 5 — enable WASM experiments:
module.exports = { experiments: { asyncWebAssembly: true } };Next.js — conditional exports auto-resolve: Node.js entry on server, browser entry on client.
Dice — range constraints
Roll number: [0, 999] (TOTAL_RANGE = 1000).
win_numbers (wn): [1, 950] (MIN_RANGE..=MAX_RANGE).
prediction_range constraints per mode
| Mode | wn formula | prediction[0] | prediction[1] | wn range |
|-------|-------------------------------------------------|-----------------|-----------------|------------|
| Under | prediction[0] | [1, 950] | unused (0) | [1, 950] |
| Over | 999 − prediction[0] | [49, 998] | unused (0) | [1, 950] |
| In | prediction[1] − prediction[0] + 1 | [0, 999] | [0, 999] | [1, 950] |
| Out | 1000 − (prediction[1] − prediction[0] + 1) | [0, 999] | [0, 999] | [1, 950] |
Under — prediction[0] ∈ [1, 950] (= wn directly). Win: roll < prediction[0].
Over — prediction[0] ∈ [49, 998] (lower: 999 − 950 = 49, upper: 999 − 1 = 998). Win: roll > prediction[0].
In — prediction[0] <= prediction[1], span ∈ [1, 950]. Win: prediction[0] <= roll <= prediction[1].
Out — prediction[0] <= prediction[1], inner span ∈ [50, 999] (wn = 1000 − inner ∈ [1, 950]). Win: roll < prediction[0] || roll > prediction[1].
Multiplier
multiplier = floor(9_900_000 / wn) (scaled ×10000).
| wn | multiplier | display | |-----|-----------|-----------| | 1 | 9_900_000 | 990.0000x | | 500 | 19_800 | 1.9800x | | 950 | 10_421 | 1.0421x |
RTP
Theoretical: 98.998% (floor-division costs ~0.002%). House edge: ~1.002%. Max win cap: 10,000 USDT (business rule, not formula).
License
MIT
