Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
pragma solidity ^0.8.21;

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

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

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

function getContextKey(bytes4 selector_) external view returns (bytes32) {
Expand Down
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 @@ -14,6 +14,6 @@ contract EquityERC20TransferLimitsModule is ERC20TransferLimitsModule {
IAssetF.Context memory ctx_;
Comment thread
Arvolear marked this conversation as resolved.
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
pragma solidity ^0.8.21;

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

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

function getContextKey(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 @@ -17,6 +17,6 @@ contract LandERC721TransferLimitsModule is ERC721TransferLimitsModule {
IAssetF.Context memory ctx_;
ctx_.selector = selector_;

return _getContextKey(ctx_);
return getContextKey(ctx_);
}
}
46 changes: 23 additions & 23 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 @@ -63,42 +63,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["getContextKey(bytes4)"](await tokenF.TRANSFER_SELECTOR());
const transferFromContextKey = await equityKYCModule["getContextKey(bytes4)"](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()],
);
};
62 changes: 39 additions & 23 deletions examples/equity-token/deploy/2_land-nft.migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
KYCComplianceFacet__factory,
LandNFT,
LandNFT__factory,
EquityRarimoModule,
EquityRarimoModule__factory,
EquityKYCModule,
EquityKYCModule__factory,
RegulatoryComplianceFacet,
RegulatoryComplianceFacet__factory,
RarimoSBT,
RarimoSBT__factory,
EquitySBT,
EquitySBT__factory,
LandERC721TransferLimitsModule,
LandERC721TransferLimitsModule__factory,
} from "@ethers-v6";
Expand Down Expand Up @@ -53,42 +53,58 @@ async function setupTransferLimitsModule(deployer: Deployer, nftF: LandNFT): Pro
return transferLimitsModule;
}

async function setupRarimoModule(deployer: Deployer, nftF: LandNFT): Promise<[EquityRarimoModule, RarimoSBT]> {
const rarimoSBT = await deployer.deploy(RarimoSBT__factory);
await rarimoSBT.__RarimoSBT_init();
async function setupKYCModule(deployer: Deployer, nftF: LandNFT): 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(nftF, rarimoSBT);
const equityKYCModule = await deployer.deploy(EquityKYCModule__factory);
await equityKYCModule.__EquityKYCModule_init(nftF, equitySBT);

const transferContextKey = await rarimoModule.getContextKey(await nftF.TRANSFER_SELECTOR());
const transferFromContextKey = await rarimoModule.getContextKey(await nftF.TRANSFER_FROM_SELECTOR());
const transferContextKey = await equityKYCModule["getContextKey(bytes4)"](await nftF.TRANSFER_SELECTOR());
const transferFromContextKey = await equityKYCModule["getContextKey(bytes4)"](await nftF.TRANSFER_FROM_SELECTOR());
const safeTransferFromContextKey = await equityKYCModule["getContextKey(bytes4)"](
await nftF.SAFE_TRANSFER_FROM_SELECTOR(),
);
const safeTransferFromWithDataContextKey = await equityKYCModule["getContextKey(bytes4)"](
await nftF.SAFE_TRANSFER_FROM_WITH_DATA_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 equityKYCModule.addHandlerTopics(transferFromContextKey, [
await equityKYCModule.HAS_SOUL_SENDER_TOPIC(),
await equityKYCModule.HAS_SOUL_RECIPIENT_TOPIC(),
await equityKYCModule.HAS_SOUL_OPERATOR_TOPIC(),
]);
await equityKYCModule.addHandlerTopics(safeTransferFromContextKey, [
await equityKYCModule.HAS_SOUL_SENDER_TOPIC(),
await equityKYCModule.HAS_SOUL_RECIPIENT_TOPIC(),
await equityKYCModule.HAS_SOUL_OPERATOR_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(safeTransferFromWithDataContextKey, [
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 [nftF, kycCompliance, regulatoryCompliance] = await setupCoreContracts(deployer);

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

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

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