diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 944aec2..09faf64 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -53,6 +53,7 @@ jobs: - name: Test env: DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/test_db + USE_CONTRACT: false run: pytest -v --cov=app --cov-report=xml - name: Upload coverage diff --git a/backend/.coverage b/backend/.coverage deleted file mode 100644 index 283f9f9..0000000 Binary files a/backend/.coverage and /dev/null differ diff --git a/backend/app/api/init.py b/backend/app/api/init.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/api/routers/tournament.py b/backend/app/api/routers/tournament.py index 71d6349..bb42d54 100644 --- a/backend/app/api/routers/tournament.py +++ b/backend/app/api/routers/tournament.py @@ -1,53 +1,99 @@ +import os +from uuid import UUID + from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select -from uuid import UUID +from sqlalchemy.ext.asyncio import AsyncSession from backend.app.db.database import get_db -from backend.app.db.models import Tournament +from backend.app.db.models import Tournament, Agent from backend.app.schemas.tournament import ( TournamentCreate, TournamentUpdate, TournamentResponse, ) from backend.app.api.deps import require_admin +from backend.app.services import contract_service +from backend.app.services.contract_service import ContractTransactionError router = APIRouter() +async def validate_agents(session: AsyncSession, agent_ids: list[UUID]) -> list[Agent]: + """Validate that all provided agent_ids exist in the DB.""" + if not agent_ids: + raise HTTPException(status_code=400, detail="At least one agent is required") + + stmt = select(Agent).where(Agent.id.in_(agent_ids)) + result = await session.execute(stmt) + agents = result.scalars().all() + + if len(agents) != len(agent_ids): + raise HTTPException(status_code=400, detail="One or more agents not found") + + return agents @router.get("/", response_model=list[TournamentResponse]) async def list_tournaments(session: AsyncSession = Depends(get_db)): - """GET route for list of tournaments""" statement = select(Tournament) result = await session.execute(statement) tournaments = result.scalars().all() return tournaments - @router.get("/{tournament_id}", response_model=TournamentResponse) -async def get_tournament(tournament_id: UUID, session: AsyncSession = Depends(get_db)): - """GET route for tournament by tournament_id""" +async def get_tournament( + tournament_id: UUID, + session: AsyncSession = Depends(get_db), +): tournament = await session.get(Tournament, tournament_id) if not tournament: raise HTTPException(status_code=404, detail="Tournament Not Found") return tournament - @router.post("/", response_model=TournamentResponse, status_code=201) async def create_tournament( tournament_data: TournamentCreate, session: AsyncSession = Depends(get_db), admin: dict = Depends(require_admin), ): - """POST route to create a new tournament""" - # Create tournament from schema - tournament = Tournament(**tournament_data.model_dump()) + agents = await validate_agents(session, tournament_data.agent_ids) + + tournament = Tournament( + name=tournament_data.name, + status=tournament_data.status, + start_date=tournament_data.start_date, + end_date=tournament_data.end_date, + prize_pool=tournament_data.prize_pool, + ) + + tournament.agent_contract_mapping = { + str(agent.id): idx + 1 for idx, agent in enumerate(agents) + } + tournament.contract_status = "PENDING" session.add(tournament) await session.commit() await session.refresh(tournament) - return tournament + use_contract = os.getenv("USE_CONTRACT", "true").lower() == "true" + + if use_contract: + try: + contract_id = await contract_service.create_tournament_on_contract( + agent_count=len(agents) + ) + tournament.contract_tournament_id = contract_id + tournament.contract_status = "ACTIVE" + await session.commit() + await session.refresh(tournament) + except ContractTransactionError as e: + tournament.contract_status = "FAILED" + await session.commit() + raise HTTPException( + status_code=500, + detail=f"Contract creation failed: {e}", + ) + + return tournament @router.put("/{tournament_id}", response_model=TournamentResponse) async def update_tournament( @@ -56,14 +102,11 @@ async def update_tournament( session: AsyncSession = Depends(get_db), admin: dict = Depends(require_admin), ): - """PUT route for updating a tournament""" db_tournament = await session.get(Tournament, tournament_id) if not db_tournament: raise HTTPException(status_code=404, detail="Tournament Not Found") - # Update only provided fields update_data = tournament_data.model_dump(exclude_unset=True) - for key, value in update_data.items(): setattr(db_tournament, key, value) @@ -73,14 +116,12 @@ async def update_tournament( return db_tournament - @router.delete("/{tournament_id}") async def delete_tournament( tournament_id: UUID, session: AsyncSession = Depends(get_db), admin: dict = Depends(require_admin), ): - """DELETE route for deleting a tournament""" tournament = await session.get(Tournament, tournament_id) if not tournament: raise HTTPException(status_code=404, detail="Tournament Not Found") @@ -90,3 +131,71 @@ async def delete_tournament( return {"message": f"Tournament {tournament_id} deleted successfully"} +# Settlement logic +async def determine_winner(tournament_id: UUID) -> UUID: + raise NotImplementedError("determine_winner logic not implemented yet") + +@router.post("/{tournament_id}/settle", response_model=TournamentResponse) +async def settle_tournament( + tournament_id: UUID, + session: AsyncSession = Depends(get_db), + admin: dict = Depends(require_admin), +): + tournament = await session.get(Tournament, tournament_id) + if not tournament or not tournament.contract_tournament_id: + raise HTTPException(status_code=404, detail="Tournament not found") + + if tournament.contract_status != "ACTIVE": + raise HTTPException(status_code=400, detail="Tournament not active") + + try: + await contract_service.close_betting(tournament.contract_tournament_id) + except ContractTransactionError as e: + raise HTTPException(status_code=500, detail=f"Close betting failed: {e}") + + winner_uuid = await determine_winner(tournament_id) + if not tournament.agent_contract_mapping: + raise HTTPException(status_code=500, detail="Agent contract mapping missing") + + try: + winner_contract_id = tournament.agent_contract_mapping[str(winner_uuid)] + except KeyError: + raise HTTPException( + status_code=500, + detail="Winner not found in agent_contract_mapping", + ) + + try: + await contract_service.settle_tournament( + tournament.contract_tournament_id, + winner_contract_id, + ) + + tournament.winner_agent_id = winner_uuid + tournament.contract_status = "COMPLETED" + await session.commit() + await session.refresh(tournament) + return tournament + except ContractTransactionError as e: + raise HTTPException(status_code=500, detail=f"Settlement failed: {e}") + +@router.post("/{tournament_id}/cancel", response_model=TournamentResponse) +async def cancel_tournament( + tournament_id: UUID, + session: AsyncSession = Depends(get_db), + admin: dict = Depends(require_admin), +): + tournament = await session.get(Tournament, tournament_id) + if not tournament or not tournament.contract_tournament_id: + raise HTTPException(status_code=404, detail="Tournament not found") + + try: + await contract_service.cancel_tournament(tournament.contract_tournament_id) + except ContractTransactionError as e: + raise HTTPException(status_code=500, detail=f"Cancel failed: {e}") + + tournament.contract_status = "CANCELLED" + await session.commit() + await session.refresh(tournament) + + return tournament diff --git a/backend/app/contracts/AgonusBetting.json b/backend/app/contracts/AgonusBetting.json new file mode 100644 index 0000000..3377138 --- /dev/null +++ b/backend/app/contracts/AgonusBetting.json @@ -0,0 +1,587 @@ +{ + "_format": "hh3-artifact-1", + "contractName": "AgonusBetting", + "sourceName": "contracts/AgonusBetting.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "BetPlaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + } + ], + "name": "BettingClosed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + } + ], + "name": "TournamentCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "agentCount", + "type": "uint256" + } + ], + "name": "TournamentCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "winningAgentId", + "type": "uint256" + } + ], + "name": "TournamentSettled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "WinningsClaimed", + "type": "event" + }, + { + "inputs": [], + "name": "BP_DIVISOR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_BET", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PLATFORM_FEE_BP", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "agentPools", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "calculatePayout", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + } + ], + "name": "cancelTournament", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + } + ], + "name": "claimWinnings", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + } + ], + "name": "closeBetting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "agentCount", + "type": "uint256" + } + ], + "name": "createTournament", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + } + ], + "name": "getAgentOdds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + } + ], + "name": "getUserBetOnAgent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "hasClaimed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTournamentId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + } + ], + "name": "placeBet", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "platformWallet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newWallet", + "type": "address" + } + ], + "name": "setPlatformWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tournamentId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "winningAgentId", + "type": "uint256" + } + ], + "name": "settleTournament", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "tournaments", + "outputs": [ + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isSettled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "totalPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "winningAgentId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "agentCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "userBets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561000f575f5ffd5b50335f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610081575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161007891906101dd565b60405180910390fd5b610090816100dd60201b60201c565b50600180819055503360025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101f6565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6101c78261019e565b9050919050565b6101d7816101bd565b82525050565b5f6020820190506101f05f8301846101ce565b92915050565b61270e806102035f395ff3fe608060405260043610610138575f3560e01c806377dede18116100aa578063b4ad06a11161006e578063b4ad06a114610462578063b726be7f1461048a578063dd20a53e146104c6578063de7adf6e14610502578063f2fde38b1461052c578063fa2af9da1461055457610178565b806377dede1814610382578063852efc3d146103aa578063873f6f9e146103d45780638831e9cf146104105780638da5cb5b1461043857610178565b80636540742f116100fc5780636540742f14610274578063677bd9ff1461029e578063715018a6146102c65780637284affc146102dc5780637503e1b714610318578063752f74661461035857610178565b806311275eda1461017c57806332432233146101b85780633e7dbd74146101e05780634afe62b51461021c57806361c4cc151461023857610178565b36610178576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161016f90611b6b565b60405180910390fd5b5f5ffd5b348015610187575f5ffd5b506101a2600480360381019061019d9190611c1a565b61057e565b6040516101af9190611c79565b60405180910390f35b3480156101c3575f5ffd5b506101de60048036038101906101d99190611c92565b6105e4565b005b3480156101eb575f5ffd5b5061020660048036038101906102019190611cbd565b6106fe565b6040516102139190611c79565b60405180910390f35b61023660048036038101906102319190611cbd565b610804565b005b348015610243575f5ffd5b5061025e60048036038101906102599190611c1a565b610a70565b60405161026b9190611c79565b60405180910390f35b34801561027f575f5ffd5b50610288610a9b565b6040516102959190611c79565b60405180910390f35b3480156102a9575f5ffd5b506102c460048036038101906102bf9190611c92565b610aa6565b005b3480156102d1575f5ffd5b506102da610f59565b005b3480156102e7575f5ffd5b5061030260048036038101906102fd9190611cbd565b610f6c565b60405161030f9190611c79565b60405180910390f35b348015610323575f5ffd5b5061033e60048036038101906103399190611c92565b610f8c565b60405161034f959493929190611d15565b60405180910390f35b348015610363575f5ffd5b5061036c610fd6565b6040516103799190611c79565b60405180910390f35b34801561038d575f5ffd5b506103a860048036038101906103a39190611cbd565b610fdc565b005b3480156103b5575f5ffd5b506103be611338565b6040516103cb9190611c79565b60405180910390f35b3480156103df575f5ffd5b506103fa60048036038101906103f59190611d66565b61133e565b6040516104079190611da4565b60405180910390f35b34801561041b575f5ffd5b5061043660048036038101906104319190611dbd565b611368565b005b348015610443575f5ffd5b5061044c611421565b6040516104599190611df7565b60405180910390f35b34801561046d575f5ffd5b5061048860048036038101906104839190611c92565b611448565b005b348015610495575f5ffd5b506104b060048036038101906104ab9190611d66565b611544565b6040516104bd9190611c79565b60405180910390f35b3480156104d1575f5ffd5b506104ec60048036038101906104e79190611c92565b611776565b6040516104f99190611c79565b60405180910390f35b34801561050d575f5ffd5b506105166118c4565b6040516105239190611c79565b60405180910390f35b348015610537575f5ffd5b50610552600480360381019061054d9190611dbd565b6118ca565b005b34801561055f575f5ffd5b5061056861194e565b6040516105759190611df7565b60405180910390f35b5f60065f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8381526020019081526020015f205490509392505050565b6105ec611973565b5f60045f8381526020019081526020015f2090505f816003015411610646576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161063d90611e5a565b60405180910390fd5b805f0160019054906101000a900460ff1615610697576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068e90611ec2565b60405180910390fd5b5f815f015f6101000a81548160ff0219169083151502179055506001815f0160016101000a81548160ff021916908315150217905550817ffa61ec8d7e5a58ceba17772b10ba0c6caa65b40b200302be35f00efc264c789560405160405180910390a25050565b5f5f60045f8581526020019081526020015f2090505f83118015610726575080600301548311155b610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161075c90611f2a565b60405180910390fd5b5f81600101540361077b576127109150506107fe565b5f60055f8681526020019081526020015f205f8581526020019081526020015f205490505f81036107b0575f925050506107fe565b5f6127106101f46127106107c49190611f75565b84600101546107d39190611fa8565b6107dd9190612016565b905081612710826107ee9190611fa8565b6107f89190612016565b93505050505b92915050565b61080c6119fa565b5f60045f8481526020019081526020015f2090505f816003015411610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d90611e5a565b60405180910390fd5b805f015f9054906101000a900460ff166108b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108ac90612090565b60405180910390fd5b66038d7ea4c680003410156108ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108f6906120f8565b60405180910390fd5b5f82118015610912575080600301548211155b610951576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094890611f2a565b60405180910390fd5b34816001015f8282546109649190612116565b925050819055503460055f8581526020019081526020015f205f8481526020019081526020015f205f82825461099a9190612116565b925050819055503460065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8481526020019081526020015f205f828254610a0b9190612116565b92505081905550823373ffffffffffffffffffffffffffffffffffffffff167f7363e6581df4db69463222156be4a09656528b9f1302890fa4c0b60819b69fc68434604051610a5b929190612149565b60405180910390a350610a6c611a40565b5050565b6006602052825f5260405f20602052815f5260405f20602052805f5260405f205f9250925050505481565b66038d7ea4c6800081565b610aae6119fa565b5f60045f8381526020019081526020015f2090505f816003015411610b08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aff90611e5a565b60405180910390fd5b805f0160019054906101000a900460ff16610b58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4f906121ba565b60405180910390fd5b60075f8381526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1615610bf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610be890612222565b60405180910390fd5b5f5f90505f826002015403610c93575f600190505b82600301548111610c8d5760065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8281526020019081526020015f205482610c789190612116565b91508080610c8590612240565b915050610c06565b50610dae565b5f60065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f846002015481526020019081526020015f205490505f8111610d38576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d2f906122d1565b60405180910390fd5b5f60055f8681526020019081526020015f205f856002015481526020019081526020015f205490505f6127106101f48660010154610d769190611fa8565b610d809190612016565b8560010154610d8f9190611f75565b9050818184610d9e9190611fa8565b610da89190612016565b93505050505b5f8111610df0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610de790612339565b60405180910390fd5b600160075f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505f3373ffffffffffffffffffffffffffffffffffffffff1682604051610e7990612384565b5f6040518083038185875af1925050503d805f8114610eb3576040519150601f19603f3d011682016040523d82523d5f602084013e610eb8565b606091505b5050905080610efc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ef3906123e2565b60405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff167f7472e24de4628e34b41a2aa1688ed4b46a9145b2de1cf93d74902473ccc1740d84604051610f439190611c79565b60405180910390a3505050610f56611a40565b50565b610f61611973565b610f6a5f611a49565b565b6005602052815f5260405f20602052805f5260405f205f91509150505481565b6004602052805f5260405f205f91509050805f015f9054906101000a900460ff1690805f0160019054906101000a900460ff16908060010154908060020154908060030154905085565b61271081565b610fe4611973565b610fec6119fa565b5f60045f8481526020019081526020015f2090505f816003015411611046576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161103d90611e5a565b60405180910390fd5b805f015f9054906101000a900460ff1615611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108d9061244a565b60405180910390fd5b805f0160019054906101000a900460ff16156110e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110de90611ec2565b60405180910390fd5b5f81600101541161112d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611124906124b2565b60405180910390fd5b5f82118015611140575080600301548211155b61117f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117690611f2a565b60405180910390fd5b5f60055f8581526020019081526020015f205f8481526020019081526020015f2054116111e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111d89061251a565b60405180910390fd5b6001815f0160016101000a81548160ff0219169083151502179055508181600201819055505f6127106101f4836001015461121c9190611fa8565b6112269190612016565b90505f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260405161126e90612384565b5f6040518083038185875af1925050503d805f81146112a8576040519150601f19603f3d011682016040523d82523d5f602084013e6112ad565b606091505b50509050806112f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112e890612582565b60405180910390fd5b847f7fcf95b063f289ddbca75027b7eb7a5c5f99864b45e472f9b5371b99c64e2e49856040516113219190611c79565b60405180910390a2505050611334611a40565b5050565b60035481565b6007602052815f5260405f20602052805f5260405f205f915091509054906101000a900460ff1681565b611370611973565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036113de576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d5906125ea565b60405180910390fd5b8060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611450611973565b5f60045f8381526020019081526020015f2090505f8160030154116114aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114a190611e5a565b60405180910390fd5b805f015f9054906101000a900460ff166114f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f090612652565b60405180910390fd5b5f815f015f6101000a81548160ff021916908315150217905550817feb7ecc3aa4dd407c7207677a2cbf861441d5c5e6c0dc70ad9692a980385c20d660405160405180910390a25050565b5f5f60045f8581526020019081526020015f209050805f0160019054906101000a900460ff16611577575f915050611770565b60075f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16156115de575f915050611770565b5f816002015403611685575f5f90505f600190505b8260030154811161167a5760065f8781526020019081526020015f205f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8281526020019081526020015f2054826116659190612116565b9150808061167290612240565b9150506115f3565b508092505050611770565b5f60065f8681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f836002015481526020019081526020015f205490505f81036116f9575f92505050611770565b5f60055f8781526020019081526020015f205f846002015481526020019081526020015f205490505f6127106101f485600101546117379190611fa8565b6117419190612016565b84600101546117509190611f75565b905081818461175f9190611fa8565b6117699190612016565b9450505050505b92915050565b5f61177f611973565b60028210158015611791575060648211155b6117d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117c7906126ba565b60405180910390fd5b5f60035f8154809291906117e390612240565b9190505590506040518060a001604052806001151581526020015f151581526020015f81526020015f81526020018481525060045f8381526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff021916908315150217905550604082015181600101556060820151816002015560808201518160030155905050807fcbdd4006f4a0ca378f1c5d1f11060ba15bc180a12590dd3ea3c219454582939e846040516118b39190611c79565b60405180910390a280915050919050565b6101f481565b6118d2611973565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611942575f6040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016119399190611df7565b60405180910390fd5b61194b81611a49565b50565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61197b611b0a565b73ffffffffffffffffffffffffffffffffffffffff16611999611421565b73ffffffffffffffffffffffffffffffffffffffff16146119f8576119bc611b0a565b6040517f118cdaa70000000000000000000000000000000000000000000000000000000081526004016119ef9190611df7565b60405180910390fd5b565b600260015403611a36576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600181905550565b60018081905550565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f33905090565b5f82825260208201905092915050565b7f55736520706c6163654265742066756e6374696f6e00000000000000000000005f82015250565b5f611b55601583611b11565b9150611b6082611b21565b602082019050919050565b5f6020820190508181035f830152611b8281611b49565b9050919050565b5f5ffd5b5f819050919050565b611b9f81611b8d565b8114611ba9575f5ffd5b50565b5f81359050611bba81611b96565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611be982611bc0565b9050919050565b611bf981611bdf565b8114611c03575f5ffd5b50565b5f81359050611c1481611bf0565b92915050565b5f5f5f60608486031215611c3157611c30611b89565b5b5f611c3e86828701611bac565b9350506020611c4f86828701611c06565b9250506040611c6086828701611bac565b9150509250925092565b611c7381611b8d565b82525050565b5f602082019050611c8c5f830184611c6a565b92915050565b5f60208284031215611ca757611ca6611b89565b5b5f611cb484828501611bac565b91505092915050565b5f5f60408385031215611cd357611cd2611b89565b5b5f611ce085828601611bac565b9250506020611cf185828601611bac565b9150509250929050565b5f8115159050919050565b611d0f81611cfb565b82525050565b5f60a082019050611d285f830188611d06565b611d356020830187611d06565b611d426040830186611c6a565b611d4f6060830185611c6a565b611d5c6080830184611c6a565b9695505050505050565b5f5f60408385031215611d7c57611d7b611b89565b5b5f611d8985828601611bac565b9250506020611d9a85828601611c06565b9150509250929050565b5f602082019050611db75f830184611d06565b92915050565b5f60208284031215611dd257611dd1611b89565b5b5f611ddf84828501611c06565b91505092915050565b611df181611bdf565b82525050565b5f602082019050611e0a5f830184611de8565b92915050565b7f546f75726e616d656e7420646f6573206e6f74206578697374000000000000005f82015250565b5f611e44601983611b11565b9150611e4f82611e10565b602082019050919050565b5f6020820190508181035f830152611e7181611e38565b9050919050565b7f416c726561647920736574746c656400000000000000000000000000000000005f82015250565b5f611eac600f83611b11565b9150611eb782611e78565b602082019050919050565b5f6020820190508181035f830152611ed981611ea0565b9050919050565b7f496e76616c6964206167656e74204944000000000000000000000000000000005f82015250565b5f611f14601083611b11565b9150611f1f82611ee0565b602082019050919050565b5f6020820190508181035f830152611f4181611f08565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611f7f82611b8d565b9150611f8a83611b8d565b9250828203905081811115611fa257611fa1611f48565b5b92915050565b5f611fb282611b8d565b9150611fbd83611b8d565b9250828202611fcb81611b8d565b91508282048414831517611fe257611fe1611f48565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61202082611b8d565b915061202b83611b8d565b92508261203b5761203a611fe9565b5b828204905092915050565b7f546f75726e616d656e74206e6f742061637469766500000000000000000000005f82015250565b5f61207a601583611b11565b915061208582612046565b602082019050919050565b5f6020820190508181035f8301526120a78161206e565b9050919050565b7f4265742062656c6f77206d696e696d756d0000000000000000000000000000005f82015250565b5f6120e2601183611b11565b91506120ed826120ae565b602082019050919050565b5f6020820190508181035f83015261210f816120d6565b9050919050565b5f61212082611b8d565b915061212b83611b8d565b925082820190508082111561214357612142611f48565b5b92915050565b5f60408201905061215c5f830185611c6a565b6121696020830184611c6a565b9392505050565b7f546f75726e616d656e74206e6f7420736574746c6564000000000000000000005f82015250565b5f6121a4601683611b11565b91506121af82612170565b602082019050919050565b5f6020820190508181035f8301526121d181612198565b9050919050565b7f416c726561647920636c61696d656400000000000000000000000000000000005f82015250565b5f61220c600f83611b11565b9150612217826121d8565b602082019050919050565b5f6020820190508181035f83015261223981612200565b9050919050565b5f61224a82611b8d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361227c5761227b611f48565b5b600182019050919050565b7f4e6f2077696e6e696e67206265747300000000000000000000000000000000005f82015250565b5f6122bb600f83611b11565b91506122c682612287565b602082019050919050565b5f6020820190508181035f8301526122e8816122af565b9050919050565b7f4e6f2077696e6e696e677320746f20636c61696d0000000000000000000000005f82015250565b5f612323601483611b11565b915061232e826122ef565b602082019050919050565b5f6020820190508181035f83015261235081612317565b9050919050565b5f81905092915050565b50565b5f61236f5f83612357565b915061237a82612361565b5f82019050919050565b5f61238e82612364565b9150819050919050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6123cc600f83611b11565b91506123d782612398565b602082019050919050565b5f6020820190508181035f8301526123f9816123c0565b9050919050565b7f42657474696e67207374696c6c206163746976650000000000000000000000005f82015250565b5f612434601483611b11565b915061243f82612400565b602082019050919050565b5f6020820190508181035f83015261246181612428565b9050919050565b7f4e6f206265747320706c616365640000000000000000000000000000000000005f82015250565b5f61249c600e83611b11565b91506124a782612468565b602082019050919050565b5f6020820190508181035f8301526124c981612490565b9050919050565b7f57696e6e696e67206167656e7420686173206e6f2062657473000000000000005f82015250565b5f612504601983611b11565b915061250f826124d0565b602082019050919050565b5f6020820190508181035f830152612531816124f8565b9050919050565b7f466565207472616e73666572206661696c6564000000000000000000000000005f82015250565b5f61256c601383611b11565b915061257782612538565b602082019050919050565b5f6020820190508181035f83015261259981612560565b9050919050565b7f496e76616c6964206164647265737300000000000000000000000000000000005f82015250565b5f6125d4600f83611b11565b91506125df826125a0565b602082019050919050565b5f6020820190508181035f830152612601816125c8565b9050919050565b7f416c726561647920636c6f7365640000000000000000000000000000000000005f82015250565b5f61263c600e83611b11565b915061264782612608565b602082019050919050565b5f6020820190508181035f83015261266981612630565b9050919050565b7f496e76616c6964206167656e7420636f756e74000000000000000000000000005f82015250565b5f6126a4601383611b11565b91506126af82612670565b602082019050919050565b5f6020820190508181035f8301526126d181612698565b905091905056fea2646970667358221220604aa9027b7ab221bf9935ecd97a2870b47fa01e1fb82553ecfa8a3cc38e6ad964736f6c634300081c0033", + "deployedBytecode": "0x608060405260043610610138575f3560e01c806377dede18116100aa578063b4ad06a11161006e578063b4ad06a114610462578063b726be7f1461048a578063dd20a53e146104c6578063de7adf6e14610502578063f2fde38b1461052c578063fa2af9da1461055457610178565b806377dede1814610382578063852efc3d146103aa578063873f6f9e146103d45780638831e9cf146104105780638da5cb5b1461043857610178565b80636540742f116100fc5780636540742f14610274578063677bd9ff1461029e578063715018a6146102c65780637284affc146102dc5780637503e1b714610318578063752f74661461035857610178565b806311275eda1461017c57806332432233146101b85780633e7dbd74146101e05780634afe62b51461021c57806361c4cc151461023857610178565b36610178576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161016f90611b6b565b60405180910390fd5b5f5ffd5b348015610187575f5ffd5b506101a2600480360381019061019d9190611c1a565b61057e565b6040516101af9190611c79565b60405180910390f35b3480156101c3575f5ffd5b506101de60048036038101906101d99190611c92565b6105e4565b005b3480156101eb575f5ffd5b5061020660048036038101906102019190611cbd565b6106fe565b6040516102139190611c79565b60405180910390f35b61023660048036038101906102319190611cbd565b610804565b005b348015610243575f5ffd5b5061025e60048036038101906102599190611c1a565b610a70565b60405161026b9190611c79565b60405180910390f35b34801561027f575f5ffd5b50610288610a9b565b6040516102959190611c79565b60405180910390f35b3480156102a9575f5ffd5b506102c460048036038101906102bf9190611c92565b610aa6565b005b3480156102d1575f5ffd5b506102da610f59565b005b3480156102e7575f5ffd5b5061030260048036038101906102fd9190611cbd565b610f6c565b60405161030f9190611c79565b60405180910390f35b348015610323575f5ffd5b5061033e60048036038101906103399190611c92565b610f8c565b60405161034f959493929190611d15565b60405180910390f35b348015610363575f5ffd5b5061036c610fd6565b6040516103799190611c79565b60405180910390f35b34801561038d575f5ffd5b506103a860048036038101906103a39190611cbd565b610fdc565b005b3480156103b5575f5ffd5b506103be611338565b6040516103cb9190611c79565b60405180910390f35b3480156103df575f5ffd5b506103fa60048036038101906103f59190611d66565b61133e565b6040516104079190611da4565b60405180910390f35b34801561041b575f5ffd5b5061043660048036038101906104319190611dbd565b611368565b005b348015610443575f5ffd5b5061044c611421565b6040516104599190611df7565b60405180910390f35b34801561046d575f5ffd5b5061048860048036038101906104839190611c92565b611448565b005b348015610495575f5ffd5b506104b060048036038101906104ab9190611d66565b611544565b6040516104bd9190611c79565b60405180910390f35b3480156104d1575f5ffd5b506104ec60048036038101906104e79190611c92565b611776565b6040516104f99190611c79565b60405180910390f35b34801561050d575f5ffd5b506105166118c4565b6040516105239190611c79565b60405180910390f35b348015610537575f5ffd5b50610552600480360381019061054d9190611dbd565b6118ca565b005b34801561055f575f5ffd5b5061056861194e565b6040516105759190611df7565b60405180910390f35b5f60065f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8381526020019081526020015f205490509392505050565b6105ec611973565b5f60045f8381526020019081526020015f2090505f816003015411610646576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161063d90611e5a565b60405180910390fd5b805f0160019054906101000a900460ff1615610697576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068e90611ec2565b60405180910390fd5b5f815f015f6101000a81548160ff0219169083151502179055506001815f0160016101000a81548160ff021916908315150217905550817ffa61ec8d7e5a58ceba17772b10ba0c6caa65b40b200302be35f00efc264c789560405160405180910390a25050565b5f5f60045f8581526020019081526020015f2090505f83118015610726575080600301548311155b610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161075c90611f2a565b60405180910390fd5b5f81600101540361077b576127109150506107fe565b5f60055f8681526020019081526020015f205f8581526020019081526020015f205490505f81036107b0575f925050506107fe565b5f6127106101f46127106107c49190611f75565b84600101546107d39190611fa8565b6107dd9190612016565b905081612710826107ee9190611fa8565b6107f89190612016565b93505050505b92915050565b61080c6119fa565b5f60045f8481526020019081526020015f2090505f816003015411610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d90611e5a565b60405180910390fd5b805f015f9054906101000a900460ff166108b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108ac90612090565b60405180910390fd5b66038d7ea4c680003410156108ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108f6906120f8565b60405180910390fd5b5f82118015610912575080600301548211155b610951576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094890611f2a565b60405180910390fd5b34816001015f8282546109649190612116565b925050819055503460055f8581526020019081526020015f205f8481526020019081526020015f205f82825461099a9190612116565b925050819055503460065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8481526020019081526020015f205f828254610a0b9190612116565b92505081905550823373ffffffffffffffffffffffffffffffffffffffff167f7363e6581df4db69463222156be4a09656528b9f1302890fa4c0b60819b69fc68434604051610a5b929190612149565b60405180910390a350610a6c611a40565b5050565b6006602052825f5260405f20602052815f5260405f20602052805f5260405f205f9250925050505481565b66038d7ea4c6800081565b610aae6119fa565b5f60045f8381526020019081526020015f2090505f816003015411610b08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aff90611e5a565b60405180910390fd5b805f0160019054906101000a900460ff16610b58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4f906121ba565b60405180910390fd5b60075f8381526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1615610bf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610be890612222565b60405180910390fd5b5f5f90505f826002015403610c93575f600190505b82600301548111610c8d5760065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8281526020019081526020015f205482610c789190612116565b91508080610c8590612240565b915050610c06565b50610dae565b5f60065f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f846002015481526020019081526020015f205490505f8111610d38576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d2f906122d1565b60405180910390fd5b5f60055f8681526020019081526020015f205f856002015481526020019081526020015f205490505f6127106101f48660010154610d769190611fa8565b610d809190612016565b8560010154610d8f9190611f75565b9050818184610d9e9190611fa8565b610da89190612016565b93505050505b5f8111610df0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610de790612339565b60405180910390fd5b600160075f8581526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505f3373ffffffffffffffffffffffffffffffffffffffff1682604051610e7990612384565b5f6040518083038185875af1925050503d805f8114610eb3576040519150601f19603f3d011682016040523d82523d5f602084013e610eb8565b606091505b5050905080610efc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ef3906123e2565b60405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff167f7472e24de4628e34b41a2aa1688ed4b46a9145b2de1cf93d74902473ccc1740d84604051610f439190611c79565b60405180910390a3505050610f56611a40565b50565b610f61611973565b610f6a5f611a49565b565b6005602052815f5260405f20602052805f5260405f205f91509150505481565b6004602052805f5260405f205f91509050805f015f9054906101000a900460ff1690805f0160019054906101000a900460ff16908060010154908060020154908060030154905085565b61271081565b610fe4611973565b610fec6119fa565b5f60045f8481526020019081526020015f2090505f816003015411611046576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161103d90611e5a565b60405180910390fd5b805f015f9054906101000a900460ff1615611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108d9061244a565b60405180910390fd5b805f0160019054906101000a900460ff16156110e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110de90611ec2565b60405180910390fd5b5f81600101541161112d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611124906124b2565b60405180910390fd5b5f82118015611140575080600301548211155b61117f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117690611f2a565b60405180910390fd5b5f60055f8581526020019081526020015f205f8481526020019081526020015f2054116111e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111d89061251a565b60405180910390fd5b6001815f0160016101000a81548160ff0219169083151502179055508181600201819055505f6127106101f4836001015461121c9190611fa8565b6112269190612016565b90505f60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260405161126e90612384565b5f6040518083038185875af1925050503d805f81146112a8576040519150601f19603f3d011682016040523d82523d5f602084013e6112ad565b606091505b50509050806112f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112e890612582565b60405180910390fd5b847f7fcf95b063f289ddbca75027b7eb7a5c5f99864b45e472f9b5371b99c64e2e49856040516113219190611c79565b60405180910390a2505050611334611a40565b5050565b60035481565b6007602052815f5260405f20602052805f5260405f205f915091509054906101000a900460ff1681565b611370611973565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036113de576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d5906125ea565b60405180910390fd5b8060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611450611973565b5f60045f8381526020019081526020015f2090505f8160030154116114aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114a190611e5a565b60405180910390fd5b805f015f9054906101000a900460ff166114f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114f090612652565b60405180910390fd5b5f815f015f6101000a81548160ff021916908315150217905550817feb7ecc3aa4dd407c7207677a2cbf861441d5c5e6c0dc70ad9692a980385c20d660405160405180910390a25050565b5f5f60045f8581526020019081526020015f209050805f0160019054906101000a900460ff16611577575f915050611770565b60075f8581526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16156115de575f915050611770565b5f816002015403611685575f5f90505f600190505b8260030154811161167a5760065f8781526020019081526020015f205f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8281526020019081526020015f2054826116659190612116565b9150808061167290612240565b9150506115f3565b508092505050611770565b5f60065f8681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f836002015481526020019081526020015f205490505f81036116f9575f92505050611770565b5f60055f8781526020019081526020015f205f846002015481526020019081526020015f205490505f6127106101f485600101546117379190611fa8565b6117419190612016565b84600101546117509190611f75565b905081818461175f9190611fa8565b6117699190612016565b9450505050505b92915050565b5f61177f611973565b60028210158015611791575060648211155b6117d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117c7906126ba565b60405180910390fd5b5f60035f8154809291906117e390612240565b9190505590506040518060a001604052806001151581526020015f151581526020015f81526020015f81526020018481525060045f8381526020019081526020015f205f820151815f015f6101000a81548160ff0219169083151502179055506020820151815f0160016101000a81548160ff021916908315150217905550604082015181600101556060820151816002015560808201518160030155905050807fcbdd4006f4a0ca378f1c5d1f11060ba15bc180a12590dd3ea3c219454582939e846040516118b39190611c79565b60405180910390a280915050919050565b6101f481565b6118d2611973565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611942575f6040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016119399190611df7565b60405180910390fd5b61194b81611a49565b50565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61197b611b0a565b73ffffffffffffffffffffffffffffffffffffffff16611999611421565b73ffffffffffffffffffffffffffffffffffffffff16146119f8576119bc611b0a565b6040517f118cdaa70000000000000000000000000000000000000000000000000000000081526004016119ef9190611df7565b60405180910390fd5b565b600260015403611a36576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600181905550565b60018081905550565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f33905090565b5f82825260208201905092915050565b7f55736520706c6163654265742066756e6374696f6e00000000000000000000005f82015250565b5f611b55601583611b11565b9150611b6082611b21565b602082019050919050565b5f6020820190508181035f830152611b8281611b49565b9050919050565b5f5ffd5b5f819050919050565b611b9f81611b8d565b8114611ba9575f5ffd5b50565b5f81359050611bba81611b96565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f611be982611bc0565b9050919050565b611bf981611bdf565b8114611c03575f5ffd5b50565b5f81359050611c1481611bf0565b92915050565b5f5f5f60608486031215611c3157611c30611b89565b5b5f611c3e86828701611bac565b9350506020611c4f86828701611c06565b9250506040611c6086828701611bac565b9150509250925092565b611c7381611b8d565b82525050565b5f602082019050611c8c5f830184611c6a565b92915050565b5f60208284031215611ca757611ca6611b89565b5b5f611cb484828501611bac565b91505092915050565b5f5f60408385031215611cd357611cd2611b89565b5b5f611ce085828601611bac565b9250506020611cf185828601611bac565b9150509250929050565b5f8115159050919050565b611d0f81611cfb565b82525050565b5f60a082019050611d285f830188611d06565b611d356020830187611d06565b611d426040830186611c6a565b611d4f6060830185611c6a565b611d5c6080830184611c6a565b9695505050505050565b5f5f60408385031215611d7c57611d7b611b89565b5b5f611d8985828601611bac565b9250506020611d9a85828601611c06565b9150509250929050565b5f602082019050611db75f830184611d06565b92915050565b5f60208284031215611dd257611dd1611b89565b5b5f611ddf84828501611c06565b91505092915050565b611df181611bdf565b82525050565b5f602082019050611e0a5f830184611de8565b92915050565b7f546f75726e616d656e7420646f6573206e6f74206578697374000000000000005f82015250565b5f611e44601983611b11565b9150611e4f82611e10565b602082019050919050565b5f6020820190508181035f830152611e7181611e38565b9050919050565b7f416c726561647920736574746c656400000000000000000000000000000000005f82015250565b5f611eac600f83611b11565b9150611eb782611e78565b602082019050919050565b5f6020820190508181035f830152611ed981611ea0565b9050919050565b7f496e76616c6964206167656e74204944000000000000000000000000000000005f82015250565b5f611f14601083611b11565b9150611f1f82611ee0565b602082019050919050565b5f6020820190508181035f830152611f4181611f08565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611f7f82611b8d565b9150611f8a83611b8d565b9250828203905081811115611fa257611fa1611f48565b5b92915050565b5f611fb282611b8d565b9150611fbd83611b8d565b9250828202611fcb81611b8d565b91508282048414831517611fe257611fe1611f48565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61202082611b8d565b915061202b83611b8d565b92508261203b5761203a611fe9565b5b828204905092915050565b7f546f75726e616d656e74206e6f742061637469766500000000000000000000005f82015250565b5f61207a601583611b11565b915061208582612046565b602082019050919050565b5f6020820190508181035f8301526120a78161206e565b9050919050565b7f4265742062656c6f77206d696e696d756d0000000000000000000000000000005f82015250565b5f6120e2601183611b11565b91506120ed826120ae565b602082019050919050565b5f6020820190508181035f83015261210f816120d6565b9050919050565b5f61212082611b8d565b915061212b83611b8d565b925082820190508082111561214357612142611f48565b5b92915050565b5f60408201905061215c5f830185611c6a565b6121696020830184611c6a565b9392505050565b7f546f75726e616d656e74206e6f7420736574746c6564000000000000000000005f82015250565b5f6121a4601683611b11565b91506121af82612170565b602082019050919050565b5f6020820190508181035f8301526121d181612198565b9050919050565b7f416c726561647920636c61696d656400000000000000000000000000000000005f82015250565b5f61220c600f83611b11565b9150612217826121d8565b602082019050919050565b5f6020820190508181035f83015261223981612200565b9050919050565b5f61224a82611b8d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361227c5761227b611f48565b5b600182019050919050565b7f4e6f2077696e6e696e67206265747300000000000000000000000000000000005f82015250565b5f6122bb600f83611b11565b91506122c682612287565b602082019050919050565b5f6020820190508181035f8301526122e8816122af565b9050919050565b7f4e6f2077696e6e696e677320746f20636c61696d0000000000000000000000005f82015250565b5f612323601483611b11565b915061232e826122ef565b602082019050919050565b5f6020820190508181035f83015261235081612317565b9050919050565b5f81905092915050565b50565b5f61236f5f83612357565b915061237a82612361565b5f82019050919050565b5f61238e82612364565b9150819050919050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f6123cc600f83611b11565b91506123d782612398565b602082019050919050565b5f6020820190508181035f8301526123f9816123c0565b9050919050565b7f42657474696e67207374696c6c206163746976650000000000000000000000005f82015250565b5f612434601483611b11565b915061243f82612400565b602082019050919050565b5f6020820190508181035f83015261246181612428565b9050919050565b7f4e6f206265747320706c616365640000000000000000000000000000000000005f82015250565b5f61249c600e83611b11565b91506124a782612468565b602082019050919050565b5f6020820190508181035f8301526124c981612490565b9050919050565b7f57696e6e696e67206167656e7420686173206e6f2062657473000000000000005f82015250565b5f612504601983611b11565b915061250f826124d0565b602082019050919050565b5f6020820190508181035f830152612531816124f8565b9050919050565b7f466565207472616e73666572206661696c6564000000000000000000000000005f82015250565b5f61256c601383611b11565b915061257782612538565b602082019050919050565b5f6020820190508181035f83015261259981612560565b9050919050565b7f496e76616c6964206164647265737300000000000000000000000000000000005f82015250565b5f6125d4600f83611b11565b91506125df826125a0565b602082019050919050565b5f6020820190508181035f830152612601816125c8565b9050919050565b7f416c726561647920636c6f7365640000000000000000000000000000000000005f82015250565b5f61263c600e83611b11565b915061264782612608565b602082019050919050565b5f6020820190508181035f83015261266981612630565b9050919050565b7f496e76616c6964206167656e7420636f756e74000000000000000000000000005f82015250565b5f6126a4601383611b11565b91506126af82612670565b602082019050919050565b5f6020820190508181035f8301526126d181612698565b905091905056fea2646970667358221220604aa9027b7ab221bf9935ecd97a2870b47fa01e1fb82553ecfa8a3cc38e6ad964736f6c634300081c0033", + "linkReferences": {}, + "deployedLinkReferences": {}, + "immutableReferences": {}, + "inputSourceName": "project/contracts/AgonusBetting.sol", + "buildInfoId": "solc-0_8_28-81040cba95ef7e83fd023c99a74862e5526c8839" + } \ No newline at end of file diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/core/config.py b/backend/app/core/config.py new file mode 100644 index 0000000..c42c74d --- /dev/null +++ b/backend/app/core/config.py @@ -0,0 +1,44 @@ +from pydantic_settings import BaseSettings +from pathlib import Path +import json + + +class Settings(BaseSettings): + """Central app settings, values come from env""" + + # Database + DATABASE_URL: str = "sqlite+aiosqlite:///./agonus.db" + + # Contract toggle (false in tests/CI to skip blockchain calls) + USE_CONTRACT: bool = True + + # Blockchain and contract settings (safe defaults for dev/CI) + CONTRACT_ADDRESS: str = "0x0000000000000000000000000000000000000001" + ADMIN_PRIVATE_KEY: str = "0x0000000000000000000000000000000000000000000000000000000000000001" + RPC_URL: str = "https://sepolia.base.org" + CONTRACT_ABI_PATH: str = "backend/app/contracts/AgonusBetting.json" + CHAIN_ID: int = 84532 + + @property + def CONTRACT_ABI(self): + """Load contract ABI from json file""" + + # Resolve path relative to this file so it works in CI and locally + path = Path(__file__).resolve().parent.parent / "contracts" / "AgonusBetting.json" + + if not path.exists(): + raise FileNotFoundError(f"Contract ABI file not found at {path}") + + data = json.loads(path.read_text()) + + if isinstance(data, dict) and "abi" in data: + return data["abi"] + + return data + + class Config: + env_file = ".env" + env_file_encoding = "utf-8" + + +settings = Settings() \ No newline at end of file diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 954cb44..5b5f005 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -49,6 +49,27 @@ class Tournament(Base): winner_agent_id: Mapped[Optional[UUID]] = mapped_column( ForeignKey("agent.id"), index=True, default=None ) + + #ADDED BY JACOB + + #on-chain tournament ID + contract_tournament_id: Mapped[Optional[int]] = mapped_column( + Integer, index=True, nullable=True + ) + + #map agent uuids to 1 based contract agent indices + agent_contract_mapping: Mapped[Optional[dict[str, int]]] = mapped_column( + JSON, default=dict + ) + + #contract lifestyle status + contract_status: Mapped[Optional[str]] = mapped_column( + String, default="PENDING" + ) + + + + # Relationships - clean and simple! trades: Mapped[list["Trade"]] = relationship(back_populates="tournament") diff --git a/backend/app/init.py b/backend/app/init.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/main.py b/backend/app/main.py index e69de29..895e35e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -0,0 +1,16 @@ +from fastapi import FastAPI +from backend.app.api.routers import tournament, agent, trade, bet, auth + +app = FastAPI(title="Agonus API") + +# Register routers +app.include_router(tournament.router, prefix="/tournaments", tags=["Tournaments"]) +app.include_router(agent.router, prefix="/agents", tags=["Agents"]) +app.include_router(trade.router, prefix="/trades", tags=["Trades"]) +app.include_router(bet.router, prefix="/bets", tags=["Bets"]) +app.include_router(auth.router, prefix="/auth", tags=["Auth"]) + + +@app.get("/") +def root(): + return {"message": "Agonus API running 🚀"} \ No newline at end of file diff --git a/backend/app/schemas/tournament.py b/backend/app/schemas/tournament.py index ec19190..690f6d0 100644 --- a/backend/app/schemas/tournament.py +++ b/backend/app/schemas/tournament.py @@ -16,7 +16,9 @@ class TournamentBase(BaseModel): # Schema for creating tournaments (POST) class TournamentCreate(TournamentBase): - pass + + #ids of agents participating in the tournament + agent_ids: list[UUID] # Schema for updating tournaments (PUT/PATCH) @@ -34,5 +36,11 @@ class TournamentResponse(TournamentBase): id: UUID created_at: datetime winner_agent_id: Optional[UUID] = None + + + #ADDED BY JACOB + #expose chain id on responses + contract_tournament_id: Optional[int] = None + #agent_contract_mapping: Optional[dict[str, int]] = None model_config = ConfigDict(from_attributes=True) diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/contract_service.py b/backend/app/services/contract_service.py new file mode 100644 index 0000000..a8f956e --- /dev/null +++ b/backend/app/services/contract_service.py @@ -0,0 +1,230 @@ +from typing import Any, Dict + +from eth_account import Account +from web3 import Web3 +from web3.exceptions import ContractLogicError, TimeExhausted + +from fastapi.concurrency import run_in_threadpool + +from backend.app.core.config import settings + + +# Initialize Web3 and contract +w3 = Web3(Web3.HTTPProvider(settings.RPC_URL)) +contract = w3.eth.contract( + address=Web3.to_checksum_address(settings.CONTRACT_ADDRESS), + abi=settings.CONTRACT_ABI, +) +admin_account = Account.from_key(settings.ADMIN_PRIVATE_KEY) + + +class ContractTransactionError(Exception): + """Raised when a contract transaction fails.""" + pass + + +def _build_base_tx() -> Dict[str, Any]: + """ + Build the base transaction dict (from, nonce, gasPrice, chainId). + """ + nonce = w3.eth.get_transaction_count(admin_account.address) + gas_price = w3.eth.gas_price + return { + "from": admin_account.address, + "nonce": nonce, + "gasPrice": gas_price, + "chainId": settings.CHAIN_ID, + } + + +def _send_tx(fn) -> str: + """ + Build, sign, send and wait for a contract transaction. + + fn: a contract.functions.(...) object + Returns the transaction hash as a hex string. + Raises ContractTransactionError on failure. + """ + try: + # Build transaction + tx = fn.build_transaction(_build_base_tx()) + + # Estimate gas + gas_estimate = w3.eth.estimate_gas(tx) + tx["gas"] = gas_estimate + + # Sign transaction + signed = w3.eth.account.sign_transaction( + tx, + private_key=settings.ADMIN_PRIVATE_KEY, + ) + + # Send transaction + tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction) + + # Wait for receipt + receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) + + # Check status + if receipt.status != 1: + raise ContractTransactionError( + f"Transaction failed with status=0, hash={tx_hash.hex()}" + ) + + return tx_hash.hex() + + except (ContractLogicError, TimeExhausted) as e: + # Contract revert or timeout + raise ContractTransactionError(str(e)) from e + except Exception as e: + # Network / nonce / other unexpected issues + raise ContractTransactionError(str(e)) from e + + +# 1) Admin write functions + + +async def create_tournament_on_contract(agent_count: int) -> int: + """ + Create a tournament on the AgonusBetting contract. + + Calls: createTournament(agentCount) + Returns: on-chain tournament ID (int) + + NOTE: Right now this returns 0 as a placeholder for the tournament ID, + because the exact pattern (event vs getter) depends on the ABI details. + Once you confirm how the contract exposes the new tournamentId, + update the inner function accordingly. + """ + + def _inner() -> int: + _send_tx(contract.functions.createTournament(agent_count)) + # TODO: replace this with real logic to fetch the new tournament ID. + # For example, if the contract has currentTournamentId(): + # new_id = contract.functions.currentTournamentId().call() + # return int(new_id) + return 0 + + return await run_in_threadpool(_inner) + + +async def close_betting(contract_tournament_id: int) -> str: + """ + Close betting for a given tournament on chain. + + Calls: closeBetting(tournamentId) + Returns: transaction hash (str) + """ + + def _inner() -> str: + return _send_tx( + contract.functions.closeBetting(contract_tournament_id) + ) + + return await run_in_threadpool(_inner) + + +async def settle_tournament( + contract_tournament_id: int, + winning_agent_id: int, +) -> str: + """ + Settle a tournament on chain with the winning agent. + + Calls: settleTournament(tournamentId, winningAgentId) + Returns: transaction hash (str) + """ + + def _inner() -> str: + return _send_tx( + contract.functions.settleTournament( + contract_tournament_id, + winning_agent_id, + ) + ) + + return await run_in_threadpool(_inner) + + +async def cancel_tournament(contract_tournament_id: int) -> str: + """ + Cancel a tournament on chain (enables refunds). + + Calls: cancelTournament(tournamentId) + Returns: transaction hash (str) + """ + + def _inner() -> str: + return _send_tx( + contract.functions.cancelTournament(contract_tournament_id) + ) + + return await run_in_threadpool(_inner) + + +# 2) Read-only functions (no transactions, just .call()) + + +async def get_tournament_state(contract_tournament_id: int) -> dict[str, Any]: + """ + Read tournament struct from the AgonusBetting contract. + + Calls: + tournaments(tournamentId) -> ( + isActive: bool, + isSettled: bool, + totalPool: uint256, + winningAgentId: uint256, + agentCount: uint256 + ) + + Returns a Python dict with those fields. + """ + + def _inner() -> dict[str, Any]: + data = contract.functions.tournaments(contract_tournament_id).call() + return { + "isActive": data[0], + "isSettled": data[1], + "totalPool": int(data[2]), + "winningAgentId": int(data[3]), + "agentCount": int(data[4]), + } + + return await run_in_threadpool(_inner) + + +async def get_agent_pool(contract_tournament_id: int, agent_id: int) -> int: + """ + Get total bet amount on a given agent in a given tournament. + + Reads: + agentPools[tournamentId][agentId] + """ + + def _inner() -> int: + amount = contract.functions.agentPools( + contract_tournament_id, + agent_id, + ).call() + return int(amount) + + return await run_in_threadpool(_inner) + + +async def get_agent_odds(contract_tournament_id: int, agent_id: int) -> int: + """ + Get odds for a given agent in a tournament, in basis points (1% = 100 bp). + + Calls: + getAgentOdds(tournamentId, agentId) + """ + + def _inner() -> int: + odds_bp = contract.functions.getAgentOdds( + contract_tournament_id, + agent_id, + ).call() + return int(odds_bp) + + return await run_in_threadpool(_inner) diff --git a/backend/coverage.xml b/backend/coverage.xml deleted file mode 100644 index 9c87559..0000000 --- a/backend/coverage.xml +++ /dev/null @@ -1,667 +0,0 @@ - - - - - - /Users/jacobhorne/most_updated_blockchain/backend/app - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/init.py b/backend/init.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/requirements.txt b/backend/requirements.txt index c2e7a04..d54669f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -67,3 +67,4 @@ watchfiles==1.1.1 web3==7.14.0 websockets==15.0.1 yarl==1.22.0 + diff --git a/backend/tests/test_tournaments.py b/backend/tests/test_tournaments.py index 401d293..90f593b 100644 --- a/backend/tests/test_tournaments.py +++ b/backend/tests/test_tournaments.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone, timedelta from decimal import Decimal from uuid import uuid4 -from backend.app.db.models import Tournament +from backend.app.db.models import Tournament, Agent @pytest.mark.anyio @@ -76,15 +76,26 @@ async def test_get_tournament_not_found(client): @pytest.mark.anyio -async def test_create_tournament(client): +async def test_create_tournament(client, test_db): '''Test for creating a tournament''' now = datetime.now(timezone.utc) + + dummy_agent = Agent( + name="Test Agent", + personality="test", + strategy_type="test", + ) + test_db.add(dummy_agent) + await test_db.commit() + await test_db.refresh(dummy_agent) + body = { "name": "Winter Cup", "status": "upcoming", "start_date": (now + timedelta(days=30)).isoformat(), "end_date": (now + timedelta(days=37)).isoformat(), "prize_pool": 20000.00, + "agent_ids": [str(dummy_agent.id)], # temporary: no agents for this simple test } r = await client.post("/tournaments/", json=body) assert r.status_code == 201, r.text diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2972302 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.12.0 +click==8.3.1 +fastapi==0.122.0 +h11==0.16.0 +idna==3.11 +pydantic==2.12.5 +pydantic_core==2.41.5 +starlette==0.50.0 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +uvicorn==0.38.0