Skip to content

Instantly share code, notes, and snippets.

@Turupawn
Created May 30, 2025 17:00
Show Gist options
  • Save Turupawn/b858c927ecfcbd1ad3fb5ada80ed731e to your computer and use it in GitHub Desktop.
Save Turupawn/b858c927ecfcbd1ad3fb5ada80ed731e to your computer and use it in GitHub Desktop.
wss real time backend
require('dotenv').config();
const express = require('express');
const Web3 = require('web3');
const app = express();
const port = process.env.PORT || 3000;
// Initialize Web3 with WebSocket provider
const web3 = new Web3(new Web3.providers.WebsocketProvider(process.env.WSS_URL));
const contractAddress = process.env.CONTRACT_ADDRESS;
const contractABI = require('../json_abi/MyContract.json');
// Create contract instance
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Create house wallet from private key
const houseAccount = web3.eth.accounts.privateKeyToAccount(process.env.HOUSE_PRIVATE_KEY);
web3.eth.accounts.wallet.add(houseAccount);
console.log('House wallet address:', houseAccount.address);
console.log('Server started on port', port);
// Keep track of processed games
const processingGameIds = new Set();
// Function to generate random bytes32 hash
function generateRandomHash() {
return web3.utils.randomHex(32);
}
// Function to post hash for a player
async function postHashForPlayer(playerAddress, gameId) {
try {
if (processingGameIds.has(gameId)) {
console.log(`Already processing game ${gameId}, skipping`);
return;
}
processingGameIds.add(gameId);
console.log(`Started processing game ${gameId}`);
const gameState = await contract.methods.getGameState(playerAddress).call();
if (gameState.gameState !== "1") {
console.log(`Game ${gameId} is no longer in committed state, skipping`);
processingGameIds.delete(gameId);
return;
}
const hash = generateRandomHash();
const stakeAmount = await contract.methods.STAKE_AMOUNT().call();
const tx = {
from: houseAccount.address,
to: contractAddress,
value: stakeAmount,
gas: 300000,
data: contract.methods.postHash(playerAddress, hash).encodeABI()
};
const signedTx = await web3.eth.accounts.signTransaction(tx, houseAccount.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
if (receipt.status) {
console.log(`Posted hash for game ${gameId} (player ${playerAddress}):`, {
hash: hash,
txHash: receipt.transactionHash
});
} else {
console.error(`Transaction failed for game ${gameId}:`, receipt);
processingGameIds.delete(gameId);
}
} catch (error) {
console.error('Error posting hash:', error);
processingGameIds.delete(gameId);
}
}
// Set up WebSocket subscriptions
async function setupSubscriptions() {
try {
// Subscribe to GameCreated events
contract.events.GameCreated()
.on('data', async (event) => {
try {
const playerAddress = event.returnValues.player;
const gameId = event.returnValues.gameId;
console.log('New game created:', { player: playerAddress, gameId: gameId });
await postHashForPlayer(playerAddress, gameId);
} catch (error) {
console.error('Error processing game created event:', error);
}
})
.on('error', (error) => {
console.error('Error in GameCreated subscription:', error);
});
// Subscribe to GameForfeited events
contract.events.GameForfeited()
.on('data', (event) => {
try {
const playerAddress = event.returnValues.player;
console.log('Game forfeited:', { player: playerAddress });
processingGameIds.delete(playerAddress);
} catch (error) {
console.error('Error processing forfeit event:', error);
}
})
.on('error', (error) => {
console.error('Error in GameForfeited subscription:', error);
});
// Subscribe to connection errors
web3.currentProvider.on('error', (error) => {
console.error('WebSocket connection error:', error);
// Attempt to reconnect after a delay
setTimeout(setupSubscriptions, 5000);
});
// Subscribe to connection close
web3.currentProvider.on('close', () => {
console.log('WebSocket connection closed. Attempting to reconnect...');
// Attempt to reconnect after a delay
setTimeout(setupSubscriptions, 5000);
});
console.log('WebSocket subscriptions set up successfully');
} catch (error) {
console.error('Error setting up WebSocket subscriptions:', error);
// Attempt to reconnect after a delay
setTimeout(setupSubscriptions, 5000);
}
}
// Initialize subscriptions
setupSubscriptions();
// Update health check to show processing game IDs
app.get('/health', (req, res) => {
res.json({
status: 'ok',
houseAddress: houseAccount.address,
processingGameIds: Array.from(processingGameIds)
});
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment