Pyth Entropy event with random number not catched on the frontend

Make sure to check the Pyth Documentation to check for guides, common errors and reference materials.

Myself, Deenank participant of ETHGLOBAL New Delhi . I have made the contract, deployed it on Arbitrium Sepolia (attached below), then on the frontend while testing, payment happens when requesting random Number but then event catcher hook does not receive any randomNumber back. What can be the issue please help.
Contract -
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IEntropyConsumer } from “@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol”;

import { IEntropyV2 } from “@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol”;

import { console } from “forge-std/console.sol”;

contract YourContract is IEntropyConsumer {

IEntropyV2 entropy;

mapping(uint64 => bool) public isRequestFulfilled;

mapping(uint64 => bytes32) public receivedRandomNumber;

// Event to notify when a random number is received, without the requestor’s address

event RandomNumberReceived(uint64 indexed sequenceNumber, bytes32 randomNumber);

// @param entropyAddress The address of the entropy contract.

constructor(address entropyAddress) {

entropy = IEntropyV2(entropyAddress);

}

function requestRandomNumber() external payable {

// Get the fee for the request

uint256 fee = entropy.getFeeV2();

require(msg.value >= fee, “Not enough fee to request a random number”);

// Request the random number with the callback

uint64 sequenceNumber = entropy.requestV2{ value: fee }();

console.log("Random number requested with sequence number: %s", sequenceNumber);

// NOTE: We no longer store msg.sender

}

// @param sequenceNumber The sequence number of the request.

// @param provider The address of the provider that generated the random number.

// @param randomNumber The generated random number.

function entropyCallback(

uint64 sequenceNumber,

address provider,

bytes32 randomNumber

) internal override {

console.log("Callback received for sequence number:", sequenceNumber);

console.log("Provider:", provider);

console.log("Random Number:", *uint256*(randomNumber));



isRequestFulfilled\[sequenceNumber\] = true;

receivedRandomNumber\[sequenceNumber\] = randomNumber;

// Emit the event with just the sequence number and the random number

emit RandomNumberReceived(sequenceNumber, randomNumber);

}

// This method is required by the IEntropyConsumer interface.

// It returns the address of the entropy contract which will call the callback.

function getEntropy() internal view override returns (address) {

return address(entropy);

}

}

JS code
import React, { useState,useEffect } from ‘react’;

import Navbar from ‘../components/Navbar.jsx’;

import ‘../styles/DoctorVerification.css’;

// import { generateZkProof } from ‘../api/zkpdfApi.js’;

import { sendDoctorProof } from ‘../api/sendDoctorProof.js’;

import axios from ‘axios’;

import { useWriteContract, useWaitForTransactionReceipt, useWatchContractEvent } from ‘wagmi’;

const contractABI = [{“type”:“constructor”,“inputs”:[{“name”:“entropyAddress”,“type”:“address”,“internalType”:“address”}],“stateMutability”:“nonpayable”},{“type”:“function”,“name”:“_entropyCallback”,“inputs”:[{“name”:“sequence”,“type”:“uint64”,“internalType”:“uint64”},{“name”:“provider”,“type”:“address”,“internalType”:“address”},{“name”:“randomNumber”,“type”:“bytes32”,“internalType”:“bytes32”}],“outputs”:,“stateMutability”:“nonpayable”},{“type”:“function”,“name”:“isRequestFulfilled”,“inputs”:[{“name”:“”,“type”:“uint64”,“internalType”:“uint64”}],“outputs”:[{“name”:“”,“type”:“bool”,“internalType”:“bool”}],“stateMutability”:“view”},{“type”:“function”,“name”:“receivedRandomNumber”,“inputs”:[{“name”:“”,“type”:“uint64”,“internalType”:“uint64”}],“outputs”:[{“name”:“”,“type”:“bytes32”,“internalType”:“bytes32”}],“stateMutability”:“view”},{“type”:“function”,“name”:“requestRandomNumber”,“inputs”:,“outputs”:,“stateMutability”:“payable”},{“type”:“event”,“name”:“RandomNumberReceived”,“inputs”:[{“name”:“sequenceNumber”,“type”:“uint64”,“indexed”:true,“internalType”:“uint64”},{“name”:“randomNumber”,“type”:“bytes32”,“indexed”:false,“internalType”:“bytes32”}],“anonymous”:false}];

const CONTRACT_ADDRESS = “0xF1c2d6Ef2A8eAd7a124f43901F70534668365160”;

const DoctorVerification = () => {

const [formData, setFormData] = useState({

licenseNumber: ‘’,

specialty: ‘’,

issuingAuthority: ‘’

});

const [dragActive, setDragActive] = useState(false);

// State for Medical License

const [medicalLicenseFile, setMedicalLicenseFile] = useState(null);

const [medicalLicenseFileName, setMedicalLicenseFileName] = useState(“”);

const [medicalLicenseVerified,setMedicalLicenseVerified] = useState(false)

const [isVerifying,setIsVerifying] = useState(true);

const [medicalLicenseConfidence,setMedicalLicenseConfidence] = useState(0)

const [idtext,setidtext] = useState(“Your unique doctor ID will appear here”)

const [randomNumber, setRandomNumber] = useState(‘’);

const [events, setEvents] = useState();

// Hook to write to contract

const { writeContract, data: hash, isPending } = useWriteContract();

// Hook to wait for transaction confirmation

const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({

hash,

});

useWatchContractEvent({

address: CONTRACT_ADDRESS,

abi: contractABI,

eventName: ‘RandomNumberReceived’,

onLogs(logs) {

console.log(‘New RandomNumberReceived events:’, logs);

logs.forEach(log => {

const sequenceNumber = log.args.sequenceNumber;

const randomNumberHex = log.args.randomNumber;

// Convert bytes32 to decimal string for display

const randomNumberDecimal = BigInt(randomNumberHex).toString();

const eventData = {

sequenceNumber: sequenceNumber.toString(),

randomNumber: randomNumberHex,

randomNumberDecimal,

blockNumber: log.blockNumber,

transactionHash: log.transactionHash,

timestamp: new Date().toLocaleString()

    };

// Store the latest random number

setRandomNumber(randomNumberHex.toString());

// Add to events list

setEvents(prev => [eventData, …prev]);

  });

},

});

// State for ID Document

const [idFile, setIdFile] = useState(null);

const [idFileName, setIdFileName] = useState(“”);

// General State

const [isLoading, setIsLoading] = useState(false);

const [proof, setProof] = useState(null);

const [error, setError] = useState(‘’);

const handleInputChange = (e) => {

setFormData({

…formData,

  \[*e*.target.name\]: *e*.target.value

});

};

const handleFile = async (file, fileType) => {

if (file && file.type === “application/pdf”) {

if (fileType === ‘medicalLicense’) {

setMedicalLicenseFile(file);

setMedicalLicenseFileName(file.name);

try {

// Show loading state (optional)

setIsVerifying(true); // Add this state if you want loading indicator

// Create FormData to send file

const formData = new FormData();

formData.append(‘file’, file);

// Send request to backend

const response = await axios.post(

http://localhost:5000/verifyDocCertificate’,

formData,

      {

headers: {

‘Content-Type’: ‘multipart/form-data’,

        },

timeout: 30000, // 30 second timeout for verification

      }

    );

// Handle successful response

if (response.data.status === ‘success’) {

if (response.data.verified) {

setMedicalLicenseVerified(true);

setMedicalLicenseConfidence(response.data.confidence);

setError(‘’); // Clear any previous errors

      } else {

setMedicalLicenseVerified(false);

setError(‘Medical license could not be verified. Please check the document and try again.’);

      }

    } else {

setError(‘Verification failed. Please try again.’);

    }

  } catch (error) {

console.error(‘Error verifying medical license:’, error);

// Handle different types of errors

if (error.code === ‘ECONNABORTED’) {

setError(‘Verification timeout. Please try again.’);

    } else if (error.response) {

// Server responded with error status

const errorMessage = error.response.data?.error || ‘Server error during verification’;

setError(errorMessage);

    } else if (error.request) {

// Request was made but no response received

setError(‘Unable to connect to verification service. Please check your connection.’);

    } else {

// Something else happened

setError(‘An unexpected error occurred during verification.’);

    }

// Reset verification state on error

setMedicalLicenseVerified(false);

setMedicalLicenseConfidence(0);

  } finally {

setIsVerifying(false); // Hide loading state

  }

} else if (*fileType* === 'id') {

setIdFile(file);

setIdFileName(file.name);

// You can add similar verification logic for ID documents here if needed

// For now, just setting the file without verification

}

setError(‘’); // Clear previous errors on new valid upload

setProof(null); // Clear previous proofs

} else {

setError(“Please upload a valid PDF file.”);

// Clear file states if invalid file

if (fileType === ‘medicalLicense’) {

setMedicalLicenseFile(null);

setMedicalLicenseFileName(‘’);

setMedicalLicenseVerified(false);

setMedicalLicenseConfidence(0);

} else if (*fileType* === 'id') {

setIdFile(null);

setIdFileName(‘’);

}

}

};

const handleGenerateProof = async () => {

if (!medicalLicenseFile || !idFile) {

setError(‘Please upload both a medical license and an ID document.’);

return;

}

setIsLoading(true);

setError(‘’);

setProof(null);

// Commented out actual ZK proof generation, using mock data for now

/*

try {

    // Generate proofs for both files concurrently

    const \[medicalLicenseProofData, idProofData\] = await Promise.all(\[

      generateZkProof(medicalLicenseFile),

      generateZkProof(idFile)

    \]);

    setProof({

      medicalLicense: JSON.stringify(medicalLicenseProofData, null, 2),

      id: JSON.stringify(idProofData, null, 2)

    });



  } catch (err) {

    setError(err.message || 'An error occurred while generating proofs.');

  } finally {

    setIsLoading(false);

  }

\*/

// Use mock data for proof

setProof({

medicalLicense: JSON.stringify({ proof: “mock_medical_license_proof”, details: “This is mock data.” }, null, 2),

id: JSON.stringify({ proof: “mock_id_proof”, details: “This is mock data.” }, null, 2)

});

setIsLoading(false);

};

useEffect(() => {

if (proof) {

sendDoctorProof(JSON.parse(proof))

      .then((*res*) => {

console.log(‘Proof sent to backend:’, res);

      })

      .catch((*err*) => {

console.error(‘Error sending proof to backend:’, err);

      });

  }

}, \[proof\]);

const handleDrag = (e) => {

e.preventDefault();

e.stopPropagation();

if (e.type === ‘dragenter’ || e.type === ‘dragover’) {

setDragActive(true);

} else if (*e*.type === 'dragleave') {

setDragActive(false);

}

};

const handleDrop = (e, fileType) => {

e.preventDefault();

e.stopPropagation();

setDragActive(false);

if (e.dataTransfer.files && e.dataTransfer.files[0]) {

console.log(e.dataTransfer.files);

handleFile(e.dataTransfer.files[0], fileType);

}

};

const handleFileUpload = (e, fileType) => {

if (e.target.files && e.target.files[0]) {

console.log(e.target.files[0]);

handleFile(e.target.files[0], fileType);

}

};

const handleCancel = () => {

setFormData({

// licenseNumber: ‘’,

name:‘’,

specialty: ‘’,

// issuingAuthority: ‘’

});

setMedicalLicenseFile(null);

setMedicalLicenseFileName(“”);

setIdFile(null);

setIdFileName(“”);

setProof(null);

setError(“”);

setIsLoading(false);

};

const handleSubmit = async () => {

if (!proof) {

setError(‘Please generate the proofs before submitting.’);

return;

}

if (!medicalLicenseFile) {

setError(‘Please upload your medical license PDF.’);

return;

}

// Prepare FormData with all details and the certificate file

const formDataToSend = new FormData();

formDataToSend.append(‘name’, formData.licenseNumber);

formDataToSend.append(‘specialty’, formData.specialty);

formDataToSend.append(‘nationality’, ‘IND’);

formDataToSend.append(‘certificate’, medicalLicenseFile);

// Add doctor address here (replace with actual address from wallet integration)

formDataToSend.append(‘doctorAddress’, window.ethereum?.selectedAddress || “0xDoctorAddress”);

try {

const response = await axios.post(‘http://localhost:5000/api/doctors/register’, formDataToSend, {

headers: { ‘Content-Type’: ‘multipart/form-data’ }

  });

// Handle response as needed

console.log(‘Doctor registered:’, response.data);

} catch (err) {

setError('Registration failed: ’ + (err.message || ‘Unknown error’));

}

}

async function generateRandomId() {

try {

console.log(‘Requesting random number from contract…’);

setIsLoading(true);

await writeContract({

address: CONTRACT_ADDRESS,

abi: contractABI,

functionName: ‘requestRandomNumber’,

value: BigInt(‘1000000000000000’), // 0.001 ETH in wei - adjust based on actual fee

  });

console.log(‘Random number request sent.’);

} catch (err) {

console.error(‘Transaction failed:’, err);

} finally {

setIsLoading(false);

}

}

// Update loading state based on transaction status

useEffect(() => {

setIsLoading(isPending || isConfirming);

}, [isPending, isConfirming]);

return (

<Navbar />

<svg width=“50” height=“60” viewBox=“0 0 50 60” fill=“none”>

<path d=“M25 45.7143C23.3424 45.7143 21.7527 45.1122 20.5806 44.0406C19.4085 42.969 18.75 41.5155 18.75 40C18.75 36.8286 21.5312 34.2857 25 34.2857C26.6576 34.2857 28.2473 34.8878 29.4194 35.9594C30.5915 37.031 31.25 38.4845 31.25 40C31.25 41.5155 30.5915 42.969 29.4194 44.0406C28.2473 45.1122 26.6576 45.7143 25 45.7143ZM43.75 54.2857V25.7143H6.25V54.2857H43.75ZM43.75 20C45.4076 20 46.9973 20.602 48.1694 21.6737C49.3415 22.7453 50 24.1988 50 25.7143V54.2857C50 55.8012 49.3415 57.2547 48.1694 58.3263C46.9973 59.398 45.4076 60 43.75 60H6.25C4.5924 60 3.00268 59.398 1.83058 58.3263C0.65848 57.2547 0 55.8012 0 54.2857V25.7143C0 22.5429 2.78125 20 6.25 20H9.375V14.2857C9.375 10.4969 11.0212 6.86328 13.9515 4.18419C16.8817 1.5051 20.856 0 25 0C27.0519 0 29.0837 0.369511 30.9794 1.08744C32.8751 1.80536 34.5976 2.85764 36.0485 4.18419C37.4995 5.51074 38.6504 7.08559 39.4356 8.81881C40.2208 10.552 40.625 12.4097 40.625 14.2857V20H43.75ZM25 5.71429C22.5136 5.71429 20.129 6.61734 18.3709 8.2248C16.6127 9.83225 15.625 12.0124 15.625 14.2857V20H34.375V14.2857C34.375 12.0124 33.3873 9.83225 31.6291 8.2248C29.871 6.61734 27.4864 5.71429 25 5.71429Z” fill=“black”/>

Doctor Credential Verification

        Securely submit your credentials for verification on the blockchain. Help us build a trusted and transparent healthcare ecosystem.

Medical License Information

{/*

This Information will be encrypted and stored securely

*/}

<label className=“form-label”>Name

<input type=“text” name=“licenseNumber” className=“form-input” placeholder=“eg. 123456789” value={formData.licenseNumber} onChange={handleInputChange} />

<label className=“form-label”>Speciality

<input type=“text” name=“specialty” className=“form-input” placeholder=“Cardiology, Pediatrics” value={formData.specialty} onChange={handleInputChange} />

{/*

          <label className="form-label">Issuing Authority</label>

          <input type="text" name="issuingAuthority" className="form-input" placeholder="eg. State Medical Board" value={formData.issuingAuthority} onChange={handleInputChange} />

        </div> \*/}

Upload and Verify Documents

1. Please upload a clear copy of your Medical License (PDF only).

handleDrop(*e*, 'medicalLicense')}>

{medicalLicenseFileName ? medicalLicenseFileName : <><span className=“upload-bold”>Upload a PDF file or drag and drop</>}

<input type=“file” className=“file-input” accept=“.pdf” onChange={(e) => handleFileUpload(e, ‘medicalLicense’)} />

2. Please upload a clear copy of your ID Document (PDF: Aadhar, PAN, etc.).

handleDrop(*e*, 'id')}>

{idFileName ? idFileName : <><span className=“upload-bold”>Upload a PDF file or drag and drop</>}

<input type=“file” className=“file-input” accept=“.pdf” onChange={(e) => handleFileUpload(e, ‘id’)} />

{error && <p className=“error-message”>{error}

}

{isLoading && <p className=“loading-message”>Generating your secure proofs… This may take a moment.

}

{proof && (

Proofs Generated Successfully!

Medical License Proof

{proof.medicalLicense}

ID Document Proof

{proof.id}
      )}

<button className=“cancel-btn” onClick={handleCancel}>Cancel

<button className=“submit-btn” onClick={handleGenerateProof} disabled={isLoading || !medicalLicenseFile || !idFile}>

<svg className=“check-icon” width=“18” height=“18” viewBox=“0 0 20 20” fill=“green”> <path d=“M12.8429 1.03526C13.1238 0.970706 13.4178 0.995206 13.6841 1.10536C13.9504 1.2155 14.1758 1.40584 14.329 1.64993L15.7081 3.85191C15.8193 4.02927 15.9692 4.17916 16.1466 4.29036L18.3486 5.66955C18.5932 5.82264 18.784 6.04818 18.8944 6.31477C19.0048 6.58137 19.0294 6.87576 18.9646 7.15697L18.3819 9.68779C18.3349 9.89233 18.3349 10.1049 18.3819 10.3094L18.9646 12.8416C19.0287 13.1224 19.0039 13.4162 18.8935 13.6822C18.7831 13.9483 18.5927 14.1733 18.3486 14.3263L16.1466 15.7068C15.9692 15.818 15.8193 15.9679 15.7081 16.1453L14.329 18.3473C14.176 18.5916 13.9506 18.7822 13.6843 18.8926C13.418 19.003 13.1239 19.0277 12.8429 18.9633L10.3107 18.3806C10.1066 18.3338 9.89459 18.3338 9.6905 18.3806L7.15828 18.9633C6.87728 19.0277 6.58319 19.003 6.31688 18.8926C6.05057 18.7822 5.82526 18.5916 5.67226 18.3473L4.29307 16.1453C4.18147 15.9678 4.03109 15.8179 3.85323 15.7068L1.65263 14.3276C1.40829 14.1746 1.21767 13.9493 1.10727 13.683C0.996866 13.4167 0.972156 13.1226 1.03657 12.8416L1.61794 10.3094C1.66495 10.1049 1.66495 9.89233 1.61794 9.68779L1.03519 7.15697C0.970589 6.87562 0.995352 6.58112 1.10603 6.31451C1.2167 6.0479 1.40777 5.82244 1.65263 5.66955L3.85323 4.29036C4.03109 4.17932 4.18147 4.02942 4.29307 3.85191L5.67226 1.64993C5.82537 1.4061 6.05053 1.21594 6.31654 1.10581C6.58256 0.995674 6.87624 0.971018 7.1569 1.03526L9.6905 1.61663C9.89459 1.66342 10.1066 1.66342 10.3107 1.61663L12.8429 1.03526Z” stroke=“white” strokeWidth=“1.5”/> <path d=“M6.55176 10.7425L9.37535 13.4467L13.4477 6.55078” stroke=“white” strokeWidth=“1.5” strokeLinecap=“round” strokeLinejoin=“round”/>

{isLoading ? ‘Generating Proofs…’ : ‘Generate ZK Proof’}

Generate your ID

Your ID, Your Identity

{/* */}

<input

type=“text”

style={{width: ‘100%’,marginBottom: ‘20px’}}

className=“form-input”

value={randomNumber || ‘’}

readOnly

placeholder={idtext}

/>

<button

className=“submit-btn”

onClick={async () => {

setidtext(“Generating your unique doctor ID…”);

generateRandomId();

          }}

disabled={isLoading}

>

          Generate Doctor ID

<button className=‘submit-btn’ onClick={handleSubmit} style={{alignSelf:‘center’}} disabled = {isLoading || !medicalLicenseFile || !idFile || !proof || !medicalLicenseVerified} >Register

);

};

export default DoctorVerification;

Have you checked your tx hash on the entropy explorer?