rps-royale/apps/contracts/test/RPSArena.test.ts
Ubuntu 1224ea28a6
Some checks are pending
Deploy to VPS / deploy (push) Waiting to run
feat: integrate blockchain commit-reveal + on-chain match creation
- Update RPSArena.sol: move bet payment to commit() instead of createMatch()
- Fix Hardhat tests to match new contract API (7/7 passing)
- Deploy updated contract to local Hardhat node (0xe7f1725...)
- Add blockchain service (server) listening to contract events
- Create matches on-chain from server matchmaking handler
- Frontend calls contract directly for commit/reveal via MetaMask
- Add Prisma DB persistence for match results and player stats
- Copy ABI to shared package for frontend/server consumption
- Update deployment docs and architecture docs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 03:27:51 +00:00

115 lines
4.3 KiB
TypeScript

import { expect } from 'chai';
import '@nomicfoundation/hardhat-chai-matchers';
import hre from 'hardhat';
import { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';
import { keccak256, solidityPacked } from 'ethers';
describe('RPSArena', () => {
async function deployFixture() {
const [owner, p1, p2] = await hre.ethers.getSigners();
const RPSArena = await hre.ethers.getContractFactory('RPSArena');
const rps = await RPSArena.deploy();
return { rps, owner, p1, p2 };
}
function hashChoice(choice: number, nonce: bigint) {
return keccak256(solidityPacked(['uint256', 'uint256'], [choice, nonce]));
}
describe('Create Match', () => {
it('should create a match with a bet', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await expect(rps.connect(p1).createMatch(p2.address, bet))
.to.emit(rps, 'MatchCreated')
.withArgs(1, p1.address, p2.address, bet);
const m = await rps.matches(1);
expect(m.player1).to.eq(p1.address);
expect(m.bet).to.eq(bet);
});
it('should revert with zero bet', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
await expect(rps.connect(p1).createMatch(p2.address, 0)).to.be.revertedWith('Bet must be > 0');
});
});
describe('Commit & Reveal', () => {
it('should allow commit from both players', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await rps.connect(p1).createMatch(p2.address, bet);
const nonce1 = 123n;
const nonce2 = 456n;
const hash1 = hashChoice(1, nonce1); // Rock
const hash2 = hashChoice(2, nonce2); // Paper
await rps.connect(p1).commit(1, hash1, { value: bet });
await rps.connect(p2).commit(1, hash2, { value: bet });
const m = await rps.matches(1);
expect(m.status).to.eq(1); // Committed
});
it('should finalize with correct winner', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await rps.connect(p1).createMatch(p2.address, bet);
const nonce1 = 123n;
const nonce2 = 456n;
await rps.connect(p1).commit(1, hashChoice(1, nonce1), { value: bet }); // Rock
await rps.connect(p2).commit(1, hashChoice(3, nonce2), { value: bet }); // Scissors
await expect(rps.connect(p1).reveal(1, 1, nonce1))
.to.emit(rps, 'Revealed')
.withArgs(1, p1.address, 1);
await expect(rps.connect(p2).reveal(1, 3, nonce2))
.to.emit(rps, 'MatchFinished')
.withArgs(1, p1.address, hre.ethers.parseEther('0.0194')); // 97% of 0.02
});
it('should reject invalid reveal', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await rps.connect(p1).createMatch(p2.address, bet);
const nonce1 = 123n;
await rps.connect(p1).commit(1, hashChoice(1, nonce1), { value: bet });
await rps.connect(p2).commit(1, hashChoice(2, 456n), { value: bet });
await expect(rps.connect(p1).reveal(1, 2, nonce1)).to.be.revertedWith('Invalid reveal');
});
it('should handle draw', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await rps.connect(p1).createMatch(p2.address, bet);
const nonce = 111n;
await rps.connect(p1).commit(1, hashChoice(1, nonce), { value: bet });
await rps.connect(p2).commit(1, hashChoice(1, nonce), { value: bet });
await rps.connect(p1).reveal(1, 1, nonce);
await expect(rps.connect(p2).reveal(1, 1, nonce))
.to.emit(rps, 'MatchDraw')
.withArgs(1, bet);
});
});
describe('Timeout', () => {
it('should allow timeout claim if no commit', async () => {
const { rps, p1, p2 } = await loadFixture(deployFixture);
const bet = hre.ethers.parseEther('0.01');
await rps.connect(p1).createMatch(p2.address, bet);
await hre.network.provider.send('evm_increaseTime', [301]);
await hre.network.provider.send('evm_mine');
await expect(rps.connect(p1).claimTimeout(1))
.to.emit(rps, 'MatchTimeout');
});
});
});