# 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.
}
```