Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions contracts/NFTF.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import {AccessControlUpgradeable} from "./core/AgentAccessControl.sol";
import {NFTFStorage} from "./storages/NFTFStorage.sol";

abstract contract NFTF is INFTF, AbstractAssetF, NFTFStorage, ERC721EnumerableUpgradeable {
/// @dev Precomputed selectors for safe transfers from the IERC721.
/// @dev Selector for `safeTransferFrom(address,address,uint256)`
bytes4 public constant SAFE_TRANSFER_FROM_SELECTOR = 0x42842e0e;
/// @dev Selector for `safeTransferFrom(address,address,uint256,bytes)`
bytes4 public constant SAFE_TRANSFER_FROM_WITH_DATA_SELECTOR = 0xb88d4fde;

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;
Expand Down
2 changes: 1 addition & 1 deletion contracts/mock/modules/ModuleMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract ModuleMock is AbstractRegulatoryModule, AbstractKYCModule {
__AbstractKYCModule_init();
}

function getContextKey(bytes4 selector_) external view returns (bytes32) {
function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

Expand Down
24 changes: 0 additions & 24 deletions contracts/mock/modules/kyc/RarimoModuleMock.sol

This file was deleted.

24 changes: 24 additions & 0 deletions contracts/mock/modules/kyc/SimpleKYCModuleMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IAssetF} from "../../../interfaces/IAssetF.sol";
import {SimpleKYCModule} from "../../../modules/kyc/SimpleKYCModule.sol";

contract SimpleKYCModuleMock is SimpleKYCModule {
function __SimpleKYCModuleMock_init(address assetF_, address sbt_) external initializer {
__AbstractModule_init(assetF_);
__AbstractKYCModule_init();
__SimpleKYCModule_init(sbt_);
}

function __SimpleKYCModuleDirect_init() external {
__SimpleKYCModule_init(address(0));
}

function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return getContextKey(ctx_);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ contract ERC20TransferLimitsModuleMock is ERC20TransferLimitsModule {
__AbstractRegulatoryModule_init();
}

function getContextKey(bytes4 selector_) external view returns (bytes32) {
function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ contract ERC721TransferLimitsModuleMock is ERC721TransferLimitsModule {
__AbstractRegulatoryModule_init();
}

function getContextKey(bytes4 selector_) external view returns (bytes32) {
function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
18 changes: 16 additions & 2 deletions contracts/modules/AbstractModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ abstract contract AbstractModule is Initializable {
_removeHandlerTopics(contextKey_, handlerTopics_);
}

/**
* @notice Function to retrieve the context key from the provided transaction context.
*
* This function calls the internal `_getContextKey` function, which can be overridden
* in derived contracts to customize how the context key is generated based on the
* specific module's requirements.
*
* @param ctx_ The transaction context
* @return context key
*/
function getContextKey(IAssetF.Context memory ctx_) public view virtual returns (bytes32) {
return _getContextKey(ctx_);
}

/**
* @notice Function to retrieve all stored handler topics by the passed context key.
*
Expand Down Expand Up @@ -184,7 +198,7 @@ abstract contract AbstractModule is Initializable {
function _handlerer() internal virtual;

/**
* @notice Function to retrieve the context key from the transaction context.
* @notice Internal function to calculate and retrieve the context key from the transaction context.
*
* The `bytes32` type has been chosen for the context key so that it could be customised.
* Depending on the future purpose of the module, it will be possible to define the process of creating a context key,
Expand All @@ -209,7 +223,7 @@ abstract contract AbstractModule is Initializable {
* @param ctx_ The transaction context
*/
function _handle(IAssetF.Context memory ctx_) internal view virtual returns (bool) {
bytes32 contextKey_ = _getContextKey(ctx_);
bytes32 contextKey_ = getContextKey(ctx_);
bytes32[] memory handlerTopics_ = getHandlerTopics(contextKey_);

for (uint256 j = 0; j < handlerTopics_.length; ++j) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,30 @@ import {IAssetF} from "../../interfaces/IAssetF.sol";
import {AbstractKYCModule} from "../AbstractKYCModule.sol";

/**
* @notice `RarimoModule` is an example of a possible KYC module implementation,
* @notice `SimpleKYCModule` is an example of a possible KYC module implementation,
* within which the user's SBT token is checked.
*/
abstract contract RarimoModule is AbstractKYCModule {
abstract contract SimpleKYCModule is AbstractKYCModule {
bytes32 public constant HAS_SOUL_SENDER_TOPIC = keccak256("HAS_SOUL_SENDER");
bytes32 public constant HAS_SOUL_RECIPIENT_TOPIC = keccak256("HAS_SOUL_RECIPIENT");
bytes32 public constant HAS_SOUL_OPERATOR_TOPIC = keccak256("HAS_SOUL_OPERATOR");

// keccak256("tokenf.standard.rarimo.module.storage")
bytes32 private constant RARIMO_MODULE_STORAGE =
0x4daee3f1bcf471e40cb8bb42f6957ecf0fb0ccfdf6e24496c76bda599dbc8902;
// keccak256("tokenf.standard.simple.kyc.module.storage")
bytes32 private constant SIMPLE_KYC_MODULE_STORAGE =
0x38deaaaa98559b0911f428b8b3b9bbf960af9ad1ba4f2251fc09aa6872c543ae;

struct RarimoModuleStorage {
struct SimpleKYCModuleStorage {
address sbt;
}

function __RarimoModule_init(address sbt_) internal onlyInitializing {
RarimoModuleStorage storage $ = _getRarimoModuleStorage();
function __SimpleKYCModule_init(address sbt_) internal onlyInitializing {
SimpleKYCModuleStorage storage $ = _getSimpleKYCModuleStorage();

$.sbt = sbt_;
}

function getSBT() public view virtual returns (address) {
RarimoModuleStorage storage $ = _getRarimoModuleStorage();
SimpleKYCModuleStorage storage $ = _getSimpleKYCModuleStorage();

return $.sbt;
}
Expand All @@ -44,33 +44,31 @@ abstract contract RarimoModule is AbstractKYCModule {
function _handleHasSoulSenderTopic(
IAssetF.Context memory ctx_
) internal view virtual returns (bool) {
RarimoModuleStorage storage $ = _getRarimoModuleStorage();

return ISBT($.sbt).balanceOf(ctx_.from) > 0;
return _hasSBT(ctx_.from);
}

function _handleHasSoulRecipientTopic(
IAssetF.Context memory ctx_
) internal view virtual returns (bool) {
RarimoModuleStorage storage $ = _getRarimoModuleStorage();

return ISBT($.sbt).balanceOf(ctx_.to) > 0;
return _hasSBT(ctx_.to);
}

function _handleHasSoulOperatorTopic(
IAssetF.Context memory ctx_
) internal view virtual returns (bool) {
RarimoModuleStorage storage $ = _getRarimoModuleStorage();
return _hasSBT(ctx_.operator);
}

return ISBT($.sbt).balanceOf(ctx_.operator) > 0;
function _hasSBT(address userAddr_) internal view returns (bool) {
return ISBT(_getSimpleKYCModuleStorage().sbt).balanceOf(userAddr_) > 0;
}

/**
* @dev Returns a pointer to the storage namespace
*/
function _getRarimoModuleStorage() private pure returns (RarimoModuleStorage storage $) {
function _getSimpleKYCModuleStorage() private pure returns (SimpleKYCModuleStorage storage $) {
assembly {
$.slot := RARIMO_MODULE_STORAGE
$.slot := SIMPLE_KYC_MODULE_STORAGE
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own

import {ASBT} from "@solarity/solidity-lib/tokens/ASBT.sol";

contract RarimoSBT is ASBT, OwnableUpgradeable {
function __RarimoSBT_init() external initializer {
__ASBT_init("RarimoSBT", "RarimoSBT");
contract EquitySBT is ASBT, OwnableUpgradeable {
function __EquitySBT_init() external initializer {
__ASBT_init("EquitySBT", "EquitySBT");
__Ownable_init(msg.sender);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ contract EquityERC20TransferLimitsModule is ERC20TransferLimitsModule {
__ERC20TransferLimitsModule_init(1 ether, MAX_TRANSFER_LIMIT);
}

function getContextKey(bytes4 selector_) external view returns (bytes32) {
function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
19 changes: 19 additions & 0 deletions examples/equity-token/contracts/modules/EquityKYCModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IAssetF} from "@tokenf/contracts/interfaces/IAssetF.sol";
import {SimpleKYCModule} from "@tokenf/contracts/modules/kyc/SimpleKYCModule.sol";

contract EquityKYCModule is SimpleKYCModule {
function __EquityKYCModule_init(address assetF_, address sbt_) external initializer {
__AbstractModule_init(assetF_);
__SimpleKYCModule_init(sbt_);
}

function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return getContextKey(ctx_);
}
}
19 changes: 0 additions & 19 deletions examples/equity-token/contracts/modules/EquityRarimoModule.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ contract LandERC721TransferLimitsModule is ERC721TransferLimitsModule {
__ERC721TransferLimitsModule_init(MAX_TRANSFERS_PER_PERIOD, TIME_PERIOD);
}

function getContextKey(bytes4 selector_) external view returns (bytes32) {
function getContextKeyBySelector(bytes4 selector_) external view returns (bytes32) {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
52 changes: 27 additions & 25 deletions examples/equity-token/deploy/1_equity-token.migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { Deployer, Reporter } from "@solarity/hardhat-migrate";
import {
KYCComplianceFacet,
KYCComplianceFacet__factory,
EquityRarimoModule,
EquityRarimoModule__factory,
EquityKYCModule,
EquityKYCModule__factory,
RegulatoryComplianceFacet,
RegulatoryComplianceFacet__factory,
EquityToken,
EquityToken__factory,
EquityERC20TransferLimitsModule,
EquityERC20TransferLimitsModule__factory,
RarimoSBT,
RarimoSBT__factory,
EquitySBT,
EquitySBT__factory,
} from "@ethers-v6";

async function setupCoreContracts(
Expand Down Expand Up @@ -48,8 +48,10 @@ async function setupTransferLimitsModule(
const transferLimitsModule = await deployer.deploy(EquityERC20TransferLimitsModule__factory);
await transferLimitsModule.__EquityERC20TransferLimitsModule_init(tokenF);

const transferContextKey = await transferLimitsModule.getContextKey(await tokenF.TRANSFER_SELECTOR());
const transferFromContextKey = await transferLimitsModule.getContextKey(await tokenF.TRANSFER_FROM_SELECTOR());
const transferContextKey = await transferLimitsModule.getContextKeyBySelector(await tokenF.TRANSFER_SELECTOR());
const transferFromContextKey = await transferLimitsModule.getContextKeyBySelector(
await tokenF.TRANSFER_FROM_SELECTOR(),
);

await transferLimitsModule.addHandlerTopics(transferContextKey, [
await transferLimitsModule.MIN_TRANSFER_LIMIT_TOPIC(),
Expand All @@ -63,42 +65,42 @@ async function setupTransferLimitsModule(
return transferLimitsModule;
}

async function setupRarimoModule(deployer: Deployer, tokenF: EquityToken): Promise<[EquityRarimoModule, RarimoSBT]> {
const rarimoSBT = await deployer.deploy(RarimoSBT__factory);
await rarimoSBT.__RarimoSBT_init();
async function setupKYCModule(deployer: Deployer, tokenF: EquityToken): Promise<[EquityKYCModule, EquitySBT]> {
const equitySBT = await deployer.deploy(EquitySBT__factory);
await equitySBT.__EquitySBT_init();

const rarimoModule = await deployer.deploy(EquityRarimoModule__factory);
await rarimoModule.__EquityRarimoModule_init(tokenF, rarimoSBT);
const equityKYCModule = await deployer.deploy(EquityKYCModule__factory);
await equityKYCModule.__EquityKYCModule_init(tokenF, equitySBT);

const transferContextKey = await rarimoModule.getContextKey(await tokenF.TRANSFER_SELECTOR());
const transferFromContextKey = await rarimoModule.getContextKey(await tokenF.TRANSFER_FROM_SELECTOR());
const transferContextKey = await equityKYCModule.getContextKeyBySelector(await tokenF.TRANSFER_SELECTOR());
const transferFromContextKey = await equityKYCModule.getContextKeyBySelector(await tokenF.TRANSFER_FROM_SELECTOR());

await rarimoModule.addHandlerTopics(transferContextKey, [
await rarimoModule.HAS_SOUL_SENDER_TOPIC(),
await rarimoModule.HAS_SOUL_RECIPIENT_TOPIC(),
await equityKYCModule.addHandlerTopics(transferContextKey, [
await equityKYCModule.HAS_SOUL_SENDER_TOPIC(),
await equityKYCModule.HAS_SOUL_RECIPIENT_TOPIC(),
]);
await rarimoModule.addHandlerTopics(transferFromContextKey, [
await rarimoModule.HAS_SOUL_SENDER_TOPIC(),
await rarimoModule.HAS_SOUL_RECIPIENT_TOPIC(),
await rarimoModule.HAS_SOUL_OPERATOR_TOPIC(),
await equityKYCModule.addHandlerTopics(transferFromContextKey, [
await equityKYCModule.HAS_SOUL_SENDER_TOPIC(),
await equityKYCModule.HAS_SOUL_RECIPIENT_TOPIC(),
await equityKYCModule.HAS_SOUL_OPERATOR_TOPIC(),
]);

return [rarimoModule, rarimoSBT];
return [equityKYCModule, equitySBT];
}

export = async (deployer: Deployer) => {
const [tokenF, kycCompliance, regulatoryCompliance] = await setupCoreContracts(deployer);

const [rarimoModule, rarimoSBT] = await setupRarimoModule(deployer, tokenF);
const [equityKYCModule, equitySBT] = await setupKYCModule(deployer, tokenF);
const transferLimitsModule = await setupTransferLimitsModule(deployer, tokenF);

await kycCompliance.addKYCModules([rarimoModule]);
await kycCompliance.addKYCModules([equityKYCModule]);
await regulatoryCompliance.addRegulatoryModules([transferLimitsModule]);

Reporter.reportContracts(
["EquityToken", await tokenF.getAddress()],
["ERC20TransferLimitsModule", await transferLimitsModule.getAddress()],
["RarimoModule", await rarimoModule.getAddress()],
["RarimoSBT", await rarimoSBT.getAddress()],
["EquityKYCModule", await equityKYCModule.getAddress()],
["EquitySBT", await equitySBT.getAddress()],
);
};
Loading