A decentralized chess platform with fully on-chain move validation, hybrid ETH + CHESS bonding, dispute resolution, on-chain ratings, and token-governed protocol controls.
This repo contains:
ethereum/: Solidity contracts, Truffle migrations, deployment artifacts, and contract testsfrontend/: SvelteKit client, built as a static app for IPFS-style deploymentdocs/: protocol, UX, and mitigation notesdeploy-app/: static deployment artifacts/scripts
At a high level, the system does four things:
- validates chess moves and endgame conditions on-chain
- locks player collateral through a hybrid bond model
- allows post-game cheating disputes through a commit-reveal DAO flow
- tracks token rewards, ELO ratings, and governance on-chain
- The contract suite currently passes locally with
330passing tests. ChessCorewas split to keep runtime size deployable by moving heavy rules logic intoChessRulesEngine.- The frontend is configured for static/IPFS deployment and now lazy-loads ABI-only artifacts.
- The system is still not formally audited.
Two limitations should be stated plainly:
- arbitrator selection is still pseudo-random on-chain, not VRF-backed
PlayerRating.getTopPlayers()is a pagination helper, not a fully sorted on-chain leaderboard
Frontend (SvelteKit static app / IPFS-compatible)
|
v
ChessFactory (EIP-1167 clones)
|
+--> ChessCore + ChessRulesEngine
| |
| +--> BondingManager
| +--> DisputeDAO
| +--> PlayerRating
| +--> RewardPool
|
+--> ChessNFT
Token / Governance Layer
- ChessToken
- ChessGovernor
- ChessTimelock
Dispute Layer
- DisputeDAO
- ArbitratorRegistry
- full on-chain move validation through
ChessCore+ChessRulesEngine - special moves: castling, en passant, promotion
- check, checkmate, stalemate, threefold repetition, 50-move rule, 75-move automatic draw
- three timeout presets:
Finney,Buterin,Nakamoto TournamentandFriendlymodes- unjoined game cancellation after timeout
- dispute-aware settlement for prizes, rewards, and ratings
- hybrid bonding in ETH + CHESS
- commit-reveal arbitrator voting
- dynamic effective quorum based on the selected panel
- up to 3 escalation levels
- slashing and challenger compensation on
Cheatverdicts - arbitrator reputation tracking
CHESSERC20 with vesting and governance hooks- governor + timelock governance flow
- configurable dispute and bonding parameters
- on-chain ELO updates
- player stats and provisional status
- reward pool for play-to-earn payouts
- frontend leaderboard view built from on-chain data, with client-side ordering
| Contract | Responsibility |
|---|---|
ChessCore |
Match lifecycle, moves, settlement, draw flows |
ChessRulesEngine |
Move legality, check/checkmate/stalemate evaluation |
ChessFactory |
Game creation through clone deployment |
ChessNFT |
NFT representation of created matches |
ChessToken |
ERC20 governance / ecosystem token |
BondingManager |
ETH + CHESS bond accounting and locking |
RewardPool |
Reward distribution |
ArbitratorRegistry |
Arbitrator staking, tiering, reputation, selection |
DisputeDAO |
Challenge window, commit-reveal voting, escalation, final decisions |
PlayerRating |
ELO ratings and player stats |
ChessGovernor |
Governance proposals and voting |
ChessTimelock |
Delayed governance execution |
The current frontend wiring targets these chain IDs:
1337/5777: local Ganache84532: Base Sepolia8453: Base mainnet
Older docs and examples still mention Sepolia, Holesky, or Linea in a few places. The frontend stores now use LOCAL, BASE_SEPOLIA, and BASE env names.
- Node.js LTS
- npm
- Ganache or another local EVM RPC for local development
- MetaMask or another injected EVM wallet for frontend testing
git clone https://github.com/jacmos3/ChessGameSolidity.git
cd ChessGameSolidity
cd ethereum
npm install
cd ../frontend
npm installBy default Truffle expects 127.0.0.1:7545.
npx ganache --server.host 127.0.0.1 --server.port 7545 --wallet.totalAccounts 20If you use another port, pass it through LOCAL_RPC_PORT.
cd ethereum
npx truffle migrate --resetThe migration writes the latest addresses to:
Copy frontend/.env.example to frontend/.env, then fill the local addresses from the latest deployment file.
These are the variables the frontend actually reads today:
VITE_CONTRACT_ADDRESS_LOCAL=
VITE_BONDING_MANAGER_LOCAL=
VITE_CHESS_TOKEN_LOCAL=
VITE_DISPUTE_DAO_LOCAL=
VITE_ARBITRATOR_REGISTRY_LOCAL=
VITE_CHESS_GOVERNOR_LOCAL=
VITE_CHESS_TIMELOCK_LOCAL=
VITE_PLAYER_RATING_LOCAL=For Base Sepolia / Base, use the corresponding ..._BASE_SEPOLIA and ..._BASE variables.
cd frontend
npm run devnpm run dev automatically runs npm run sync:abis, so the frontend ABI-only artifacts stay aligned with the latest Solidity build output.
Open the URL shown by Vite, typically http://127.0.0.1:3000/.
cd ethereum
npx truffle testIf your RPC runs on a non-default port:
LOCAL_RPC_PORT=8545 npx truffle testWith gas reporting:
REPORT_GAS=true npx truffle test.
├── README.md
├── deploy-app/
├── docs/
│ ├── ANTI_CHEATING_TOKENOMICS.md
│ ├── USER_GUIDE.md
│ ├── UX_UI_AUDIT_REPORT.md
│ └── VULNERABILITIES_MITIGATIONS.md
├── ethereum/
│ ├── contracts/
│ │ ├── Chess/
│ │ │ ├── ChessBoard.sol
│ │ │ ├── ChessCore.sol
│ │ │ ├── ChessFactory.sol
│ │ │ ├── ChessMediaLibrary.sol
│ │ │ ├── ChessNFT.sol
│ │ │ └── ChessRulesEngine.sol
│ │ ├── DAO/
│ │ ├── Governance/
│ │ ├── Rating/
│ │ └── Token/
│ ├── deployments/
│ ├── flattened/
│ ├── migrations/
│ ├── scripts/
│ └── test/
└── frontend/
├── scripts/
│ └── extract-abis.mjs
├── src/
│ ├── lib/
│ │ ├── components/
│ │ ├── contracts/
│ │ │ ├── abi/
│ │ │ └── loadAbi.js
│ │ └── stores/
│ └── routes/
└── static/
function createChessGame(
uint8 _timeoutPreset,
uint8 _gameMode
) external payable returns (address);
function getDeployedChessGames() external view returns (address[] memory);function joinGameAsBlack() external payable;
function makeMove(uint8 startX, uint8 startY, uint8 endX, uint8 endY) external;
function makeMoveWithPromotion(
uint8 startX,
uint8 startY,
uint8 endX,
uint8 endY,
int8 promotionPiece
) external;
function resign() external;
function canClaimPrize() external view returns (bool);
function claimPrize() external;
function finalizePrizes() external;
function withdrawPrize() external;
function cancelUnjoinedGame() external;
function offerDraw() external;
function acceptDraw() external;
function claimDrawByRepetition() external;
function claimDrawByFiftyMoveRule() external;Important settlement note:
- decisive, claimable results can use
claimPrize() - draws and dispute-aware settlement use
finalizePrizes()+withdrawPrize()
function challenge(uint256 gameId, address accusedPlayer) external;
function getChallengeWindowRemaining(uint256 gameId) external view returns (uint256);
function getEffectiveQuorum(uint256 disputeId) external view returns (uint256);
function getSelectedArbitrators(uint256 disputeId) external view returns (address[] memory);- SvelteKit
1.30.4 - Svelte
4.2.8 - Vite
4.5.2 - Tailwind CSS
3.4.0 - ethers.js
5.7.2 - chess.js
1.0.0-beta.8 @sveltejs/adapter-staticfor IPFS-compatible static builds
Implemented protections include:
- reentrancy protection on fund-moving flows
- role-based access control
- challenge windows and commit / reveal deadlines
- dispute max duration cap
- bond locking and slashing
- custom errors for lower revert overhead
Known limitations:
- no formal external audit yet
- arbitrator selection is not VRF-backed
- local frontend config still depends on manual env address wiring
- some older docs mention outdated network names and should be cleaned up separately
- Create a branch from
dev - Run the contract suite before pushing
- Keep ABI artifacts and frontend wiring aligned with contract changes
- Prefer fixing stale docs when protocol behavior changes
Repository-wide licensing still needs cleanup.
- most Solidity files use
SPDX-License-Identifier: MIT - ethereum/package.json currently declares
ISC - the repo does not currently ship a top-level
LICENSEfile
If this project is meant to be distributed publicly, add a single root license file and align the package manifests with it.