Skip to content

Commit

Permalink
Natspec comments (#3)
Browse files Browse the repository at this point in the history
* Add interfaces

* Add doc comments for core contracts

* Add doc comments for modules
  • Loading branch information
Hrom131 committed Apr 2, 2024
1 parent 7f06c81 commit bedabe3
Show file tree
Hide file tree
Showing 17 changed files with 549 additions and 52 deletions.
2 changes: 2 additions & 0 deletions contracts/core/AgentAccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ abstract contract AgentAccessControl is
AgentAccessControlStorage,
DiamondAccessControl
{
/// @inheritdoc IAgentAccessControl
bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");

function __AgentAccessControl_init()
Expand All @@ -27,6 +28,7 @@ abstract contract AgentAccessControl is
grantRole(AGENT_ROLE, msg.sender);
}

/// @inheritdoc IAgentAccessControl
function checkRole(bytes32 role_, address account_) public view virtual {
_checkRole(role_, account_);
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/core/KYCCompliance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@ abstract contract KYCCompliance is IKYCCompliance, KYCComplianceStorage, AgentAc

function __KYCCompliance_init() internal onlyInitializing(KYC_COMPLIANCE_STORAGE_SLOT) {}

/// @inheritdoc IKYCCompliance
function addKYCModules(
address[] memory kycModules_
) public virtual onlyRole(_KYCComplianceRole()) {
_addKYCModules(kycModules_);
}

/// @inheritdoc IKYCCompliance
function removeKYCModules(
address[] memory kycModules_
) public virtual onlyRole(_KYCComplianceRole()) {
_removeKYCModules(kycModules_);
}

/// @inheritdoc IKYCCompliance
function isKYCed(TokenF.Context calldata ctx_) public view virtual returns (bool) {
address[] memory regulatoryModules_ = getKYCModules();

Expand Down
4 changes: 4 additions & 0 deletions contracts/core/RegulatoryCompliance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,21 @@ abstract contract RegulatoryCompliance is
onlyInitializing(REGULATORY_COMPLIANCE_STORAGE_SLOT)
{}

/// @inheritdoc IRegulatoryCompliance
function addRegulatoryModules(
address[] memory rModules_
) public virtual onlyRole(_regulatoryComplianceRole()) {
_addRegulatoryModules(rModules_);
}

/// @inheritdoc IRegulatoryCompliance
function removeRegulatoryModules(
address[] memory rModules_
) public virtual onlyRole(_regulatoryComplianceRole()) {
_removeRegulatoryModules(rModules_);
}

/// @inheritdoc IRegulatoryCompliance
function transferred(TokenF.Context calldata ctx_) public virtual onlyThis {
address[] memory regulatoryModules_ = getRegulatoryModules();

Expand All @@ -57,6 +60,7 @@ abstract contract RegulatoryCompliance is
}
}

/// @inheritdoc IRegulatoryCompliance
function canTransfer(TokenF.Context calldata ctx_) public view virtual returns (bool) {
address[] memory regulatoryModules_ = getRegulatoryModules();

Expand Down
54 changes: 25 additions & 29 deletions contracts/core/TokenF.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";

import {Diamond} from "@solarity/solidity-lib/diamond/Diamond.sol";
import {DiamondERC20} from "@solarity/solidity-lib/diamond/tokens/ERC20/DiamondERC20.sol";

import {ITokenF} from "../interfaces/ITokenF.sol";
import {IKYCCompliance} from "../interfaces/IKYCCompliance.sol";
import {IRegulatoryCompliance} from "../interfaces/IRegulatoryCompliance.sol";

Expand All @@ -12,34 +15,14 @@ import {TokenFStorage} from "./storages/TokenFStorage.sol";
import {RegulatoryComplianceStorage} from "./storages/RegulatoryComplianceStorage.sol";
import {KYCComplianceStorage} from "./storages/KYCComplianceStorage.sol";

/**
* @notice The TokenF contract
*
* The TokenF is a Diamond-based ERC20 token implementation enabling the storage of all core contracts under the same Diamond proxy.
*
* The TokenF provides flexibility for implementing eligibility checks through the integration of compliance modules without
* affecting the standard ERC20 behaviour.
*
* Transfer methods forward the entire transfer context to compliance modules, ensuring adherence to specific requirements,
* such as regulatory standards or KYC protocols.
*/
abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessControl {
abstract contract TokenF is ITokenF, TokenFStorage, Diamond, DiamondERC20, AgentAccessControl {
bytes4 public constant TRANSFER_SELECTOR = this.transfer.selector;
bytes4 public constant TRANSFER_FROM_SELECTOR = this.transferFrom.selector;
bytes4 public constant MINT_SELECTOR = this.mint.selector;
bytes4 public constant BURN_SELECTOR = this.burn.selector;
bytes4 public constant FORCED_TRANSFER_SELECTOR = this.forcedTransfer.selector;
bytes4 public constant RECOVERY_SELECTOR = this.recovery.selector;

struct Context {
bytes4 selector;
address from;
address to;
uint256 amount;
address operator;
bytes data;
}

function __TokenF_init(
address regulatoryCompliance_,
address kycCompliance_,
Expand Down Expand Up @@ -70,7 +53,11 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
_diamondCut(new Facet[](0), kycCompliance_, initKYC_);
}

function transfer(address to_, uint256 amount_) public virtual override returns (bool) {
/// @inheritdoc IERC20
function transfer(
address to_,
uint256 amount_
) public virtual override(DiamondERC20, IERC20) returns (bool) {
_canTransfer(msg.sender, to_, amount_, address(0));
_isKYCed(msg.sender, to_, amount_, address(0));

Expand All @@ -81,11 +68,12 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

/// @inheritdoc IERC20
function transferFrom(
address from_,
address to_,
uint256 amount_
) public virtual override returns (bool) {
) public virtual override(DiamondERC20, IERC20) returns (bool) {
_canTransfer(from_, to_, amount_, msg.sender);
_isKYCed(from_, to_, amount_, msg.sender);

Expand All @@ -96,10 +84,11 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

/// @inheritdoc ITokenF
function mint(
address account_,
uint256 amount_
) public virtual onlyRole(_mintRole()) returns (bool) {
) public virtual override onlyRole(_mintRole()) returns (bool) {
_canTransfer(address(0), account_, amount_, msg.sender);
_isKYCed(address(0), account_, amount_, msg.sender);

Expand All @@ -110,10 +99,11 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

/// @inheritdoc ITokenF
function burn(
address account_,
uint256 amount_
) public virtual onlyRole(_burnRole()) returns (bool) {
) public virtual override onlyRole(_burnRole()) returns (bool) {
_canTransfer(account_, address(0), amount_, msg.sender);
_isKYCed(account_, address(0), amount_, msg.sender);

Expand All @@ -124,11 +114,12 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

/// @inheritdoc ITokenF
function forcedTransfer(
address from_,
address to_,
uint256 amount_
) public virtual onlyRole(_forcedTransferRole()) returns (bool) {
) public virtual override onlyRole(_forcedTransferRole()) returns (bool) {
_canTransfer(from_, to_, amount_, msg.sender);
_isKYCed(from_, to_, amount_, msg.sender);

Expand All @@ -139,10 +130,11 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

/// @inheritdoc ITokenF
function recovery(
address oldAccount_,
address newAccount_
) public virtual onlyRole(_recoveryRole()) returns (bool) {
) public virtual override onlyRole(_recoveryRole()) returns (bool) {
uint256 oldBalance_ = balanceOf(oldAccount_);

_canTransfer(oldAccount_, newAccount_, oldBalance_, msg.sender);
Expand All @@ -155,15 +147,19 @@ abstract contract TokenF is TokenFStorage, Diamond, DiamondERC20, AgentAccessCon
return true;
}

function diamondCut(Facet[] memory modules_) public virtual onlyRole(_diamondCutRole()) {
/// @inheritdoc ITokenF
function diamondCut(
Facet[] memory modules_
) public virtual override onlyRole(_diamondCutRole()) {
diamondCut(modules_, address(0), "");
}

/// @inheritdoc ITokenF
function diamondCut(
Facet[] memory modules_,
address initModule_,
bytes memory initData_
) public virtual onlyRole(_diamondCutRole()) {
) public virtual override onlyRole(_diamondCutRole()) {
_diamondCut(modules_, initModule_, initData_);
}

Expand Down
14 changes: 9 additions & 5 deletions contracts/core/storages/KYCComplianceStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ pragma solidity ^0.8.20;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

abstract contract KYCComplianceStorage {
import {IKYCComplianceView} from "../../interfaces/IKYCComplianceView.sol";

abstract contract KYCComplianceStorage is IKYCComplianceView {
using EnumerableSet for EnumerableSet.AddressSet;

bytes32 internal constant KYC_COMPLIANCE_STORAGE_SLOT =
Expand All @@ -13,12 +15,14 @@ abstract contract KYCComplianceStorage {
EnumerableSet.AddressSet kycModules;
}

function getKYCModules() public view virtual returns (address[] memory) {
return _getKYCComplianceStorage().kycModules.values();
/// @inheritdoc IKYCComplianceView
function getKYCModulesCount() public view virtual override returns (uint256) {
return _getKYCComplianceStorage().kycModules.length();
}

function getKYCModulesCount() public view virtual returns (uint256) {
return _getKYCComplianceStorage().kycModules.length();
/// @inheritdoc IKYCComplianceView
function getKYCModules() public view virtual override returns (address[] memory) {
return _getKYCComplianceStorage().kycModules.values();
}

function _getKYCComplianceStorage() internal pure returns (KYCCStorage storage _kyccStorage) {
Expand Down
14 changes: 9 additions & 5 deletions contracts/core/storages/RegulatoryComplianceStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ pragma solidity ^0.8.20;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

abstract contract RegulatoryComplianceStorage {
import {IRegulatoryComplianceView} from "../../interfaces/IRegulatoryComplianceView.sol";

abstract contract RegulatoryComplianceStorage is IRegulatoryComplianceView {
using EnumerableSet for EnumerableSet.AddressSet;

bytes32 internal constant REGULATORY_COMPLIANCE_STORAGE_SLOT =
Expand All @@ -13,12 +15,14 @@ abstract contract RegulatoryComplianceStorage {
EnumerableSet.AddressSet regulatoryModules;
}

function getRegulatoryModules() public view returns (address[] memory) {
return _getRegulatoryComplianceStorage().regulatoryModules.values();
/// @inheritdoc IRegulatoryComplianceView
function getRegulatoryModulesCount() public view virtual override returns (uint256) {
return _getRegulatoryComplianceStorage().regulatoryModules.length();
}

function getRegulatoryModulesCount() public view virtual returns (uint256) {
return _getRegulatoryComplianceStorage().regulatoryModules.length();
/// @inheritdoc IRegulatoryComplianceView
function getRegulatoryModules() public view virtual override returns (address[] memory) {
return _getRegulatoryComplianceStorage().regulatoryModules.values();
}

function _getRegulatoryComplianceStorage()
Expand Down
24 changes: 24 additions & 0 deletions contracts/interfaces/IAgentAccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,32 @@ pragma solidity ^0.8.20;

import {IAccessControl} from "@solarity/solidity-lib/diamond/access/access-control/DiamondAccessControl.sol";

/**
* @notice The `AgentAccessControl` contract is an add-on to Solarity's `DiamondAccessControl` and adds one basic role, `AGENT_ROLE`, to its implementation.
* This role is used in the base version of the `TokenF` framework for all privileged functions such as `mint`, `burn`, `addKYCModules`, etc.
*/
interface IAgentAccessControl is IAccessControl {
/**
* @notice Function that returns the key for the base role is `AGENT_ROLE`.
*
* All addresses that own this role are privileged and can call various functions to manage parts of the token.
*
* In a basic implementation of `TokenF`, a user with the Agent role can call absolutely all the privileged functions, such as `mint`, `burn` and etc.
*
* The Agent role key itself is created as follows - `keccak256("AGENT_ROLE")`
*
* @return The key for the agent role
*/
function AGENT_ROLE() external view returns (bytes32);

/**
* @notice Function that is required to check whether a particular user has the required role for the contract logic.
*
* If the user does not have the required role, the transaction will fail with the error
* `AccessControl: account *<user-address>* is missing role *<role-key>*`.
*
* @param role_ The role key to check
* @param account_ The account for role verification
*/
function checkRole(bytes32 role_, address account_) external view;
}
54 changes: 51 additions & 3 deletions contracts/interfaces/IKYCCompliance.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {TokenF} from "../core/TokenF.sol";
import {ITokenF} from "./ITokenF.sol";
import {IKYCComplianceView} from "./IKYCComplianceView.sol";

interface IKYCCompliance {
/**
* @notice `KYCCompliance` contract is used to manage KYC Compliance modules.
* It performs storage, addition of new KYC modules and deletion of existing KYC modules.
*
* It also implements the `isKYCed` hook, which in turn is called by the `TokenF` contract.
*
* All actions for module management can only be done by users who have a special role.
* In the basic version of `TokenF` this role is the Agent role.
*
* It is possible to override the role that is required to configure the module list.
*
* Also this contract is used as a facet in the `TokenF` contract.
*/
interface IKYCCompliance is IKYCComplianceView {
/**
* @notice Function is required to add new KYC modules to a `KYCCompliance` contract.
*
* If you try to add a module that already exists in the list of KYC modules,
* the transaction will fail with an error - `SetHelper: element already exists`.
*
* This function in the basic `TokenF` implementation can only be called by users who have Agent role.
*
* An internal function `_KYCComplianceRole` is used to retrieve the role that is used in the validation,
* which can be overridden if you want to use a role other than Agent.
*
* @param kycModules_ The array of KYC modules to add
*/
function addKYCModules(address[] memory kycModules_) external;

/**
* @notice Function is required to remove existing KYC modules from the list in the `KYCCompliance` contract.
*
* If you try to delete a module that is not in the list of KYC modules,
* the transaction will fail with an error - `SetHelper: no such element`.
*
* This function in the basic `TokenF` implementation can only be called by users who have the Agent role.
*
* An internal function `_KYCComplianceRole` is used to retrieve the role that is used in the validation,
* which can be overridden if you want to use a role other than Agent.
*
* @param kycModules_ The array with KYC modules to remove
*/
function removeKYCModules(address[] memory kycModules_) external;

function isKYCed(TokenF.Context calldata ctx_) external view returns (bool);
/**
* @notice Function that is used to verify that all required KYC rules that have been added to `KYCCompliance` are met.
*
* The entire transaction context is passed to the checker, giving modules full information for further checks.
*
* @param ctx_ The context of the transaction
* @return true if the passed context satisfies the checks on all modules
*/
function isKYCed(ITokenF.Context calldata ctx_) external view returns (bool);
}
23 changes: 23 additions & 0 deletions contracts/interfaces/IKYCComplianceView.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* @notice `IKYCComplianceView` interface stores all view functions that are in the `KYCCompliance` contract
*/
interface IKYCComplianceView {
/**
* @notice Function to get the total number of KYC modules,
* that are currently added to the list of `KYCCompliance` contract modules
*
* @return Total number of all KYC modules in the `KYCCompliance` contract
*/
function getKYCModulesCount() external view returns (uint256);

/**
* @notice Function to get the address list of all KYC modules,
* that are currently added to the list of `KYCCompliance` contract modules
*
* @return Array of addresses of all KYC modules in the `KYCCompliance` contract
*/
function getKYCModules() external view returns (address[] memory);
}
Loading

0 comments on commit bedabe3

Please sign in to comment.