mirror of
https://github.com/0xFableOrg/0xFable.git
synced 2026-01-09 20:48:06 -05:00
Adding Player Handles (cleanup) (#137)
This commit is contained in:
16
packages/contracts/src/MockResolver.sol
Normal file
16
packages/contracts/src/MockResolver.sol
Normal file
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./PlayerHandle.sol";
|
||||
|
||||
contract MockENSResolver is IENSResolver {
|
||||
mapping(bytes32 => string) public names;
|
||||
|
||||
function setName(bytes32 node, string memory _name) public {
|
||||
names[node] = _name;
|
||||
}
|
||||
|
||||
function name(bytes32 node) external view override returns (string memory) {
|
||||
return names[node];
|
||||
}
|
||||
}
|
||||
106
packages/contracts/src/PlayerHandle.sol
Normal file
106
packages/contracts/src/PlayerHandle.sol
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
||||
|
||||
interface IENSResolver {
|
||||
function name(bytes32 node) external view returns (string memory);
|
||||
}
|
||||
|
||||
error InvalidHandle();
|
||||
error HandleAlreadyTaken();
|
||||
error PlayerNotEligible();
|
||||
|
||||
contract PlayerHandle is Ownable {
|
||||
using Strings for uint256;
|
||||
|
||||
IENSResolver public ensResolver;
|
||||
mapping(address => string) private handles;
|
||||
mapping(string => address) private handleOwners;
|
||||
mapping(address => bool) private useEns;
|
||||
|
||||
event HandleRegistered(address indexed player, string handle);
|
||||
event HandleChanged(address indexed player, string newHandle);
|
||||
event UseEnsSet(address indexed player, bool useEns);
|
||||
|
||||
constructor(address _ensResolver) {
|
||||
ensResolver = IENSResolver(_ensResolver);
|
||||
}
|
||||
|
||||
function registerHandle(string memory handle) external {
|
||||
if (!checkHandleValidity(handle)) {
|
||||
revert InvalidHandle();
|
||||
}
|
||||
if (handleOwners[handle] != address(0)) {
|
||||
revert HandleAlreadyTaken();
|
||||
}
|
||||
if (!checkPlayerEligibility(msg.sender)) {
|
||||
revert PlayerNotEligible();
|
||||
}
|
||||
|
||||
if (bytes(handles[msg.sender]).length > 0) {
|
||||
handleOwners[handles[msg.sender]] = address(0);
|
||||
}
|
||||
|
||||
handles[msg.sender] = handle;
|
||||
handleOwners[handle] = msg.sender;
|
||||
|
||||
emit HandleRegistered(msg.sender, handle);
|
||||
}
|
||||
|
||||
function changeHandle(string memory newHandle) external {
|
||||
if (!checkHandleValidity(newHandle)) {
|
||||
revert InvalidHandle();
|
||||
}
|
||||
|
||||
if (handleOwners[newHandle] != address(0)) {
|
||||
revert HandleAlreadyTaken();
|
||||
}
|
||||
|
||||
string memory oldHandle = handles[msg.sender];
|
||||
handles[msg.sender] = newHandle;
|
||||
handleOwners[newHandle] = msg.sender;
|
||||
handleOwners[oldHandle] = address(0);
|
||||
|
||||
emit HandleChanged(msg.sender, newHandle);
|
||||
}
|
||||
|
||||
function setUseEns(bool _useEns) external {
|
||||
useEns[msg.sender] = _useEns;
|
||||
emit UseEnsSet(msg.sender, _useEns);
|
||||
}
|
||||
|
||||
function getPlayerHandle(address player) external view returns (string memory) {
|
||||
if (useEns[player]) {
|
||||
bytes32 node = keccak256(abi.encodePacked(addressToBytes32(player)));
|
||||
string memory ensName = ensResolver.name(node);
|
||||
if (bytes(ensName).length > 0) {
|
||||
return ensName;
|
||||
}
|
||||
}
|
||||
return handles[player];
|
||||
}
|
||||
|
||||
function checkHandleValidity(string memory handle) public pure returns (bool) {
|
||||
bytes memory b = bytes(handle);
|
||||
if (b.length < 5 || b.length > 15) {
|
||||
return false;
|
||||
}
|
||||
for (uint256 i; i < b.length; i++) {
|
||||
bytes1 char = b[i];
|
||||
if (!(char >= 0x30 && char <= 0x39) && !(char >= 0x41 && char <= 0x5A) && !(char >= 0x61 && char <= 0x7A)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkPlayerEligibility(address player) public pure returns (bool) {
|
||||
return true; // Placeholder function; implement eligibility logic as needed
|
||||
}
|
||||
|
||||
function addressToBytes32(address addr) private pure returns (bytes32) {
|
||||
return bytes32(uint256(uint160(addr)));
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import {InventoryCardsCollection} from "../InventoryCardsCollection.sol";
|
||||
import {Groth16Verifier as DrawVerifier} from "../verifiers/DrawVerifier.sol";
|
||||
import {Groth16Verifier as DrawHandVerifier} from "../verifiers/DrawHandVerifier.sol";
|
||||
import {Groth16Verifier as PlayVerifier} from "../verifiers/PlayVerifier.sol";
|
||||
import {MockENSResolver} from "../MockResolver.sol";
|
||||
import {PlayerHandle} from "../PlayerHandle.sol";
|
||||
|
||||
import {Script, console2} from "forge-std/Script.sol";
|
||||
// import {Multicall3} from "multicall/Multicall3.sol";
|
||||
@@ -24,6 +26,8 @@ contract Deploy is Script {
|
||||
DrawHandVerifier public drawHandVerifier;
|
||||
Game public game;
|
||||
DeckAirdrop public airdrop;
|
||||
MockENSResolver public mockEnsResolver;
|
||||
PlayerHandle public playerHandle;
|
||||
|
||||
bool private doLog = true;
|
||||
|
||||
@@ -51,6 +55,8 @@ contract Deploy is Script {
|
||||
bool noRandom = vm.envOr("NO_RANDOM", false);
|
||||
game = new Game(inventory, drawVerifier, playVerifier, drawHandVerifier, checkProofs, noRandom);
|
||||
airdrop = new DeckAirdrop(inventory);
|
||||
mockEnsResolver = new MockENSResolver();
|
||||
playerHandle = new PlayerHandle(address(mockEnsResolver));
|
||||
|
||||
// initialize
|
||||
cardsCollection.setInventory(inventory);
|
||||
@@ -63,6 +69,8 @@ contract Deploy is Script {
|
||||
log("InventoryCardsCollection address", address(inventoryCardsCollection));
|
||||
log("Game address", address(game));
|
||||
log("DeckAirdrop address", address(airdrop));
|
||||
log("MockENSResolver address", address(mockEnsResolver));
|
||||
log("PlayerHandle address", address(playerHandle));
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
|
||||
78
packages/contracts/src/test/PlayerHandle.t.sol
Normal file
78
packages/contracts/src/test/PlayerHandle.t.sol
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../PlayerHandle.sol";
|
||||
import {MockENSResolver} from "../MockResolver.sol";
|
||||
|
||||
// running tests for PlayerHandle and MockENSResolver
|
||||
contract PlayerHandleTest is Test {
|
||||
PlayerHandle public playerHandle;
|
||||
MockENSResolver public mockENSResolver;
|
||||
|
||||
address public alice = address(0x1);
|
||||
address public bob = address(0x2);
|
||||
|
||||
function setUp() public {
|
||||
mockENSResolver = new MockENSResolver();
|
||||
playerHandle = new PlayerHandle(address(mockENSResolver));
|
||||
}
|
||||
|
||||
function testRegisterHandle() public {
|
||||
vm.prank(alice);
|
||||
playerHandle.registerHandle("AliceHandle");
|
||||
|
||||
assertEq(playerHandle.getPlayerHandle(alice), "AliceHandle");
|
||||
}
|
||||
|
||||
function testRegisterInvalidHandle() public {
|
||||
vm.prank(alice);
|
||||
vm.expectRevert(abi.encodeWithSignature("InvalidHandle()"));
|
||||
playerHandle.registerHandle("a");
|
||||
}
|
||||
|
||||
function testHandleAlreadyTaken() public {
|
||||
vm.prank(alice);
|
||||
playerHandle.registerHandle("AliceHandle");
|
||||
|
||||
vm.prank(bob);
|
||||
vm.expectRevert(abi.encodeWithSignature("HandleAlreadyTaken()"));
|
||||
playerHandle.registerHandle("AliceHandle");
|
||||
}
|
||||
|
||||
function testChangeHandle() public {
|
||||
vm.prank(alice);
|
||||
playerHandle.registerHandle("AliceHandle");
|
||||
|
||||
vm.prank(alice);
|
||||
playerHandle.changeHandle("NewAliceHandle");
|
||||
|
||||
assertEq(playerHandle.getPlayerHandle(alice), "NewAliceHandle");
|
||||
}
|
||||
|
||||
function testSetUseEns() public {
|
||||
bytes32 node = keccak256(abi.encodePacked(addressToBytes32(alice)));
|
||||
mockENSResolver.setName(node, "alice.eth");
|
||||
|
||||
vm.prank(alice);
|
||||
playerHandle.setUseEns(true);
|
||||
|
||||
assertEq(playerHandle.getPlayerHandle(alice), "alice.eth");
|
||||
}
|
||||
|
||||
function testUnsetUseEns() public {
|
||||
bytes32 node = keccak256(abi.encodePacked(addressToBytes32(alice)));
|
||||
mockENSResolver.setName(node, "alice.eth");
|
||||
|
||||
vm.prank(alice);
|
||||
playerHandle.registerHandle("AliceHandle");
|
||||
playerHandle.setUseEns(true);
|
||||
playerHandle.setUseEns(false);
|
||||
|
||||
assertEq(playerHandle.getPlayerHandle(alice), "AliceHandle");
|
||||
}
|
||||
|
||||
function addressToBytes32(address addr) private pure returns (bytes32) {
|
||||
return bytes32(uint256(uint160(addr)));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ export interface Deployment {
|
||||
Game: Address
|
||||
DeckAirdrop: Address
|
||||
Multicall3: Address
|
||||
PlayerHandle: Address
|
||||
MockENSResolver: Address
|
||||
}
|
||||
|
||||
// NOTE: This silly `default` affair is required for running the e2e tests which cause
|
||||
|
||||
Reference in New Issue
Block a user