From 6e563c420f20ef7f36f35fd9cbd3e5bcf4908929 Mon Sep 17 00:00:00 2001 From: Captain Unknown Date: Wed, 7 Aug 2024 04:04:39 +0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=96=20Added=20basic=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/AccessManager.sol | 75 ++++++++++++++++++++++++++++--- contracts/Actor.sol | 31 ++++++++++++- contracts/ActorsManager.sol | 53 ++++++++++++++++++++-- contracts/Batch.sol | 31 ++++++++++++- contracts/BatchManager.sol | 64 ++++++++++++++++++++++++-- contracts/SupplyChain.sol | 59 +++++++++++++++++++++++- contracts/mocks/AccessManager.sol | 3 -- contracts/mocks/BatchManager.sol | 2 - 8 files changed, 296 insertions(+), 22 deletions(-) diff --git a/contracts/AccessManager.sol b/contracts/AccessManager.sol index 6c0b00b..9f6ead5 100644 --- a/contracts/AccessManager.sol +++ b/contracts/AccessManager.sol @@ -22,78 +22,124 @@ contract AccessManager is AccessControl, AccessControlEnumerable { event CompanyUserRoleRevoked(address indexed companyUser, uint256 timestamp); /** - * @notice To check if the party is TODO documentation - */ + * @dev Throws `UnAuthorized` if called by any account other than the Admin or Default (Super) Admin. + */ modifier onlyClearanceLevelA() { if (!(hasRole(ADMIN_ROLE, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender))) revert Errors.UnAuthorized("ADMIN_ROLE"); _; } + /** + * @dev Throws `UnAuthorized` if called by any account other than company user, Admin or Default (Super) Admin. + */ modifier onlyClearanceLevelB() { if (!(hasRole(COMPANY_USER_ROLE, msg.sender) || hasRole(ADMIN_ROLE, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender))) revert Errors.UnAuthorized("COMPANY_USER_ROLE"); _; } + /** + * @dev Sets the Default (Super) Admin. + */ constructor(address defaultAdmin) { _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin); } /// Authorized Contract Role + /** + * @dev grants the AUTHORIZED_CONTRACT_ROLE to the provided address. + * Necessary for all guarded external calls in between contracts. + */ function grantAuthorizedContractRole(address contractAddress) public onlyClearanceLevelA { _grantRole(AUTHORIZED_CONTRACT_ROLE, contractAddress); emit AuthorizedContractRoleGranted(contractAddress, block.timestamp); } + /** + * @dev revokes the AUTHORIZED_CONTRACT_ROLE from the provided address. + */ function revokeAuthorizedContractRole(address contractAddress) public onlyClearanceLevelA { _revokeRole(AUTHORIZED_CONTRACT_ROLE, contractAddress); emit AuthorizedContractRoleRevoked(contractAddress, block.timestamp); } /// Admin Role + /** + * @dev grants the ADMIN_ROLE to the provided address. + * Admins can only be appointed by the Default (Super) Admin. + */ function grantAdminRole(address account) public onlyRole(DEFAULT_ADMIN_ROLE) { _grantRole(ADMIN_ROLE, account); emit AdminRoleGranted(account, block.timestamp); } + /** + * @dev revokes the ADMIN_ROLE from the provided address. + */ function revokeAdminRole(address account) public onlyRole(DEFAULT_ADMIN_ROLE) { _revokeRole(ADMIN_ROLE, account); emit AdminRoleRevoked(account, block.timestamp); } /// Company User Role + /** + * @dev grants the COMPANY_USER_ROLE to the provided address. + * Company User can only be appointed by the by actors of clearance level A. + * Company User can perform management ops ('with write access' as specified in the specs). + */ function grantCompanyUserRole(address account) public onlyClearanceLevelA { _grantRole(COMPANY_USER_ROLE, account); emit CompanyUserRoleGranted(account, block.timestamp); } + /** + * @dev revokes the COMPANY_USER_ROLE from the provided address. + */ function revokeCompanyUserRole(address account) public onlyClearanceLevelA { _revokeRole(COMPANY_USER_ROLE, account); emit CompanyUserRoleRevoked(account, block.timestamp); } /// Consumer Role + /** + * @dev grants the CONSUMER_ROLE to the provided address. + * Consumers gain read-only access, this role can be utilized in a permissioned chain. + */ function grantConsumerRole(address account) public onlyClearanceLevelB { _grantRole(CONSUMER_ROLE, account); } + /** + * @dev revokes the CONSUMER_ROLE from the provided address. + */ function revokeConsumerRole(address account) public onlyClearanceLevelB { _revokeRole(CONSUMER_ROLE, account); } /// Super User | Default Admin role + /** + * @dev grants the DEFAULT_ADMIN_ROLE to the provided account. + * This is an irreversible step unless the given account agrees to transfer back the role. + */ function transferDefaultAdminRole(address account) public onlyRole(DEFAULT_ADMIN_ROLE) { _revokeRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(DEFAULT_ADMIN_ROLE, account); emit DefaultAdminRoleTransferred(account, msg.sender, block.timestamp); } + /** + * @dev To retrieve the current Default (Super) Admin. + * @return The address with the DEFAULT_ADMIN_ROLE. + */ function getCurrentDefaultAdmin() public view returns (address) { return getRoleMember(DEFAULT_ADMIN_ROLE, 0); } - // Get All Authorized Contracts + /** + * @dev To get All Authorized Contracts. + * @return The addresses with the AUTHORIZED_CONTRACT_ROLE. + */ function getCurrentContracts() public view returns (address[] memory) { uint256 roleMemberCount = getRoleMemberCount(AUTHORIZED_CONTRACT_ROLE); address[] memory contracts = new address[](roleMemberCount); @@ -104,7 +150,10 @@ contract AccessManager is AccessControl, AccessControlEnumerable { return contracts; } - // Get All Admins + /** + * @dev To get All Admins. + * @return The addresses with the ADMIN_ROLE. + */ function getCurrentAdmins() public view returns (address[] memory) { uint256 roleMemberCount = getRoleMemberCount(ADMIN_ROLE); address[] memory admins = new address[](roleMemberCount); @@ -115,7 +164,10 @@ contract AccessManager is AccessControl, AccessControlEnumerable { return admins; } - // Get All Company Users + /** + * @dev To get All Company Users. + * @return The addresses with the COMPANY_USER_ROLE. + */ function getCurrentCompanyUsers(uint256 query) public view returns (address[] memory) { uint256 roleMemberCount = getRoleMemberCount(COMPANY_USER_ROLE); if (roleMemberCount < query) query = roleMemberCount; @@ -127,7 +179,10 @@ contract AccessManager is AccessControl, AccessControlEnumerable { return companyUsers; } - // Get All Consumers + /** + * @dev To get All Consumers. + * @return The addresses with the CONSUMER_ROLE. + */ function getCurrentConsumers(uint256 query) public view returns (address[] memory) { uint256 roleMemberCount = getRoleMemberCount(CONSUMER_ROLE); if (roleMemberCount < query) query = roleMemberCount; @@ -140,6 +195,10 @@ contract AccessManager is AccessControl, AccessControlEnumerable { } /// Necessary Overrides: + /** + * @dev To grant a custom role. + * @return success status. + */ function _grantRole(bytes32 role, address account) internal override(AccessControl, AccessControlEnumerable) @@ -148,6 +207,10 @@ contract AccessManager is AccessControl, AccessControlEnumerable { return super._grantRole(role, account); } + /** + * @dev To revoke a custom role. + * @return success status. + */ function _revokeRole(bytes32 role, address account) internal override(AccessControl, AccessControlEnumerable) diff --git a/contracts/Actor.sol b/contracts/Actor.sol index bb32a3f..e563dcf 100644 --- a/contracts/Actor.sol +++ b/contracts/Actor.sol @@ -8,7 +8,11 @@ import { ERC721URIStorage } from "@openzeppelin/contracts/token/ERC721/extension import { AccessManager } from "./AccessManager.sol"; import { Errors } from "./Errors.sol"; -/// @custom:security-contact @captainunknown7@gmail.com +/** +* @title An ERC721 collection of actors. +* @dev A soul-bound NFT for identity management. +* @custom:security-contact @captainunknown7@gmail.com +*/ contract Actor is ERC721, ERC721Enumerable, ERC721URIStorage { uint256 private _nextActorId; AccessManager public acl; @@ -20,6 +24,9 @@ contract Actor is ERC721, ERC721Enumerable, ERC721URIStorage { _; } + /** + * @dev Sets the ACL and determines the hash AUTHORIZED_CONTRACT_ROLE. + */ constructor(address aclAddress, string memory actorType, string memory prefix) ERC721(actorType, prefix) { acl = AccessManager(aclAddress); AUTHORIZED_CONTRACT_ROLE = acl.AUTHORIZED_CONTRACT_ROLE(); @@ -29,6 +36,13 @@ contract Actor is ERC721, ERC721Enumerable, ERC721URIStorage { return "ipfs://"; } + /** + * @dev To register a new actor of any kind. + * Can only by called an external contract which would be `ActorsManager`. + * @param The receiver of the ID. + * @param The hash of the ID. + * @return The registered Actor ID. + */ function registerActor(address account, string calldata hash) external onlyAuthorizedContract @@ -41,6 +55,12 @@ contract Actor is ERC721, ERC721Enumerable, ERC721URIStorage { return actorId; } + /** + * @dev To update an actor of any kind. + * Can only by called an external contract which would be `ActorsManager`. + * @param Actor ID to update. + * @param The new hash. + */ function updateActor(uint256 actorId, string memory newHash) external onlyAuthorizedContract @@ -48,14 +68,23 @@ contract Actor is ERC721, ERC721Enumerable, ERC721URIStorage { _setTokenURI(actorId, newHash); } + /** + * @dev Reverts with `SoulBoundTransferNotAllowed`. + */ function transferFrom(address /*from*/, address /*to*/, uint256 /*tokenId*/) public pure override(IERC721, ERC721) { revert Errors.SoulBoundTransferNotAllowed(); } + /** + * @dev Reverts with `SoulBoundTransferNotAllowed`. + */ function safeTransferFrom(address /*from*/, address /*to*/, uint256 /*tokenId*/, bytes memory /*data*/) public pure override(IERC721, ERC721) { revert Errors.SoulBoundTransferNotAllowed(); } + /** + * @return Whether the id has been issued or not. + */ function idExists(uint256 id) public view returns(bool) { return id < totalSupply(); } diff --git a/contracts/ActorsManager.sol b/contracts/ActorsManager.sol index 8332763..b4402dc 100644 --- a/contracts/ActorsManager.sol +++ b/contracts/ActorsManager.sol @@ -9,7 +9,11 @@ import { Errors } from "./Errors.sol"; import { FunctionsClient } from "@chainlink/contracts@1.2.0/src/v0.8/functions/v1_0_0/FunctionsClient.sol"; import { FunctionsRequest } from "@chainlink/contracts@1.2.0/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol"; -/// @custom:security-contact @captainunknown7@gmail.com +/** +* @title Actors Manager. +* @dev Aggregates the collections for all actor types & performs the necessary validation. +* @custom:security-contact @captainunknown7@gmail.com +*/ contract ActorsManager is FunctionsClient { using FunctionsRequest for FunctionsRequest.Request; AccessManager public acl; @@ -41,6 +45,7 @@ contract ActorsManager is FunctionsClient { string hash; } mapping(bytes32 => RequestInfo) private lastValidationRequest; + string validationSource = "const actorType = args[0];" "const hash = args[1];" @@ -64,6 +69,10 @@ contract ActorsManager is FunctionsClient { _; } + /** + * @dev Sets the ACL and determines the hash AUTHORIZED_CONTRACT_ROLE. + * Along with the Chainlink Configuration. + */ constructor(address aclAddress, bytes32 _donId, address _donRouter, uint64 _donSubscriptionId) FunctionsClient(_donRouter) { @@ -82,6 +91,12 @@ contract ActorsManager is FunctionsClient { AUTHORIZED_CONTRACT_ROLE = acl.AUTHORIZED_CONTRACT_ROLE(); } + /** + * @dev Creates the batch & updates the on-chain state if the metadata validation succeeds. + * @param The type of the actor to register. + * @param The account to receive the identification NFT. + * @param The hash of the metadata of the actor. + */ function registerActor(uint8 actorType, address account, string calldata hash) public onlyValidActorType(actorType) @@ -96,6 +111,13 @@ contract ActorsManager is FunctionsClient { }); } + /** + * @dev Updates the metadata of the actor if the metadata validation succeeds. + * @param The actor type. + * @param The actor ID to replace the hash of. + * @param The hash of the actor. + * @param Callback selector, which calls a post validation function in SupplyChain to perform update. + */ function updateActor(uint8 actorType, uint256 actorId, string calldata hash) public onlyValidActorType(actorType) @@ -110,6 +132,12 @@ contract ActorsManager is FunctionsClient { }); } + /** + * @dev To retrieve the actor URI. + * @param The type of the actor. + * @param The ID of the actor. + * @return The hash of the batch. + */ function getActorURI(uint8 actorType, uint256 actorId) public view @@ -119,6 +147,13 @@ contract ActorsManager is FunctionsClient { return actors[actorType].tokenURI(actorId); } + /** + * @dev To retrieve the batch URIs in a chunk, chunk size cannot exceed 100. + * @param The type of the actor. + * @param The starting index (ID) of the actors. + * @param Total request size. + * @return The hashes of the actors. + */ function getActorsURIsInBatch(uint8 actorType, uint256 cursor, uint256 pageSize) public view @@ -141,7 +176,11 @@ contract ActorsManager is FunctionsClient { return actorURIs; } - // Chainlink Metadata Validation Function + /** + * @dev An internal function to be called to send a validation request. + * @param The hash of the metadata to be validated. + * @return The DON Function request ID. + */ function validateMetadata(uint8 actorType, string calldata hash) internal returns(bytes32) { FunctionsRequest.Request memory req; req.initializeRequestForInlineJavaScript(validationSource); @@ -157,10 +196,16 @@ contract ActorsManager is FunctionsClient { ); } + /** + * @dev An internal function to be called by the donRouter. + * @param The validation request ID. + * @param The response from the DON Function. + * @return The error from the DON Function (if any). + */ function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { - // TODO: Request Id Check - // if (s_lastRequestId != requestId) revert UnexpectedRequestID(requestId); RequestInfo memory info = lastValidationRequest[requestId]; + if (bytes(info.hash).length == 0) revert Errors.UnexpectedRequestID(requestId); + uint256 actorId = info.actorId; uint8 actorType = uint8(info.actorType); string memory hash = info.hash; diff --git a/contracts/Batch.sol b/contracts/Batch.sol index de054d4..c9b348d 100644 --- a/contracts/Batch.sol +++ b/contracts/Batch.sol @@ -8,7 +8,11 @@ import { ERC721URIStorage } from "@openzeppelin/contracts/token/ERC721/extension import { AccessManager } from "./AccessManager.sol"; import { Errors } from "./Errors.sol"; -/// @custom:security-contact @captainunknown7@gmail.com +/** +* @title An ERC721 collection of batches. +* @dev A soul-bound NFT to identify batches as dNFTs held by the `SupplyChain` contract. +* @custom:security-contact @captainunknown7@gmail.com +*/ contract Batch is ERC721, ERC721Enumerable, ERC721URIStorage { uint256 private _batchId; AccessManager public acl; @@ -20,6 +24,9 @@ contract Batch is ERC721, ERC721Enumerable, ERC721URIStorage { _; } + /** + * @dev Sets the ACL and determines the hash AUTHORIZED_CONTRACT_ROLE. + */ constructor(address aclAddress, string memory batchName, string memory batchSymbol) ERC721(batchName, batchSymbol) { acl = AccessManager(aclAddress); AUTHORIZED_CONTRACT_ROLE = acl.AUTHORIZED_CONTRACT_ROLE(); @@ -29,6 +36,13 @@ contract Batch is ERC721, ERC721Enumerable, ERC721URIStorage { return "ipfs://"; } + /** + * @dev To create a new batch with the provided hash. + * Can only by called an external contract which would be `BatchManager`. + * @param The receiver of the batch. It'd be `SupplyChain` when called as a registrar. + * @param The hash of the Batch. + * @return The registered Batch ID. + */ function createBatch(address account, string calldata hash) external onlyAuthorizedContract @@ -40,6 +54,12 @@ contract Batch is ERC721, ERC721Enumerable, ERC721URIStorage { return batchId; } + /** + * @dev To update batch hash. + * Can only by called an external contract which would be `BatchManager`. + * @param Batch ID to update. + * @param The new hash to be updated with. + */ function updateBatch(uint256 batchId, string memory newHash) external onlyAuthorizedContract @@ -47,14 +67,23 @@ contract Batch is ERC721, ERC721Enumerable, ERC721URIStorage { _setTokenURI(batchId, newHash); } + /** + * @dev Reverts with `SoulBoundTransferNotAllowed`. + */ function transferFrom(address /*from*/, address /*to*/, uint256 /*tokenId*/) public pure override(IERC721, ERC721) { revert Errors.SoulBoundTransferNotAllowed(); } + /** + * @dev Reverts with `SoulBoundTransferNotAllowed`. + */ function safeTransferFrom(address /*from*/, address /*to*/, uint256 /*tokenId*/, bytes memory /*data*/) public pure override(IERC721, ERC721) { revert Errors.SoulBoundTransferNotAllowed(); } + /** + * @return Whether the id has been issued or not. + */ function idExists(uint256 id) public view returns(bool) { return id < totalSupply(); } diff --git a/contracts/BatchManager.sol b/contracts/BatchManager.sol index 5180ed7..b7c3996 100644 --- a/contracts/BatchManager.sol +++ b/contracts/BatchManager.sol @@ -10,7 +10,11 @@ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableS import { FunctionsClient } from "@chainlink/contracts@1.2.0/src/v0.8/functions/v1_0_0/FunctionsClient.sol"; import { FunctionsRequest } from "@chainlink/contracts@1.2.0/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol"; -/// @custom:security-contact @captainunknown7@gmail.com +/** +* @title Batch Aggregator. +* @dev Maintains the necessary on-chain batch state, keeping in sync with the underlying collection. +* @custom:security-contact @captainunknown7@gmail.com +*/ contract BatchManager is FunctionsClient { using FunctionsRequest for FunctionsRequest.Request; using EnumerableSet for EnumerableSet.UintSet; @@ -88,6 +92,10 @@ contract BatchManager is FunctionsClient { event BatchCreated(uint256 indexed batchId, string hash, uint256 timestamp); event BatchStatusUpdated(uint256 indexed batchId, BatchState state, string hash, uint256 timestamp); + /** + * @dev Sets the ACL and determines the hash AUTHORIZED_CONTRACT_ROLE. + * Along with the Chainlink Configuration & finally the address of the `SupplyChain` contract. + */ constructor(address aclAddress, address _supplyChainContract, bytes32 _donId, address _donRouter, uint64 _donSubscriptionId) FunctionsClient(_donRouter) { @@ -103,6 +111,12 @@ contract BatchManager is FunctionsClient { supplyChainContract = _supplyChainContract; } + /** + * @dev Creates the batch & updates the on-chain state if the metadata validation succeeds. + * @param The ID of the farmer who created the batch. + * @param The hash of the metadata of the batch. + * @param Callback selector, which calls a post validation function in SupplyChain to perform creation. + */ function createBatch(uint256 _farmerId, string calldata hash, bytes4 _callbackFunction) external onlyAuthorizedContract @@ -132,6 +146,12 @@ contract BatchManager is FunctionsClient { }); } + /** + * @dev Updates the metadata of the batch if the metadata validation succeeds. + * @param The new updated batch info itself. + * @param The hash of the new dynamically updated metadata. + * @param Callback selector, which calls a post validation function in SupplyChain to perform update. + */ function updateBatch(BatchInfo calldata _batch, string calldata hash, bytes4 _callbackFunction) external onlyAuthorizedContract @@ -147,6 +167,11 @@ contract BatchManager is FunctionsClient { }); } + /** + * @dev To retrieve the batch URI. + * @param The ID of the batch. + * @return The hash of the batch. + */ function getBatchURI(uint256 batchId) public view @@ -155,6 +180,12 @@ contract BatchManager is FunctionsClient { return batches.tokenURI(batchId); } + /** + * @dev To retrieve the batch URIs in a chunk, chunk size cannot exceed 100. + * @param The starting index (ID) of the batches. + * @param Total request size. + * @return The hashes of the batches. + */ function getBatchURIsInBatch(uint256 cursor, uint256 pageSize) public view @@ -176,7 +207,11 @@ contract BatchManager is FunctionsClient { return batchURIs; } - // Chainlink Metadata Validation Function + /** + * @dev An internal function to be called to send a validation request. + * @param The hash of the metadata to be validated. + * @return The DON Function request ID. + */ function validateMetadata(string calldata hash) internal returns(bytes32) { FunctionsRequest.Request memory req; req.initializeRequestForInlineJavaScript(validationSource); @@ -191,10 +226,16 @@ contract BatchManager is FunctionsClient { ); } + /** + * @dev An internal function to be called by the donRouter. + * @param The validation request ID. + * @param The response from the DON Function. + * @return The error from the DON Function (if any). + */ function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { - // TODO: Request Id Check - // if (s_lastRequestId != requestId) revert UnexpectedRequestID(requestId); RequestInfo memory info = lastValidationRequest[requestId]; + if (bytes(info.hash).length == 0) revert Errors.UnexpectedRequestID(requestId); + uint256 _batchId = info.batchId; string memory hash = info.hash; @@ -223,14 +264,26 @@ contract BatchManager is FunctionsClient { if(!success) revert Errors.FulfillmentFailed(); } + /** + * @dev A guarded function to change the `SupplyChain` contract address. + * @param The new `SupplyChain` contract address. + */ function setSupplyChainAddress(address _supplyChainAddress) public onlyAdminRole { supplyChainContract = _supplyChainAddress; } + /** + * @dev A guarded function to update the on-chain batch state, to be called by the `SupplyChain` contract. + * @param The batch ID to set the batch for. + * @param The new batch info itself. + */ function setBatch(uint256 _batchId, BatchInfo calldata batch) external onlyAuthorizedContract { batchInfoForId[_batchId] = batch; } + /** + * @dev To get the actors involved in a particular batch, a read-only external function. + */ function getUpdatedBatchActors(uint256 _batchId) external view @@ -256,6 +309,9 @@ contract BatchManager is FunctionsClient { ); } + /** + * @dev To get the getBatchFarmerId of a particular batch, a read-only external function. + */ function getBatchFarmerId(uint256 _batchId) external view diff --git a/contracts/SupplyChain.sol b/contracts/SupplyChain.sol index 4bf05e5..916a196 100644 --- a/contracts/SupplyChain.sol +++ b/contracts/SupplyChain.sol @@ -9,7 +9,10 @@ import { ActorsManager } from "./ActorsManager.sol"; import { Actor } from "./Actor.sol"; import { Errors } from "./Errors.sol"; -/// @custom:security-contact @captainunknown7@gmail.com +/** +* @title The Core DNFT based SupplyChain. +* @custom:security-contact @captainunknown7@gmail.com +*/ contract SupplyChain { using EnumerableSet for EnumerableSet.UintSet; AccessManager public acl; @@ -41,6 +44,10 @@ contract SupplyChain { mapping(uint256 => BatchIdsForActors) private distributors; mapping(uint256 => BatchIdsForActors) private retailers; + /** + * @dev Sets the ACL and determines the hash AUTHORIZED_CONTRACT_ROLE. + * And handles the deployment of the `BatchManager` contract. + */ constructor(address aclAddress, address _actorsManager, bytes32 _donId, address _donRouter, uint64 _donSubscriptionId) { batchManager = new BatchManager(aclAddress, address(this), _donId, _donRouter, _donSubscriptionId); @@ -49,6 +56,11 @@ contract SupplyChain { actorsManager = ActorsManager(_actorsManager); } + /** + * @dev A callback function to be called by the `BatchManager` upon batch creation metadata validation. + * @param Batch ID of the newly created batch. + * @return The success status if the batch is logged in the ledger, upon a failure it reverts with `FulfillmentFailed`. + */ function performBatchCreation(uint256 _batchId) external onlyBatchManager @@ -57,6 +69,12 @@ contract SupplyChain { return farmers[batchManager.getBatchFarmerId(_batchId)].batchIds.add(_batchId); } + /** + * @dev A callback function to be called by the `BatchManager` upon the dynamic batch update metadata validation. + * Note that the on-chain info is managed by the `BatchManager` in sync with this function. + * @param Batch ID of the updated batch. + * @return The success status if the batch is logged to the new actor, upon a failure it reverts with `FulfillmentFailed`. + */ function performBatchUpdate(uint256 _batchId) external onlyBatchManager @@ -86,10 +104,21 @@ contract SupplyChain { return true; // Batch Update not necessary for intermediary stages i.e storage/transit etc } + /** + * @dev To add a newly harvested batch. Creates a new instance of the batch & validates the metadata. + * @param Farmer ID of the Harvester of the batch. + * @param The hash of the harvested batch. + */ function addHarvestedBatch(uint256 farmerId, string calldata hash) public onlyCompanyUser { batchManager.createBatch(farmerId, hash, this.performBatchCreation.selector); } + /** + * @dev To update the batch state, once the batch moves onto any of the next phases. + * Performs necessary validation of the metadata & updates the DNFT. + * @param The updated Batch info itself. + * @param The updated hash of the batch. + */ function updateBatchState(BatchManager.BatchInfo calldata _batch, string calldata hash) public onlyCompanyUser @@ -97,26 +126,54 @@ contract SupplyChain { batchManager.updateBatch(_batch, hash, this.performBatchUpdate.selector); } + /** + * @dev To retrieve all the batches of a particular farmer. + * @param The farmer ID to retrieve the batches for. + * @return The IDs of the batches the farmer harvested. + */ function getBatchesHarvested(uint256 farmerId) public view returns (uint256[] memory) { return farmers[farmerId].batchIds.values(); } + /** + * @dev To retrieve all the batches of a particular processor. + * @param The processor ID to retrieve the batches for. + * @return The IDs of the batches the processor ever processed. + */ function getBatchesProcessed(uint256 processorId) public view returns (uint256[] memory) { return processors[processorId].batchIds.values(); } + /** + * @dev To retrieve all the batches of a particular packager. + * @param The packager ID to retrieve the batches for. + * @return The IDs of the batches the packager ever packaged. + */ function getBatchesPackaged(uint256 packagerId) public view returns (uint256[] memory) { return packagers[packagerId].batchIds.values(); } + /** + * @dev To retrieve all the batches of a particular distributor. + * @param The distributor ID to retrieve the batches for. + * @return The IDs of the batches the distributor was involved in. + */ function getBatchesDistributed(uint256 distributorId) public view returns (uint256[] memory) { return distributors[distributorId].batchIds.values(); } + /** + * @dev To retrieve all the batches of a particular retailer. + * @param The retailer ID to retrieve the batches for. + * @return The IDs of the batches the retailer was involved in. + */ function getBatchesRetailed(uint256 retailerId) public view returns (uint256[] memory) { return retailers[retailerId].batchIds.values(); } + /** + * @dev To interface with ERC721 & receive the batch DNFTs. + */ function onERC721Received(address, address, uint256, bytes calldata) external view returns(bytes4) { if(msg.sender == address(batchManager.batches())) return this.onERC721Received.selector; return bytes4(0); diff --git a/contracts/mocks/AccessManager.sol b/contracts/mocks/AccessManager.sol index 6c0b00b..188f822 100644 --- a/contracts/mocks/AccessManager.sol +++ b/contracts/mocks/AccessManager.sol @@ -21,9 +21,6 @@ contract AccessManager is AccessControl, AccessControlEnumerable { event CompanyUserRoleGranted(address indexed companyUser, uint256 timestamp); event CompanyUserRoleRevoked(address indexed companyUser, uint256 timestamp); - /** - * @notice To check if the party is TODO documentation - */ modifier onlyClearanceLevelA() { if (!(hasRole(ADMIN_ROLE, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender))) revert Errors.UnAuthorized("ADMIN_ROLE"); diff --git a/contracts/mocks/BatchManager.sol b/contracts/mocks/BatchManager.sol index c41ad95..7bcece0 100644 --- a/contracts/mocks/BatchManager.sol +++ b/contracts/mocks/BatchManager.sol @@ -175,8 +175,6 @@ contract BatchManager { } function fulfillRequest(uint256 requestId, bytes memory response, bytes memory err) public onlyAuthorizedContract { - // TODO: Request Id Check - // if (s_lastRequestId != requestId) revert UnexpectedRequestID(requestId); RequestInfo memory info = lastValidationRequest[requestId]; uint256 _batchId = info.batchId; string memory hash = info.hash;