diff --git a/contracts/contribution/ContributionNft.sol b/contracts/contribution/ContributionNft.sol index a725976..115a3fa 100644 --- a/contracts/contribution/ContributionNft.sol +++ b/contracts/contribution/ContributionNft.sol @@ -27,8 +27,14 @@ contract ContributionNft is mapping(uint256 => uint8) private _cores; mapping(uint256 => bool) public modelContributions; + mapping(uint256 => uint256) public modelDatasets; - event NewContribution(uint256 tokenId, uint256 virtualId, uint256 parentId); + event NewContribution( + uint256 tokenId, + uint256 virtualId, + uint256 parentId, + uint256 datasetId + ); address private _admin; // Admin is able to create contribution proposal without votes @@ -59,7 +65,8 @@ contract ContributionNft is string memory newTokenURI, uint256 proposalId, uint256 parentId, - bool isModel_ + bool isModel_, + uint256 datasetId ) external returns (uint256) { IGovernor personaDAO = getAgentDAO(virtualId); require( @@ -77,9 +84,10 @@ contract ContributionNft is if (isModel_) { modelContributions[proposalId] = true; + modelDatasets[proposalId] = datasetId; } - emit NewContribution(proposalId, virtualId, parentId); + emit NewContribution(proposalId, virtualId, parentId, datasetId); return proposalId; } @@ -100,7 +108,11 @@ contract ContributionNft is ) public view - override(IContributionNft, ERC721Upgradeable, ERC721URIStorageUpgradeable) + override( + IContributionNft, + ERC721Upgradeable, + ERC721URIStorageUpgradeable + ) returns (string memory) { return super.tokenURI(tokenId); @@ -125,7 +137,11 @@ contract ContributionNft is ) public view - override(ERC721Upgradeable, ERC721URIStorageUpgradeable, ERC721EnumerableUpgradeable) + override( + ERC721Upgradeable, + ERC721URIStorageUpgradeable, + ERC721EnumerableUpgradeable + ) returns (bool) { return super.supportsInterface(interfaceId); @@ -142,7 +158,11 @@ contract ContributionNft is address to, uint256 tokenId, address auth - ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (address) { + ) + internal + override(ERC721Upgradeable, ERC721EnumerableUpgradeable) + returns (address) + { return super._update(to, tokenId, auth); } @@ -155,4 +175,10 @@ contract ContributionNft is ) public view override(IERC721, ERC721Upgradeable) returns (address) { return _ownerOf(tokenId); } + + function getDatasetId( + uint256 tokenId + ) external view returns (uint256) { + return modelDatasets[tokenId]; + } } diff --git a/contracts/contribution/IContributionNft.sol b/contracts/contribution/IContributionNft.sol index ca60456..1612a14 100644 --- a/contracts/contribution/IContributionNft.sol +++ b/contracts/contribution/IContributionNft.sol @@ -9,4 +9,5 @@ interface IContributionNft { function getCore(uint256 tokenId) external view returns (uint8); function isModel(uint256 tokenId) external view returns (bool); function getAdmin() external view returns (address); + function getDatasetId(uint256 tokenId) external view returns (uint256); } diff --git a/contracts/contribution/IServiceNft.sol b/contracts/contribution/IServiceNft.sol index a6b552c..73c4490 100644 --- a/contracts/contribution/IServiceNft.sol +++ b/contracts/contribution/IServiceNft.sol @@ -2,23 +2,18 @@ pragma solidity ^0.8.20; interface IServiceNft { + function getCore(uint256 tokenId) external view returns (uint8); - function getMaturity(uint256 tokenId) external view returns (uint16); + function getMaturity(uint256 tokenId) external view returns (uint256); - function getImpact(uint256 tokenId) external view returns (uint16); + function getImpact(uint256 tokenId) external view returns (uint256); function getCoreService( uint256 virtualId, uint8 coreType ) external view returns (uint256); - event CoreServiceUpdated( - uint256 virtualId, - uint8 coreType, - uint256 serviceId - ); - function getCoreDatasetAt( uint256 virtualId, uint8 coreType, @@ -36,4 +31,22 @@ interface IServiceNft { ) external view returns (uint256[] memory); function getMintedAt(uint256 tokenId) external view returns (uint256); + + event CoreServiceUpdated( + uint256 virtualId, + uint8 coreType, + uint256 serviceId + ); + + event NewService( + uint256 tokenId, + uint8 coreId, + uint256 maturity, + uint256 impact, + bool isModel + ); + + event DatasetImpactUpdated(uint16 weight); + + event SetServiceScore(uint256 serviceId, uint256 eloRating, uint256 impact); } diff --git a/contracts/contribution/ServiceNft.sol b/contracts/contribution/ServiceNft.sol index 85a9466..ccd1709 100644 --- a/contracts/contribution/ServiceNft.sol +++ b/contracts/contribution/ServiceNft.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; -import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/interfaces/IERC5805.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../virtualPersona/IAgentNft.sol"; @@ -19,16 +19,19 @@ contract ServiceNft is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, - ERC721URIStorageUpgradeable + ERC721URIStorageUpgradeable, + OwnableUpgradeable { uint256 private _nextTokenId; address public personaNft; address public contributionNft; + uint16 public datasetImpactWeight; + mapping(uint256 tokenId => uint8 coreId) private _cores; - mapping(uint256 tokenId => uint16 maturity) private _maturities; - mapping(uint256 tokenId => uint16 impact) private _impacts; + mapping(uint256 tokenId => uint256 maturity) private _maturities; + mapping(uint256 tokenId => uint256 impact) private _impacts; mapping(uint256 tokenId => uint256 blockNumber) private _mintedAts; mapping(uint256 personaId => mapping(uint8 coreId => uint256 serviceId)) @@ -36,14 +39,6 @@ contract ServiceNft is mapping(uint256 personaId => mapping(uint8 coreId => uint256[] serviceId)) private _coreDatasets; - event NewService( - uint256 tokenId, - uint8 coreId, - uint16 maturity, - uint16 impact, - bool isModel - ); - function initialize( address initialAgentNft, address initialContributionNft @@ -57,8 +52,9 @@ contract ServiceNft is uint256 virtualId, bytes32 descHash ) external returns (uint256) { - IAgentNft.VirtualInfo memory info = IAgentNft(personaNft) - .virtualInfo(virtualId); + IAgentNft.VirtualInfo memory info = IAgentNft(personaNft).virtualInfo( + virtualId + ); require(_msgSender() == info.dao, "Caller is not VIRTUAL DAO"); IGovernor personaDAO = IGovernor(info.dao); @@ -85,20 +81,14 @@ contract ServiceNft is ); // Calculate maturity _maturities[proposalId] = IAgentDAO(info.dao).getMaturity(proposalId); - // Calculate impact - // Get current service maturity - uint256 prevServiceId = _coreServices[virtualId][_cores[proposalId]]; - - _impacts[proposalId] = _maturities[proposalId] > - _maturities[prevServiceId] - ? _maturities[proposalId] - _maturities[prevServiceId] - : 0; bool isModel = IContributionNft(contributionNft).isModel(proposalId); if (isModel) { _coreServices[virtualId][_cores[proposalId]] = proposalId; emit CoreServiceUpdated(virtualId, _cores[proposalId], proposalId); + + updateImpact(virtualId, proposalId); } else { _coreDatasets[virtualId][_cores[proposalId]].push(proposalId); } @@ -114,17 +104,38 @@ contract ServiceNft is return proposalId; } + function updateImpact(uint256 virtualId, uint256 proposalId) public { + // Calculate impact + // Get current service maturity + uint256 prevServiceId = _coreServices[virtualId][_cores[proposalId]]; + uint256 rawImpact = _maturities[proposalId] > _maturities[prevServiceId] + ? _maturities[proposalId] - _maturities[prevServiceId] + : 0; + uint256 datasetId = IContributionNft(contributionNft).getDatasetId( + proposalId + ); + + _impacts[proposalId] = rawImpact; + if (datasetId > 0) { + _impacts[datasetId] = (rawImpact * datasetImpactWeight) / 10000; + _impacts[proposalId] = rawImpact - _impacts[datasetId]; + + emit SetServiceScore(proposalId, _maturities[proposalId], _impacts[proposalId]); + emit SetServiceScore(datasetId, _maturities[proposalId], _impacts[datasetId]); + } + } + function getCore(uint256 tokenId) public view returns (uint8) { _requireOwned(tokenId); return _cores[tokenId]; } - function getMaturity(uint256 tokenId) public view returns (uint16) { + function getMaturity(uint256 tokenId) public view returns (uint256) { _requireOwned(tokenId); return _maturities[tokenId]; } - function getImpact(uint256 tokenId) public view returns (uint16) { + function getImpact(uint256 tokenId) public view returns (uint256) { _requireOwned(tokenId); return _impacts[tokenId]; } @@ -163,11 +174,21 @@ contract ServiceNft is return _coreDatasets[virtualId][coreType]; } + function setDatasetImpactWeight(uint16 weight) public onlyOwner { + datasetImpactWeight = weight; + emit DatasetImpactUpdated(weight); + } + // The following functions are overrides required by Solidity. function tokenURI( uint256 tokenId - ) public view override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) { + ) + public + view + override(ERC721Upgradeable, ERC721URIStorageUpgradeable) + returns (string memory) + { // Service NFT is a mirror of Contribution NFT return IContributionNft(contributionNft).tokenURI(tokenId); } @@ -177,7 +198,11 @@ contract ServiceNft is ) public view - override(ERC721Upgradeable, ERC721URIStorageUpgradeable, ERC721EnumerableUpgradeable) + override( + ERC721Upgradeable, + ERC721URIStorageUpgradeable, + ERC721EnumerableUpgradeable + ) returns (bool) { return super.supportsInterface(interfaceId); @@ -194,7 +219,11 @@ contract ServiceNft is address to, uint256 tokenId, address auth - ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (address) { + ) + internal + override(ERC721Upgradeable, ERC721EnumerableUpgradeable) + returns (address) + { return super._update(to, tokenId, auth); } } diff --git a/contracts/virtualPersona/AgentDAO.sol b/contracts/virtualPersona/AgentDAO.sol index ee8eae3..374ec71 100644 --- a/contracts/virtualPersona/AgentDAO.sol +++ b/contracts/virtualPersona/AgentDAO.sol @@ -200,15 +200,12 @@ contract AgentDAO is _proposalMaturities[proposalId] += (maturity * weight); - emit NewEloRating(proposalId, account, maturity, votes); + emit ValidatorEloRating(proposalId, account, maturity, votes); } - function getMaturity(uint256 proposalId) public view returns (uint16) { + function getMaturity(uint256 proposalId) public view returns (uint256) { (, uint256 forVotes, ) = proposalVotes(proposalId); - return - SafeCast.toUint16( - Math.min(10000, _proposalMaturities[proposalId] / forVotes) - ); + return Math.min(10000, _proposalMaturities[proposalId] / forVotes); } function quorum( diff --git a/contracts/virtualPersona/IAgentDAO.sol b/contracts/virtualPersona/IAgentDAO.sol index 6820721..829c5e9 100644 --- a/contracts/virtualPersona/IAgentDAO.sol +++ b/contracts/virtualPersona/IAgentDAO.sol @@ -22,7 +22,7 @@ interface IAgentDAO { uint256 timepoint ) external view returns (uint256); - function getMaturity(uint256 proposalId) external view returns (uint16); + function getMaturity(uint256 proposalId) external view returns (uint256); - event NewEloRating(uint256 proposalId, address voter, uint256 score, uint8[] votes); + event ValidatorEloRating(uint256 proposalId, address voter, uint256 score, uint8[] votes); }