# EloContract.Sol ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@tableland/evm/contracts/utils/TablelandDeployments.sol"; import "@tableland/evm/contracts/utils/SQLHelpers.sol"; import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "./EloRatingSystem.sol"; contract EloContract is ERC721Holder, Pausable, EloRatingSystem { using SafeERC20 for IERC20; uint256 private _tableId; string private constant _TABLE_PREFIX = "elo_kv"; // Mapping to store ERC4626 vault contract address for each league && mapping for player deposits mapping(string => address) public leagueVaults; mapping(address => uint256) public playerDeposits; IERC20 public token; uint256 public totalDeposited; // Other variables and functions specific to your contract constructor(address _token) { token = IERC20(_token); } // Functions for interacting with Tableland function createTable(string memory leagueKey) public payable { _tableId = TablelandDeployments.get().createTable( address(this), SQLHelpers.toCreateFromSchema( "id int primary key," // Player ID "name text," // Player name "league text," // League name "elo_rating int," // Elo rating "league_icon_uri text", // League icon URI _TABLE_PREFIX ) ); // Create a new ERC4626 vault for the league ERC4626 vault = new ERC4626(/* parameters */); leagueVaults[leagueKey] = address(vault); } function joinLeague(address player, uint32 initialEloRating, uint256 depositAmount, string memory leagueKey, string memory playerId) public whenNotPaused { require(depositAmount > 0, "Deposit amount should be greater than 0"); require(playerDeposits[player] == 0, "Player has already joined the league"); // Transfer the deposit to the vault token.safeTransferFrom(msg.sender, address(this), depositAmount); totalDeposited += depositAmount; playerDeposits[player] = depositAmount; // Add the new player to the league with the initial ELO rating _addPlayerToLeague(playerId, initialEloRating, leagueKey); } function _addPlayerToLeague(string memory playerId, uint32 initialEloRating, string memory leagueKey) private { string memory tableName = string(abi.encodePacked(_TABLE_PREFIX, "_", leagueKey)); // Insert the new player with the initial ELO rating into the Tableland table TablelandDeployments.get().runSQL( address(this), _tableId, SQLHelpers.toInsert( tableName, _tableId, SQLHelpers.quote(playerId) + "," + SQLHelpers.quote("New Player") + "," + SQLHelpers.quote(leagueKey) + "," + SQLHelpers.quote(toString(initialEloRating)) + "," + SQLHelpers.quote("league_icon_uri_placeholder") ) ); } function submitMatchResult(string memory leagueKey, string memory playerAKey, string memory playerBKey, bool playerAWon) public whenNotPaused { // Read player ratings from Tableland (use off-chain component to read the data) uint32 playerARating = 0; // Replace this with the actual rating fetched from Tableland uint32 playerBRating = 0; // Replace this with the actual rating fetched from Tableland uint32 leagueKFactor = leagueKFactors[leagueKey]; require(leagueKFactor > 0, "League K-factor not set"); // Calculate new ratings based on the match result (uint32 newRatingA, uint32 newRatingB) = _eloRating(playerARating, playerBRating, leagueKFactor, playerAWon); // Update player ratings in Tableland updatePlayerRating(leagueKey, playerAKey, newRatingA); updatePlayerRating(leagueKey, playerBKey, newRatingB); } function updatePlayerRating(string memory leagueKey, string memory playerId, uint32 newRating) private { // Construct the table name using the league key string memory tableName = string(abi.encodePacked(_TABLE_PREFIX, "_", leagueKey)); // Update player rating in Tableland using the constructed table name and player ID TablelandDeployments.get().runSQL( address(this), _tableId, SQLHelpers.toUpdate( tableName, _tableId, "elo_rating = ", SQLHelpers.quote(toString(newRating)), "where id = ", SQLHelpers.quote(playerId) ) ); } // ERC-4626 Vault functions function deposit(uint256 amount) public whenNotPaused { require(amount > 0, "Amount should be greater than 0"); token.safeTransferFrom(msg.sender, address(this), amount); totalDeposited += amount; // Add any additional logic for handling deposits (e.g., minting shares) } function withdraw(uint256 amount) public whenNotPaused { require(amount > 0, "Amount should be greater than 0"); require(amount <= totalDeposited, "Insufficient balance in the vault"); token.safeTransfer(msg.sender, amount); totalDeposited -= amount; // Add any additional logic for handling withdrawals (e.g., burning shares) } function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } } ``` ## IEloContract.sol ``` interface ILeagueMembership { function submitMatchResult(address playerA, address playerB, bool playerAWon) external; function claimVaultShare(uint256 amount) external; function joinLeague(address player, uint32 initialEloRating, uint256 depositAmount, string memory leagueKey, string memory playerId) external; } ``` ## EloRatingSystem.sol ``` pragma solidity ^0.8.12; contract EloRatingSystem { mapping(string => uint32) public leagueKFactors; function setLeagueKFactor(string memory leagueKey, uint32 kFactor) public { // Add any access control logic if needed (e.g., onlyOwner, onlyAdmin, etc.) leagueKFactors[leagueKey] = kFactor; } function _eloRating(uint32 ratingA, uint32 ratingB, uint32 kFactor, bool playerAWon) internal pure returns (uint32 newRatingA, uint32 newRatingB) { int32 ratingDifference = int32(ratingB) - int32(ratingA); int32 expectedScoreA = 1 / (1 + 10 ** (ratingDifference / 400)); int32 expectedScoreB = 1 / (1 + 10 ** (-ratingDifference / 400)); int32 actualScoreA = playerAWon ? 1 : 0; int32 actualScoreB = playerAWon ? 0 : 1; newRatingA = uint32(int32(ratingA) + kFactor * (actualScoreA - expectedScoreA)); newRatingB = uint32(int32(ratingB) + kFactor * (actualScoreB - expectedScoreB)); } } ``` ## MembershipNFT.sol ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@tableland/evm/contracts/utils/URITemplate.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./ILeagueMembership.sol"; contract MembershipNFT is ERC721, Ownable, ILeagueMembership ,ERC721URIStorage{ string private _baseURIString = "https://testnets.tableland.network/query?unwrap=true&extract=true&s="; string private _uriTemplate = "SELECT+json_object%28%27id%27%2C+id%2C+%27player_name%27%2C+player_name%2C+%27elo_rating%27%2C+elo_rating%2C+%27league_icon_uri%27%2C+league_icon_uri%29+FROM+your_table_name+WHERE+id%3D{id}"; constructor(address eloContractAddress) ERC721("MembershipNFT", "MNFT") { _eloContract = ILeagueMembership(eloContractAddress); setURITemplate(_uriTemplate); } function mint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } function submitMatchResult(address playerA, address playerB, bool playerAWon) public override { require(ownerOf(uint256(playerA)) == msg.sender, "Player A membership not owned"); require(ownerOf(uint256(playerB)) == msg.sender, "Player B membership not owned"); _eloContract.submitMatchResult(playerA, playerB, playerAWon); } function claimVaultShare(uint256 amount) public override { address playerAddress = address(uint160(msg.sender)); require(ownerOf(uint256(playerAddress)) == msg.sender, "Membership not owned"); _eloContract.claimVaultShare(amount); } function joinLeague(address player, uint32 initialEloRating, uint256 depositAmount, string memory leagueKey, string memory playerId) public override { // Check for membership requirements here if needed // Call the EloContract's joinLeague function to handle the deposit and adding a new player _eloContract.joinLeague(player, initialEloRating, depositAmount, leagueKey, playerId); // Mint the membership token uint256 tokenId = uint256(uint160(player)); mint(player, tokenId); } function _baseURI() internal view override returns (string memory) { return _baseURIString; } function setURITemplate(string memory uriTemplate) public onlyOwner { _setURITemplate(uriTemplate); } function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "URI query for nonexistent token"); return _getTokenURI(_toString(tokenId)); } function mintMembership(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } } ``` ## EloTokenizedVault.sol ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.12; import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol contract EloTokenizedVault is EloContract, ERC4626 { constructor(address _token, address _underlyingAsset) EloContract(_token) ERC4626(_underlyingAsset) {} // Implement other functions for payouts, donations, etc. } ```