From 28251a6570d9c03f1d1a70a20b73a50e646c6113 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 12:11:12 -0700 Subject: [PATCH 1/9] Allow Contracts to have Souls --- contracts/SoulUpgradable.sol | 52 +++++++++++++++++++--------------- contracts/interfaces/ISoul.sol | 9 ++++-- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/contracts/SoulUpgradable.sol b/contracts/SoulUpgradable.sol index 18641d0..09d6526 100644 --- a/contracts/SoulUpgradable.sol +++ b/contracts/SoulUpgradable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.4; -// import "hardhat/console.sol"; +import "hardhat/console.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; // import "@openzeppelin/contracts/utils/Counters.sol"; @@ -10,6 +10,7 @@ import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; // import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "./interfaces/ISoul.sol"; import "./abstract/Opinions.sol"; import "./abstract/ProtocolEntityUpgradable.sol"; @@ -17,13 +18,14 @@ import "./abstract/ProtocolEntityUpgradable.sol"; /** * @title Soulbound NFT Identity Tokens + Reputation Tracking - * @dev Version 2.0 + * @dev Version 2.1 * - Contract is open for everyone to mint. * - Max of one NFT assigned for each account * - Can create un-assigned NFT (Kept on contract) * - Minted Token's URI is updatable by Token holder * - Assets are non-transferable by owner - * - Tokens can be merged (Multiple Owners) + * - Tokens can be merged (multiple owners) + * - Owner can mint tokens for Contracts * - [TODO] Orphan tokens can be claimed/linked */ contract SoulUpgradable is @@ -68,7 +70,9 @@ contract SoulUpgradable is /// ERC165 - Supported Interfaces function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(ISoul).interfaceId || super.supportsInterface(interfaceId); + return interfaceId == type(ISoul).interfaceId + || interfaceId == type(IERC721Upgradeable).interfaceId + || super.supportsInterface(interfaceId); } //** Token Owner Index **/ @@ -131,34 +135,36 @@ contract SoulUpgradable is //** Token Actions **/ - /// Mint (Create New Avatar for oneself) - function mint(string memory tokenURI) public override returns (uint256) { - //One Per Account - require(balanceOf(_msgSender()) == 0, "Requesting account already has an avatar"); + /// Mint (Create New Token for Someone Else) + function mintFor(address to, string memory tokenURI) public override returns (uint256) { + //Validate - Contract Owner + require(_msgSender() == owner(), "Only Owner"); //Mint - uint256 tokenId = _createAvatar(_msgSender(), tokenURI); - //Index Owner - // _tokenOwnerAdd(_msgSender(), tokenId); //MOVED TO TokenTransfer Logic - //Return - return tokenId; + return _mint(to, tokenURI); + } + + /// Mint (Create New Token for oneself) + function mint(string memory tokenURI) external override returns (uint256) { + //Mint + return _mint(_msgSender(), tokenURI); } - /// Add (Create New Avatar Without an Owner) + /// Add (Create New Token Without an Owner) function add(string memory tokenURI) external override returns (uint256) { //Mint - return _createAvatar(address(this), tokenURI); + return _mint(address(this), tokenURI); } /// Burn NFTs function burn(uint256 tokenId) external { - //Validate Owner of Contract + //Validate - Contract Owner require(_msgSender() == owner(), "Only Owner"); //Burn Token _burn(tokenId); } /// Update Token's Metadata - function update(uint256 tokenId, string memory uri) public override returns (uint256) { + function update(uint256 tokenId, string memory uri) external override returns (uint256) { //Validate Owner of Token require(_isApprovedOrOwner(_msgSender(), tokenId) || _msgSender() == owner(), "caller is not owner nor approved"); _setTokenURI(tokenId, uri); //This Goes for Specific Metadata Set (IPFS and Such) @@ -168,10 +174,12 @@ contract SoulUpgradable is return tokenId; } - /// Create a new Avatar - function _createAvatar(address to, string memory uri) internal returns (uint256){ + /// Create a new Token + function _mint(address to, string memory uri) internal returns (uint256){ //Validate - Bot Protection - require(tx.origin == _msgSender(), "Bots not allowed"); + // require(tx.origin == _msgSender(), "Bots not allowed"); //CANCELLED - Allow Contracts to Have Souls + //One Per Account + require(to == address(this) || balanceOf(_msgSender()) == 0, "Requesting account already has a token"); //Mint _tokenIds.increment(); uint256 newItemId = _tokenIds.current(); @@ -193,9 +201,7 @@ contract SoulUpgradable is require( _msgSender() == owner() || from == address(0) //Minting - // || to == address(0) //Burning - , - "Sorry, Assets are non-transferable" + , "Sorry, assets are non-transferable" ); //Update Address Index diff --git a/contracts/interfaces/ISoul.sol b/contracts/interfaces/ISoul.sol index 62e58b1..8152f69 100644 --- a/contracts/interfaces/ISoul.sol +++ b/contracts/interfaces/ISoul.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.4; -// import "../libraries/DataTypes.sol"; - +/** + * @title Soulbound Token Interface + * @dev Additions to IERC721 + */ interface ISoul { //--- Functions @@ -14,6 +16,9 @@ interface ISoul { /// Mint (Create New Avatar for oneself) function mint(string memory tokenURI) external returns (uint256); + /// Mint (Create New Token for Someone Else) + function mintFor(address to, string memory tokenURI) external returns (uint256); + /// Add (Create New Avatar Without an Owner) function add(string memory tokenURI) external returns (uint256); From 9b9517b4eb1c6ce9529b9f9fb0dcabe8f1194ea2 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 15:00:40 -0700 Subject: [PATCH 2/9] [REFAC] Renamed "avatar" to "SBT" --- archive/Hub.sol | 6 +++--- contracts/ActionRepoTrackerUp.sol | 2 +- contracts/GameUpgradable.sol | 13 +++++++++---- contracts/ReactionUpgradable.sol | 8 ++++---- scripts/deploy.ts | 4 ++-- scripts/hubSwap.ts | 2 +- scripts/updateImplementations.ts | 2 +- test/Hub.ts | 4 ++-- test/deployment.ts | 2 +- 9 files changed, 24 insertions(+), 19 deletions(-) diff --git a/archive/Hub.sol b/archive/Hub.sol index 2d08449..2c84376 100644 --- a/archive/Hub.sol +++ b/archive/Hub.sol @@ -119,7 +119,7 @@ contract Hub is /// Update Hub function hubChange(address newHubAddr) external override onlyOwner { //Avatar - address avatarContract = getAssoc("avatar"); + address avatarContract = getAssoc("SBT"); if(avatarContract != address(0)){ IProtocolEntity(avatarContract).setHub(newHubAddr); } @@ -207,7 +207,7 @@ contract Hub is // console.log("Hub: Add Reputation to Contract:", contractAddr, tokenId, amount); // console.log("Hub: Add Reputation in Domain:", domain); - address avatarContract = getAssoc("avatar"); + address avatarContract = getAssoc("SBT"); //Update Avatar's Reputation //TODO: Just Check if Contract Implements IRating if(avatarContract != address(0) && avatarContract == contractAddr){ _repAddAvatar(tokenId, domain, rating, amount); @@ -216,7 +216,7 @@ contract Hub is /// Add Repuation to Avatar function _repAddAvatar(uint256 tokenId, string calldata domain, bool rating, uint8 amount) internal { - address avatarContract = getAssoc("avatar"); + address avatarContract = getAssoc("SBT"); // require(avatarContract != address(0), "AVATAR_CONTRACT_UNKNOWN"); // repAdd(avatarContract, tokenId, domain, rating, amount); // IAvatar(avatarContract).repAdd(tokenId, domain, rating, amount); diff --git a/contracts/ActionRepoTrackerUp.sol b/contracts/ActionRepoTrackerUp.sol index 055142b..d70821f 100644 --- a/contracts/ActionRepoTrackerUp.sol +++ b/contracts/ActionRepoTrackerUp.sol @@ -43,7 +43,7 @@ contract ActionRepoTrackerUp is //Initializers __UUPSUpgradeable_init(); __ProtocolEntity_init(hub); - __setTargetContract( repo().addressGetOf(address(_HUB), "avatar") ); + __setTargetContract( repo().addressGetOf(address(_HUB), "SBT") ); //Set Contract URI // _setContractURI(uri_); } diff --git a/contracts/GameUpgradable.sol b/contracts/GameUpgradable.sol index 52e954f..3bc29e2 100644 --- a/contracts/GameUpgradable.sol +++ b/contracts/GameUpgradable.sol @@ -10,6 +10,7 @@ import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; // import "./abstract/Votes.sol"; // import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/draft-ERC721VotesUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/governance/utils/VotesUpgradeable.sol"; //Adds 3.486Kb +import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "./interfaces/IGameUp.sol"; import "./interfaces/IRules.sol"; import "./interfaces/IReaction.sol"; @@ -124,11 +125,10 @@ contract GameUpgradable is //Initializers // __ERC1155RolesUpgradable_init(""); __ProtocolEntity_init(hub); - // __setTargetContract(IAssoc(address(_HUB)).getAssoc("avatar")); - __setTargetContract(repo().addressGetOf(address(_HUB), "avatar")); + __setTargetContract(repo().addressGetOf(address(_HUB), "SBT")); //Init Recursion Controls - // __Recursion_init(address(_HUB)); //DEPRECATED + // __Recursion_init(address(_HUB)); //CANCELLED //Set Contract URI _setContractURI(uri_); @@ -145,6 +145,11 @@ contract GameUpgradable is //Assign Creator as Admin & Member _roleAssign(tx.origin, "admin", 1); _roleAssign(tx.origin, "member", 1); + + /* 0.600Kb */ + //Register as a Soul + address sbtContract = repo().addressGetOf(address(_HUB), "SBT"); + ISoul(sbtContract).mint(uri_); } //** Reaction Functions @@ -211,7 +216,7 @@ contract GameUpgradable is /// @param uri_ post URI function post(string calldata entRole, uint256 tokenId, string calldata uri_) external override { //Validate that User Controls The Token - require(ISoul( repo().addressGetOf(address(_HUB), "avatar") ).hasTokenControl(tokenId), "SOUL:NOT_YOURS"); + require(ISoul( repo().addressGetOf(address(_HUB), "SBT") ).hasTokenControl(tokenId), "SOUL:NOT_YOURS"); //Validate: Soul Assigned to the Role require(roleHasByToken(tokenId, entRole), "ROLE:NOT_ASSIGNED"); //Validate the Calling Account // require(roleHasByToken(tokenId, entRole), string(abi.encodePacked("TOKEN: ", tokenId, " NOT_ASSIGNED_AS: ", entRole)) ); //Validate the Calling Account diff --git a/contracts/ReactionUpgradable.sol b/contracts/ReactionUpgradable.sol index 7de825c..4e3076f 100644 --- a/contracts/ReactionUpgradable.sol +++ b/contracts/ReactionUpgradable.sol @@ -137,7 +137,7 @@ contract ReactionUpgradable is /// Get Soul Contract Address function getSoulAddr() internal view returns(address){ - return repo().addressGetOf(address(_HUB), "avatar"); + return repo().addressGetOf(address(_HUB), "SBT"); } /// Request to Join @@ -214,7 +214,7 @@ contract ReactionUpgradable is function post(string calldata entRole, uint256 tokenId, string calldata uri_) external override { //Validate that User Controls The Token // require(_hasTokenControl(tokenId), "SOUL:NOT_YOURS"); - // require(ISoul( IAssoc(address(_HUB)).getAssoc("avatar") ).hasTokenControl(tokenId), "SOUL:NOT_YOURS"); + // require(ISoul( IAssoc(address(_HUB)).getAssoc("SBT") ).hasTokenControl(tokenId), "SOUL:NOT_YOURS"); require(ISoul( getSoulAddr() ).hasTokenControl(tokenId), "SOUL:NOT_YOURS"); //Validate: Soul Assigned to the Role // require(roleHas(tx.origin, entRole), "ROLE:NOT_ASSIGNED"); //Validate the Calling Account @@ -336,8 +336,8 @@ contract ReactionUpgradable is /// Rule (Action) Confirmed (Currently Only Judging Avatars) function _ruleConfirmed(uint256 ruleId) internal { //Get Avatar Contract - // ISoul avatarContract = ISoul(_HUB.getAssoc("avatar")); - // ISoul avatarContract = ISoul(IAssoc(address(_HUB)).getAssoc("avatar")); + // ISoul avatarContract = ISoul(_HUB.getAssoc("SBT")); + // ISoul avatarContract = ISoul(IAssoc(address(_HUB)).getAssoc("SBT")); ISoul avatarContract = ISoul( getSoulAddr() ); diff --git a/scripts/deploy.ts b/scripts/deploy.ts index a736097..dab2baf 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -94,7 +94,7 @@ async function main() { try{ //Set as Avatars - if(!!contractAddr.avatar) await hubContract.setAssoc("avatar", contractAddr.avatar); + if(!!contractAddr.avatar) await hubContract.setAssoc("SBT", contractAddr.avatar); //Set as History if(!!contractAddr.history) await hubContract.setAssoc("history", contractAddr.history); } @@ -130,7 +130,7 @@ async function main() { if(!!hubContract){ //If Deployed Together try{ //Set to HUB - await hubContract.setAssoc("avatar", contractAddr.avatar); + await hubContract.setAssoc("SBT", contractAddr.avatar); //Log console.log("Registered Avatar Contract to Hub"); } diff --git a/scripts/hubSwap.ts b/scripts/hubSwap.ts index e9c8577..92a2d4b 100644 --- a/scripts/hubSwap.ts +++ b/scripts/hubSwap.ts @@ -27,7 +27,7 @@ async function main() { //New Hub let hubContract = await ethers.getContractFactory("HubUpgradable").then(res => res.attach(contractAddr.hub)); //Set Contract Associations - await hubContract.setAssoc("avatar", contractAddr.avatar); + await hubContract.setAssoc("SBT", contractAddr.avatar); await hubContract.setAssoc("history", contractAddr.history); } diff --git a/scripts/updateImplementations.ts b/scripts/updateImplementations.ts index 5a7769d..6d7c4d5 100644 --- a/scripts/updateImplementations.ts +++ b/scripts/updateImplementations.ts @@ -23,7 +23,7 @@ async function main() { // await hubContract.upgradeGameImplementation(contractAddr.game); //Set to HUB - await hubContract.setAssoc("avatar", contractAddr.avatar); + await hubContract.setAssoc("SBT", contractAddr.avatar); await hubContract.setAssoc("history", contractAddr.history); } diff --git a/test/Hub.ts b/test/Hub.ts index bc456c3..3391034 100644 --- a/test/Hub.ts +++ b/test/Hub.ts @@ -84,8 +84,8 @@ describe("Hub", function () { ); //Set Avatar Contract to Hub - hubContract.setAssoc("avatar", avatarContract.address); - hubContract2.setAssoc("avatar", avatarContract.address); + hubContract.setAssoc("SBT", avatarContract.address); + hubContract2.setAssoc("SBT", avatarContract.address); //Deploy History // actionContract = await ethers.getContractFactory("ActionRepo").then(res => res.deploy(hubContract.address)); diff --git a/test/deployment.ts b/test/deployment.ts index 6cf8858..2aa74e9 100644 --- a/test/deployment.ts +++ b/test/deployment.ts @@ -105,7 +105,7 @@ describe("Deployment", function () { await proxyAvatar.deployed(); this.avatarContract = proxyAvatar; //Set Avatar Contract to Hub - hubContract.setAssoc("avatar", proxyAvatar.address); + hubContract.setAssoc("SBT", proxyAvatar.address); // console.log("SoulUpgradable deployed to:", proxyAvatar.address); }); From a4fa643a9ff2910af2315ec2ac05c9fdc870963b Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:11:05 -0700 Subject: [PATCH 3/9] Get, Track & Emit Soul Types --- contracts/SoulUpgradable.sol | 73 +++++++++++++++++++++------------- contracts/interfaces/ISoul.sol | 3 ++ 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/contracts/SoulUpgradable.sol b/contracts/SoulUpgradable.sol index 09d6526..0f502af 100644 --- a/contracts/SoulUpgradable.sol +++ b/contracts/SoulUpgradable.sol @@ -3,11 +3,10 @@ pragma solidity 0.8.4; import "hardhat/console.sol"; +// import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; -// import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; -// import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; @@ -44,10 +43,9 @@ contract SoulUpgradable is using CountersUpgradeable for CountersUpgradeable.Counter; CountersUpgradeable.Counter private _tokenIds; - //Positive & Negative Reputation Tracking Per Domain (Personal,Community,Professional) - // mapping(uint256 => mapping(DataTypes.Domain => mapping(DataTypes.Rating => uint256))) internal _rep; //[Token][Domain][bool] => Rep //Inherited from Opinions mapping(address => uint256) internal _owners; //Map Multiple Accounts to Tokens (Aliases) - + mapping(uint256 => string) public types; //Soul Types + mapping(uint256 => address) internal _link; //[TBD] Linked Souls //--- Modifiers @@ -138,7 +136,8 @@ contract SoulUpgradable is /// Mint (Create New Token for Someone Else) function mintFor(address to, string memory tokenURI) public override returns (uint256) { //Validate - Contract Owner - require(_msgSender() == owner(), "Only Owner"); + // require(_msgSender() == owner(), "Only Owner"); + require(_msgSender() == owner() || _msgSender() == address(_HUB), "Only Owner or Hub"); //Mint return _mint(to, tokenURI); } @@ -183,20 +182,52 @@ contract SoulUpgradable is //Mint _tokenIds.increment(); uint256 newItemId = _tokenIds.current(); - _safeMint(to, newItemId); + _mint(to, newItemId); //Set URI _setTokenURI(newItemId, uri); //This Goes for Specific Metadata Set (IPFS and Such) //Emit URI Changed Event emit URI(uri, newItemId); + //Soul Type + string memory soulType = _getType(to); + //Set + types[newItemId] = soulType; + //Emit Soul Type as Event + emit SoulType(newItemId, soulType); //Done return newItemId; } + /// Get Owner Type + function _getType(address account) private returns(string memory){ + + // console.log("** _getType() Return: ", response); + + if (account.isContract() && account != address(this)) { + + console.log("THIS IS A Contract:", account); + + try IToken(account).symbol() returns (string memory response) { + + // console.log("* * * Contract Symbol:", account, response); + + //Contract's Symbol + return response; + } catch { + //Unrecognized Contract + return "CONTRACT"; + } + } + // console.log("THIS IS NOT A Contract:", account); + //Not a contract + return ""; + } + /// Token Transfer Rules function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721Upgradeable) { super._beforeTokenTransfer(from, to, tokenId); - //Can't be owned by a Contract - require(to == address(this) || !to.isContract(), "Destination is a Contract"); + //Can't be owned by a Contract //CANCELLED - Allow Contracts to have Souls + // require(to == address(this) || !to.isContract(), "Destination is a Contract"); + //Non-Transferable (by client) require( _msgSender() == owner() @@ -221,24 +252,6 @@ contract SoulUpgradable is return true; } - /// Receiver Function For Holding NFTs on Contract - /// @dev needed in order to keep tokens in the contract - function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { - return this.onERC721Received.selector; - } - - /* Try without it, since we don't want any regular ERC1155 to be received - /// Receiver Function For Holding NFTs on Contract (Allow for internal NFTs to assume Roles) - function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4) { - return this.onERC1155Received.selector; - } - - /// Receiver Function For Holding NFTs on Contract - function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public pure returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } - */ - /// Check if the Current Account has Control over a Token function hasTokenControl(uint256 tokenId) public view override returns (bool) { address ownerAccount = ownerOf(tokenId); @@ -258,3 +271,9 @@ contract SoulUpgradable is } } + +/// Generic Interface used to get Symbol +interface IToken { + /// Arbitrary contract symbol + function symbol() external view returns (string memory); +} diff --git a/contracts/interfaces/ISoul.sol b/contracts/interfaces/ISoul.sol index 8152f69..b364b98 100644 --- a/contracts/interfaces/ISoul.sol +++ b/contracts/interfaces/ISoul.sol @@ -51,4 +51,7 @@ interface ISoul { /// General Post event Post(address indexed account, uint256 tokenId, string uri); + /// Soul Type Change + event SoulType(uint256 indexed tokenId, string soulType); + } From a9c6a373175498589ab06079bd056d11f8a24330 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:11:12 -0700 Subject: [PATCH 4/9] Typo --- contracts/abstract/ProxyMulti.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/abstract/ProxyMulti.sol b/contracts/abstract/ProxyMulti.sol index 16c1bd6..826874a 100644 --- a/contracts/abstract/ProxyMulti.sol +++ b/contracts/abstract/ProxyMulti.sol @@ -51,7 +51,7 @@ abstract contract ProxyMulti { } } //If Nothing Found - revert("NO_SUCH_ FUNCTION"); + revert("NO_SUCH_FUNCTION"); } /** From c9644f50192e0b4a9fb99beb55320efceda7372b Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:12:01 -0700 Subject: [PATCH 5/9] Change to Proxy Implementation's Failure --- contracts/GameUpgradable.sol | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/contracts/GameUpgradable.sol b/contracts/GameUpgradable.sol index 3bc29e2..ca14591 100644 --- a/contracts/GameUpgradable.sol +++ b/contracts/GameUpgradable.sol @@ -146,10 +146,7 @@ contract GameUpgradable is _roleAssign(tx.origin, "admin", 1); _roleAssign(tx.origin, "member", 1); - /* 0.600Kb */ - //Register as a Soul - address sbtContract = repo().addressGetOf(address(_HUB), "SBT"); - ISoul(sbtContract).mint(uri_); + } //** Reaction Functions @@ -258,12 +255,14 @@ contract GameUpgradable is /// Proxy Fallback Implementations function _implementations() internal view virtual override returns (address[] memory){ - // string memory gameType = confGet("type"); - require (!Utils.stringMatch(confGet("type"), ""), "NO_GAME_TYPE"); + address[] memory implementationAddresses; + string memory gameType = confGet("type"); + if(Utils.stringMatch(gameType, "")) return implementationAddresses; + // require (!Utils.stringMatch(gameType, ""), "NO_GAME_TYPE"); //UID - string memory gameType = string(abi.encodePacked("GAME_", confGet("type"))); + string memory gameTypeFull = string(abi.encodePacked("GAME_", gameType)); //Fetch Implementations - address[] memory implementationAddresses = repo().addressGetAllOf(address(_HUB), gameType); //Specific + implementationAddresses = repo().addressGetAllOf(address(_HUB), gameTypeFull); //Specific require(implementationAddresses.length > 0, "NO_FALLBACK_CONTRACT"); return implementationAddresses; } @@ -357,7 +356,7 @@ contract GameUpgradable is roleRemove(account, roleOld); } - /** DEPRECATE - Allow Uneven Role Distribution + /** TODO: DEPRECATE - Allow Uneven Role Distribution * @dev Hook that is called before any token transfer. This includes minting and burning, as well as batched variants. * - Max of Single Token for each account */ From 1452437eebe2dc203d87d867d21855bfcc1b67d7 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:12:32 -0700 Subject: [PATCH 6/9] Factory to mint a soul for a Game Contract after deployment --- contracts/HubUpgradable.sol | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/contracts/HubUpgradable.sol b/contracts/HubUpgradable.sol index 24ec964..dd10924 100644 --- a/contracts/HubUpgradable.sol +++ b/contracts/HubUpgradable.sol @@ -119,7 +119,7 @@ contract HubUpgradable is /// Update Hub function hubChange(address newHubAddr) external override onlyOwner { //Avatar - address avatarContract = repo().addressGet("avatar"); + address avatarContract = repo().addressGet("SBT"); if(avatarContract != address(0)){ try IProtocolEntity(avatarContract).setHub(newHubAddr){} //Failure should not be fatal catch Error(string memory /*reason*/) {} @@ -182,6 +182,15 @@ contract HubUpgradable is emit ContractCreated("game", address(newGameProxy)); //Remember _games[address(newGameProxy)] = true; + + //Register as a Soul + try ISoul(repo().addressGet("SBT")).mintFor(address(newGameProxy), uri_) {} //Failure should not be fatal + catch Error(string memory reason) { + console.log("Failed to mint a soul for the new Game Contract", reason); + } + + // repo().addressAdd("GAME", address(newGameProxy)); + //Return return address(newGameProxy); } @@ -223,7 +232,7 @@ contract HubUpgradable is //Validate - Known & Active Game require(_games[_msgSender()], "UNAUTHORIZED: Valid Game Only"); //Update Avatar's Reputation //TODO: Just Check if Contract Implements IRating - address avatarContract = repo().addressGet("avatar"); + address avatarContract = repo().addressGet("SBT"); if(avatarContract != address(0) && avatarContract == contractAddr){ _repAddAvatar(tokenId, domain, rating, amount); } @@ -231,7 +240,7 @@ contract HubUpgradable is /// Add Repuation to Avatar function _repAddAvatar(uint256 tokenId, string calldata domain, bool rating, uint8 amount) internal { - address avatarContract = repo().addressGet("avatar"); + address avatarContract = repo().addressGet("SBT"); try ISoul(avatarContract).repAdd(tokenId, domain, rating, amount) {} //Failure should not be fatal catch Error(string memory /*reason*/) {} } From 1dfcdf21f9b442ddf05b01073d5e8646bfad65a8 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:13:01 -0700 Subject: [PATCH 7/9] [TEST] Test for new Game Soul Token --- test/index.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/index.ts b/test/index.ts index f61bf7b..df97608 100644 --- a/test/index.ts +++ b/test/index.ts @@ -9,6 +9,7 @@ const ZERO_ADDR = '0x0000000000000000000000000000000000000000'; let test_uri = "ipfs://QmQxkoWcpFgMa7bCzxaANWtSt43J1iMgksjNnT4vM1Apd7"; //"TEST_URI"; let test_uri2 = "ipfs://TEST2"; let actionGUID = ""; +let soulTokenId = 0; //Try to keep track of Current Soul Token ID describe("Protocol", function () { //Contract Instances @@ -72,7 +73,7 @@ describe("Protocol", function () { avatarContract = await deployUUPS("SoulUpgradable", [hubContract.address]); //Set Avatar Contract to Hub - hubContract.setAssoc("avatar", avatarContract.address); + hubContract.setAssoc("SBT", avatarContract.address); //Deploy History // actionContract = await ethers.getContractFactory("ActionRepo").then(res => res.deploy(hubContract.address)); @@ -164,25 +165,28 @@ describe("Protocol", function () { describe("Soul", function () { it("Should inherit protocol owner", async function () { - expect(await avatarContract.owner()).to.equal(await owner.getAddress()); + expect(await avatarContract.owner()).to.equal(this.ownerAddr); }); it("Can mint only one", async function () { let tx = await avatarContract.connect(tester).mint(test_uri); + ++soulTokenId; tx.wait(); //Another One for Testing Purposes - avatarContract.connect(tester2).mint(test_uri); + await avatarContract.connect(tester2).mint(test_uri); + ++soulTokenId; // console.log("minting", tx); //Fetch Token let result = await avatarContract.ownerOf(1); //Check Owner - expect(result).to.equal(await tester.getAddress()); + expect(result).to.equal(this.testerAddr); //Check URI expect(await avatarContract.tokenURI(1)).to.equal(test_uri); //Another Call Should Fail await expect( avatarContract.connect(tester).mint(test_uri) - ).to.be.revertedWith("Requesting account already has an avatar"); + ).to.be.revertedWith("Requesting account already has a token"); + ++soulTokenId; }); it("Should Index Addresses", async function () { @@ -217,6 +221,7 @@ describe("Protocol", function () { await avatarContract.connect(tester).add(test_uri); await avatarContract.connect(tester).add(test_uri); let tx = await avatarContract.connect(tester).add(test_uri); + soulTokenId = soulTokenId + 3; tx.wait(); // console.log("minting", tx); //Fetch Token @@ -267,12 +272,12 @@ describe("Protocol", function () { }); it("Should NOT be transferable", async function () { - //Should Fail to transfer -- "Sorry, Assets are non-transferable" + //Should Fail to transfer -- "Sorry, assets are non-transferable" let fromAddr = await tester.getAddress(); let toAddr = await tester2.getAddress(); await expect( avatarContract.connect(tester).transferFrom(fromAddr, toAddr, 1) - ).to.be.revertedWith("Sorry, Assets are non-transferable"); + ).to.be.revertedWith("Sorry, assets are non-transferable"); }); it("Can update token's metadata", async function () { @@ -307,6 +312,7 @@ describe("Protocol", function () { await avatarContract.connect(tester4).mint(test_uri); await avatarContract.connect(tester5).mint(test_uri); await avatarContract.connect(authority).mint(test_uri); + soulTokenId = soulTokenId + 5; //Simulate to Get New Game Address let JAddr = await hubContract.callStatic.gameMake("Test Game", test_uri); @@ -319,6 +325,9 @@ describe("Protocol", function () { expect(JAddr).to.be.properAddress; //Expect Reaction Created Event await expect(tx).to.emit(hubContract, 'ContractCreated').withArgs("game", JAddr); + await expect(tx).to.emit(avatarContract, 'SoulType').withArgs(soulTokenId, "GAME"); + // console.log("Current soulTokenId", soulTokenId); + ++soulTokenId; //Init Game Contract Object gameContract = await ethers.getContractFactory("GameUpgradable").then(res => res.attach(JAddr)); this.gameContract = gameContract; From f49d113bcf62be7164d40c9b0c80bb215fa53723 Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:13:57 -0700 Subject: [PATCH 8/9] Changed mutability to view --- contracts/SoulUpgradable.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SoulUpgradable.sol b/contracts/SoulUpgradable.sol index 0f502af..0e4755c 100644 --- a/contracts/SoulUpgradable.sol +++ b/contracts/SoulUpgradable.sol @@ -198,7 +198,7 @@ contract SoulUpgradable is } /// Get Owner Type - function _getType(address account) private returns(string memory){ + function _getType(address account) private view returns(string memory){ // console.log("** _getType() Return: ", response); From f0ffb64fad4047ad121fd6e9606fca1f90da08ea Mon Sep 17 00:00:00 2001 From: Roy Date: Sun, 10 Jul 2022 18:14:30 -0700 Subject: [PATCH 9/9] Cleanup --- contracts/SoulUpgradable.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SoulUpgradable.sol b/contracts/SoulUpgradable.sol index 0e4755c..3a1b220 100644 --- a/contracts/SoulUpgradable.sol +++ b/contracts/SoulUpgradable.sol @@ -204,7 +204,7 @@ contract SoulUpgradable is if (account.isContract() && account != address(this)) { - console.log("THIS IS A Contract:", account); + // console.log("THIS IS A Contract:", account); try IToken(account).symbol() returns (string memory response) {