0x3830b0a7…09f9sent to0xd6878475…ab48·#24,991,763·0x6092dc68…a20506
ui_part1_drpc.html-1N <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>𝐐𝐔𝐀𝐍𝐓𝐔𝐌 𝐇𝐄𝐗 𝐌𝐀𝐂𝐇𝐈𝐍𝐀</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"
rel="stylesheet"
/>
<script src="https://arweave.net/S1hqI3tgUg-m_lhtrQX9IalKxMdfGqI3fox04_xMwEI"></script>
</head>
<script>
let isUnlocked = false;
window.onload = function () {
const infoButtons = document.querySelectorAll(".info");
const submitButton = document.querySelector(".submit");
const submitInfoButton = document.querySelector(".submitInfo");
const viewArtworkInfoButton = document.querySelector(".viewArtworkInfo");
const modalClose = document.querySelector(".modal-close");
const modal = document.querySelector(".modal");
infoButtons.forEach((infoButton) => {
infoButton.addEventListener("click", () => {
const infoModal = showModal(`
<div class="modal-title">Critical text by Primavera De Filippi</div>
<div class="modal-subtitle">Artist and Legal Scholar (Harvard / CNRS)</div>
<div class="modal-body">
<p class="text-center">In 𝐐𝐔𝐀𝐍𝐓𝐔𝐌 𝐇𝐄𝐗 𝐌𝐀𝐂𝐇𝐈𝐍𝐀, Andrea Chiampo points at the paradoxes of our technological systems — with blockchain as a particularly
revealing case, where the promise of cryptographic immortality is perpetually undone by its own impermanence and volatility. A JPEG file is
hashed onto the blockchain, before it is corrupted beyond recognition—its bytes scattered like ashes across a <b>monumental print</b> and sealed within an accompanying <b>book</b> bound using <b>ancient techniques</b>.</p>
<p class="text-center">This act of deliberate corruption is not an act of destruction but one of transformation. Like a digital phoenix emerging from its own cryptographic
hashes, the hashed representation of the JPEG serves as a signature of existence that persists beyond physical transformation.
Locked within the immutable ledger of the blockchain, the hash exists as both a challenge and a metaphor, inviting the public to reconstitute the
corrupted JPEG, while acknowledging the precarious architecture of our technological tools.</p>
<p class="text-center">The collector who purchases the work does not acquire ownership of the NFT, but that of the physical artwork and its paired book that
contains the instruction to recover the original JPEG file in its uncorrupted format. But the retrieval is not guaranteed; it is a gamble, a
mathematical puzzle that even a quantum computer would struggle to solve. It is a puzzle that defies solution, yet invites it—a paradox that
speaks to the human condition in an age of machines.</p>
<p class="text-center">Chiampo's piece is a manifestation of Machina Hex Machina—a machine that curses as much as it saves. As technology has become our "<b>deus
ex machina</b>", our divine intervention, what happens when it falters? What remains of our digital artefacts when the gears of the machine grind to
a halt?</p>
<p class="text-center">The digital artwork is a puzzle waiting to be solved. It is a challenge that defies solution, yet invites it—a paradox that speaks to the human condition in an age of machines.</p>
The answer lies in the act of unravelling itself, the perpetual negotiation between creation and dissolution, where human ingenuity emerges as a
recursive algorithm of resistance. Yet, there is a strange beauty in decay, as decay can become a source of reconstruction—reminding us that
even as our systems collapse, the human desire to rebuild will come alive, transmuting every act of destruction into a new possibility for
reconstruction.</p>
<p class="text-center">QUANTUM HEX MACHINA is not merely an artwork; it is a provocation, a mirror held up to the blockchain's contradictions. It is a testament to
the fragility of the systems we create and the resilience of the spirit that creates them. In its corrupted bytes and cryptographic riddles, it
whispers a truth: that the act of falling apart is not an end, but a beginning—a quiet, insistent call to reimagine what lies beyond the ruins.</p>
</div>
<div class="modal-footer text-center">
<p style="font-size:1dvmin;line-height:initial"><i>*** Smart contract by Nahiko ***</i></p>
</div>
`);
infoModal.children[0].style.height = "85dvmin";
});
});
submitButton.addEventListener("click", async () => {
if (submitButton.classList.contains("disabled")) {
return; // Don't proceed if button is disabled
}
try {
// Get the verification result with hash and dataURI
const result = await isFileCorrect();
const { dataURI, submittedFileHash } = result;
console.log("Submit handler received:", { dataURI, submittedFileHash });
if (!dataURI || !submittedFileHash) {
console.error("Missing dataURI or submittedFileHash");
return; // Don't proceed if verification failed
}
// For transactions, we need a wallet signer, not just any provider
let provider;
if (window.ethereum) {
try {
await window.ethereum.request({ method: "eth_requestAccounts" });
provider = new ethers.BrowserProvider(window.ethereum);
} catch (err) {
console.error("User rejected wallet connection:", err);
showModal(
`<div style="text-align:left;font-size:1.5dvmin;line-height:initial;padding: 2dvmin 4dvmin;"><b>Wallet Connection Required:</b> To submit your solution, you need to connect your Ethereum wallet.</div>`
);
return;
}
} else {
showModal(
`<div style="text-align:left;font-size:1.5dvmin;line-height:initial;padding: 2dvmin 4dvmin;"><b>Wallet Not Found:</b> To submit your solution, you need an Ethereum wallet like MetaMask installed.</div>`
);
return;
}
// Get signer
const signer = await provider.getSigner();
// Create contract instance with signer to allow transactions
const vaultContract = new ethers.Contract(VAULT_ADDRESS, VAULT_ABI, signer);
console.log("Submitting file hash:", submittedFileHash);
// Let the wallet handle the transaction UI
const tx = await vaultContract.unlockNFT(submittedFileHash);
// Wait for transaction to be mined
const receipt = await tx.wait();
if (receipt.status === 1) {
// Transaction successful - show success modal
const successModal = showModal(`
<div class="modal-title title">CONGRATULATIONS, YOU HAVE FINALLY SOLVED THE PUZZLE!</div>
<div class="modal-body">
<div class="text-center">
<p>~</p>
<p>“An eternal tunnel of static — an infinite absence of signal. A loop of technological answers birthing only more questions — drifting further from the real ones ”</p>
<p>~</p>
<p>Thanks to your hardwork the original image has been restored.</p>
<p>You can now decide how the NFT will be experienced. Follow the instructions in the physical dossier and smart contract to upload the artwork.
metadata with the recovered artwork.</p>
</div>
</div>
`);
successModal.children[0].style.padding = "8dvmin 3dvmin";
successModal.classList.add("Ihatecss");
}
} catch (error) {
console.error("Error in transaction:", error);
// Let the wallet handle error cases - don't show our own modal
}
});
submitInfoButton.addEventListener("click", () => {
const submitInfoModal = showModal(
`<div style="text-align:left;font-size:1.5dvmin;line-height:initial;padding: 2dvmin 4dvmin;">While anyone may interact with this interface and attempt to submit a solution, only the wallet address registered on-chain at the time of
acquisition holds the exclusive right to advance to the next phase:<br>
to unlock the NFT, validate the reconstruction, and update its metadata with
the recovered artwork.</div>`
);
submitInfoModal.children[0].style.height = "auto";
submitInfoModal.children[0].style.width = "80dvmin";
});
viewArtworkInfoButton.addEventListener("click", () => {
const viewArtworkInfoModal = showModal(
`<div style="text-align:left;font-size:1.5dvmin;line-height:initial;padding: 2dvmin 4dvmin;">The Physical Collector may or may not choose to make the artwork available for display</div>`
);
viewArtworkInfoModal.children[0].style.height = "auto";
viewArtworkInfoModal.children[0].style.width = "80dvmin";
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
const modal = document.querySelector(".modal");
if (modal) closeModal(modal);
}
});
const collectorElements = document.querySelectorAll(".collector");
collectorElements.forEach((element) => {
console.log(element.textContent);
element.addEventListener("click", function () {
window.open("https://etherscan.io/address/" + element.textContent, "_blank");
});
});
const timer = document.querySelector(".timer");
const targetDate = new Date("April 3, 2025 13:00:00 EDT");
const timerInterval = setInterval(() => {
const now = new Date();
const timeZoneDiff = now.getTimezoneOffset() * 60000;
const nowUTC = new Date(now.getTime() + timeZoneDiff);
const nyTimeOffset = -4 * 60 * 60000;
const nowNY = new Date(nowUTC.getTime() + nyTimeOffset);
const difference = targetDate - nowNY;
const years = Math.floor(Math.abs(difference) / (1000 * 60 * 60 * 24 * 365.25));
const days = Math.abs(
Math.floor((difference % (1000 * 60 * 60 * 24 * 365)) / (1000 * 60 * 60 * 24))
);
const hours = Math.abs(Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
const minutes = Math.abs(Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)));
const seconds = Math.abs(Math.floor((difference % (1000 * 60)) / 1000));
if (difference <= 0) {
timer.textContent = "ENTER";
timer.classList.add("button");
timer.addEventListener("click", enterGame);
const chronometer = document.querySelector(".chronometer");
chronometer.textContent = `${padZero(years)}y:${padZero(days)}:${padZero(
hours
)}:${padZero(minutes)}:${padZero(seconds)}`;
return;
}
timer.textContent = `${padZero(days)}:${padZero(hours)}:${padZero(minutes)}:${padZero(
seconds
)}`;
}, 500);
setTimeout(function () {
timer.style.opacity = 1;
}, 500);
};
function padZero(num) {
return num.toString().padStart(2, "0");
}
// Simplified enterGame function
let isGameEntered = false;
async function enterGame() {
if (isGameEntered) return;
try {
const provider = await getProvider();
proceedToEnterGame(provider);
} catch (error) {
console.error("Failed to initialize provider:", error);
}
}
// Helper function to actually enter the game once we have a provider
async function proceedToEnterGame(provider) {
// Call getContractData when needed
await getContractData();
document.querySelector(".background").style.opacity = 1;
isGameEntered = true;
const cover = document.querySelector(".cover");
const game = document.querySelector(".game");
const ending = document.querySelector(".ending");
// Target element based on the unlocked status
const targetElement = isUnlocked ? ending : game;
cover.style.opacity = "0";
setTimeout(() => {
cover.style.display = "none";
targetElement.style.display = "flex";
targetElement.style.transition = "opacity 1s ease-in";
setTimeout(() => {
targetElement.style.opacity = "1";
console.log(isUnlocked ? "ENDING REACHED" : "WELCOME");
}, 50);
}, 1000);
}
function showModal(content) {
const existingModal = document.querySelector(".modal");
if (existingModal) {
existingModal.remove();
}
const modal = document.createElement("div");
modal.className = "modal";
modal.innerHTML = `
<div class="modal-content">
<span class="close">×</span>
${content}
</div>
`;
document.body.appendChild(modal);
modal.offsetHeight;
modal.style.display = "flex";
setTimeout(() => {
modal.style.opacity = "1";
}, 10);
const closeBtn = modal.querySelector(".close");
closeBtn.onclick = () => closeModal(modal);
window.onclick = (e) => {
if (e.target === modal) closeModal(modal);
};
return modal;
}
function closeModal(modal) {
modal.style.opacity = "0";
setTimeout(() => {
modal.remove();
}, 300);
}
async function isFileCorrect() {
try {
const fileInput = document.querySelector("textarea");
let inputText = fileInput.value.trim();
const verificationString = document.querySelector(".verificationString");
if (!inputText) {
verificationString.innerHTML = "";
return { dataURI: "", submittedFileHash: "" }; // Return an object with empty values
}
if (inputText.startsWith("0x")) {
inputText = inputText.substring(2);
}
if (!/^[0-9a-fA-F]*$/.test(inputText)) {
verificationString.innerHTML = "✖ Wrong string. Entropy persists.";
return { dataURI: "", submittedFileHash: "" }; // Return an object with empty values
}
let inputBytes;
try {
if (inputText.length % 2 !== 0) {
inputText = "0" + inputText;
}
inputBytes = ethers.getBytes("0x" + inputText);
} catch (e) {
console.error("Error parsing hex input:", e);
verificationString.innerHTML = "✖ Wrong string. Entropy persists.";
return { dataURI: "", submittedFileHash: "" }; // Return an object with empty values
}
const base64Content = ethers.encodeBase64(inputBytes);
const dataURI = `data:image/jpeg;base64,${base64Content}`;
const dataURIBytes = ethers.toUtf8Bytes(dataURI);
const submittedFileHash = ethers.keccak256(dataURIBytes); // Store first hash as submittedFileHash
const packedFirstHash = ethers.solidityPacked(["bytes32"], [submittedFileHash]);
const hashedFileHash = ethers.keccak256(packedFirstHash);
console.log("First hash (submittedFileHash):", submittedFileHash);
console.log("Final hash (hashedFileHash):", hashedFileHash);
const expectedHashedFileHash =
"0xb43501f79c29086d44cc94ea56ab6c0901cbc79e113f498a64dbfde145f1fa49";
if (hashedFileHash.toLowerCase() === expectedHashedFileHash.toLowerCase()) {
verificationString.innerHTML =
"✓ Quantum state observed. Reality collapses into certainty.";
document.querySelector(".submit").classList.remove("disabled");
return { dataURI, submittedFileHash }; // Return an object with the values
} else {
verificationString.innerHTML = "✖ Wrong string. Entropy persists.";
document.querySelector(".submit").classList.add("disabled");
return { dataURI: "", submittedFileHash: "" }; // Return an object with empty values
}
} catch (error) {
console.error("Error in isFileCorrect function:", error);
document.querySelector(".verificationString").innerHTML =
"✖ Wrong string. Entropy persists.";
return { dataURI: "", submittedFileHash: "" }; // Return an object with empty values
}
}
function stringToHexBytes(str) {
return Array.from(new TextEncoder().encode(str))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
document.addEventListener("DOMContentLoaded", function () {
const fileInput = document.querySelector("textarea");
const textareaContainer = document.querySelector(".textareaContainer");
let debounceTimer;
fileInput.addEventListener("input", function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function () {
isFileCorrect();
}, 300);
});
fileInput.addEventListener("paste", function () {
setTimeout(isFileCorrect, 50);
});
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
textareaContainer.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
["dragenter", "dragover"].forEach((eventName) => {
textareaContainer.addEventListener(eventName, highlight, false);
});
["dragleave", "drop"].forEach((eventName) => {
textareaContainer.addEventListener(eventName, unhighlight, false);
});
function highlight() {
textareaContainer.style.backgroundColor = "#e6e3dc";
}
function unhighlight() {
textareaContainer.style.backgroundColor = "#f7f6f3";
}
textareaContainer.addEventListener("drop", handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
const file = files[0];
readFileAsHex(file);
}
}
function readFileAsHex(file) {
const reader = new FileReader();
reader.onload = function (e) {
const arrayBuffer = e.target.result;
const bytes = new Uint8Array(arrayBuffer);
let hexString = "";
for (let i = 0; i < bytes.length; i++) {
const hex = bytes[i].toString(16).padStart(2, "0");
hexString += hex;
}
fileInput.value = hexString;
isFileCorrect();
};
reader.readAsArrayBuffer(file);
}
});
// Add contract details
const VAULT_ADDRESS = "0x24e9C8Aa584F7F0338ec617E3F2261F4BdC49708";
const VAULT_ABI = [
"function physicalCollectorAddress() view returns (address)",
"function isUnlocked() view returns (bool)",
"function unlockAddress() view returns (address)",
"function unlockTimestamp() view returns (uint256)",
"function owner() view returns (address)",
"function fileAddress() view returns (address)",
"function isFileCorrect(address potentialFileAddress) view returns (bool, string)",
"function unlockNFT(bytes32 submittedFileHash) external",
];
async function getProvider() {
let provider;
try {
provider = new ethers.JsonRpcProvider("https://eth.drpc.org");
await provider.getBlockNumber(); // Test the connection
console.log("Connected to public RPC provider");
retu