<div class="w-full max-w-lg flex flex-col mx-auto my-4">
<div class="flex items-center gap-2">
<svg width="1.25rem" height="1.25rem" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.7156 1.53348C8.89223 1.43415 9.10778 1.43415 9.2844 1.53348L15.2057 4.86421L9.00045 8.3547L2.79461 4.86404L8.7156 1.53348ZM2.23242 5.83859V12.4805C2.23242 12.69 2.34546 12.8833 2.52811 12.986L8.43795 16.3103V9.3291L2.23242 5.83859ZM9.56295 16.3099L15.4719 12.986C15.6545 12.8833 15.7676 12.69 15.7676 12.4805V5.83891L9.56295 9.3291V16.3099Z" fill="currentColor"></path></svg>
<h1 class="text-lg font-bold">Lootbox.com Provably Fair - Boxes</h1>
</div>
<div class="flex flex-col gap-1.5 mt-4">
<label for="hashedServerSeed">Hashed Server Seed</label>
<input id="hashedServerSeed">
<div class="msg text-sm" id="hashedServerSeed-msg"></div>
</div>
<div class="flex flex-col gap-1.5 mt-3">
<label for="serverSeed">Server Seed</label>
<input id="serverSeed">
<div class="msg text-sm" id="serverSeed-msg"></div>
</div>
<div class="flex flex-col gap-1.5 mt-3">
<label for="clientSeed">Client Seed</label>
<input id="clientSeed">
<div class="msg text-sm" id="clientSeed-msg"></div>
</div>
<div class="flex flex-col gap-1.5 mt-3">
<label for="nonce">Nonce</label>
<input id="nonce" type="number">
<div class="msg text-sm" id="nonce-msg"></div>
</div>
<button id="verify" class="mt-6">Verify</button>
<div class="msg text-sm mt-4" id="result"></div>
</div>
html {
width: 100%;
height: 100%;
overflow: hidden;
color-scheme: dark;
}
body {
width: 100%;
height: 100%;
color: #9da0c0;
font-weight: 500;
background: #0f111d;
padding: 0 1rem;
overflow-y: auto;
}
h1 {
color: #f3faff;
}
label {
width: fit-content;
}
label > span {
color: #515474;
}
input {
height: 2.5rem;
color: #f3faff;
font-size: .875rem;
line-height: calc(1.25/.875);
background: #191b2b;
border: 1px solid transparent;
border-radius: 0.5rem;
padding: 0 1rem;
outline: none !important;
transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
input:hover {
border-color: #2d2f46;
}
input:focus {
border-color: #ffc300;
}
button {
height: 2.625rem;
color: #13141b;
font-weight: 700;
font-size: 0.875rem;
background: #21d35b;
border-top: 1px solid #49e578;
border-bottom: 2px solid #2e7e40;
border-radius: 0.5rem;
padding: 0 1rem;
user-select: none;
transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
button:hover {
filter: brightness(0.9);
}
button:active {
filter: brightness(0.8);
}
import { sha256 } from 'https://esm.sh/js-sha256'
import { sha512 } from 'https://esm.sh/js-sha512'
document.getElementById('verify').onclick = () => {
resetMessages()
const hashedServerSeed = document.getElementById('hashedServerSeed').value
const serverSeed = document.getElementById('serverSeed').value
const clientSeed = document.getElementById('clientSeed').value
const nonce = document.getElementById('nonce').value
let error = false
if (!hashedServerSeed) error = message('error', 'This field is required', 'hashedServerSeed')
if (!serverSeed) error = message('error', 'This field is required', 'serverSeed')
if (!clientSeed) error = message('error', 'This field is required', 'clientSeed')
if (!nonce) error = message('error', 'This field is required', 'nonce')
if (error) return
const hashedServerSeedReplica = sha256(serverSeed)
if (hashedServerSeedReplica !== hashedServerSeed) return message('error', "Hashed Server Seed doesn't resolve to Server Seed")
const MAX_TICKET = 100_000_000
const resultHash = sha512(`${clientSeed}:${serverSeed}:${nonce}`)
const subHash = resultHash.slice(0, 13)
const valueFromHash = parseInt(subHash, 16)
const e = Math.pow(2, 52)
const normalizedValue = valueFromHash / e
const ticketResult = Math.floor(normalizedValue * MAX_TICKET) + 1
const formattedTicketResult = Intl.NumberFormat('en-US', {
style: 'decimal'
}).format(ticketResult)
message('success', `Ticket Result: ${formattedTicketResult}`)
}
function message(type, msg, elemId) {
if (type === 'error') {
if (elemId) {
document.getElementById(`${elemId}-msg`).innerHTML = `<div class="text-red-500 font-normal">${msg}</div>`
document.getElementById(elemId).classList.add('border-red-500')
} else {
document.getElementById('result').innerHTML = `
<div class="text-red-500 text-lg font-bold">UNBOXING IS INVALID</div>
<div class="text-white font-normal">${msg}</div>
`
}
} else {
document.getElementById('result').innerHTML = `
<div class="text-green-500 text-lg font-bold">UNBOXING IS VALID</div>
<div class="text-white font-normal">${msg}</div>
`
}
return type === 'error'
}
function resetMessages() {
const messages = document.querySelectorAll('.msg')
messages.forEach(msg => {
msg.innerHTML = ''
})
const inputs = document.querySelectorAll('input')
inputs.forEach(msg => {
msg.classList.remove('border-red-500')
})
}
This Pen doesn't use any external JavaScript resources.