Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMTAT 3.0.0 #283

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
75dd083
Separate ERC20 calls inside snapshotModule
rya-sge Nov 12, 2024
be8669a
Merge pull request #282 from CMTA/snapshot-update
rya-sge Nov 12, 2024
37078ca
Add TransferEngineModule, remove snapshotModule
rya-sge Nov 13, 2024
115ac4d
Use a struct to represent a document
rya-sge Nov 13, 2024
694b4a6
Merge pull request #284 from CMTA/update-document
rya-sge Jan 13, 2025
ac24d33
Replace transferEngine by SnapshotEngine
rya-sge Jan 16, 2025
fb17fcc
Add enforcement
rya-sge Jan 22, 2025
6b39163
Remove authorisationEngine, add snapshotEngine
rya-sge Jan 22, 2025
1c41498
Update OZ and solidity, setter for ERC20Name and ERC20Symbol, move en…
rya-sge Feb 5, 2025
6f1846d
Merge pull request #288 from CMTA/update-snapshot-2
rya-sge Feb 5, 2025
693a41b
Remove factory contract
rya-sge Feb 6, 2025
b06a5b6
Merge branch 'dev' into update-snapshot-2
rya-sge Feb 6, 2025
39480f7
Merge pull request #289 from CMTA/update-snapshot-2
rya-sge Feb 6, 2025
0a0385e
SnapsModule: clean-up + add doc
rya-sge Feb 10, 2025
3e81a65
FIx bug with snapshot Engine
rya-sge Feb 10, 2025
9ae7f60
Remove useless package
rya-sge Feb 10, 2025
7a361dc
Add ERC-1363
rya-sge Feb 11, 2025
7fa2d8f
Add terms as document + improve doc
rya-sge Feb 11, 2025
70c46bb
Improvement initialize function
rya-sge Feb 12, 2025
8304fd4
Code clean-up + linter
rya-sge Feb 12, 2025
b282d75
Remove useless node js package + improve doc
rya-sge Feb 12, 2025
76a2b88
improve test and doc
rya-sge Feb 13, 2025
9dc4896
improve doc, generate slither report
rya-sge Feb 13, 2025
9d01630
Add burn without reason, change burn/mint interface + improve doc
rya-sge Feb 13, 2025
0add5ee
Use require with custom error
rya-sge Feb 13, 2025
fd7ce10
Update readme
rya-sge Feb 13, 2025
5725d6c
Add ERC3643 partial support + correspondance table
rya-sge Feb 17, 2025
15ee4bc
Rename validateTransfer in canTransfer, remove IERC1404Wrapper, add I…
rya-sge Feb 18, 2025
b3fb525
Create interface to share spend event between ERC20Base and ERC20Burn
rya-sge Feb 18, 2025
b9e7261
Add RuleEngine check with approve + rename function for IERC3643
rya-sge Feb 18, 2025
2d54578
Add test for ERC20 approve
rya-sge Feb 19, 2025
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
14 changes: 14 additions & 0 deletions contracts/interfaces/engine/ITransferEngine.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MPL-2.0

pragma solidity ^0.8.20;

/*
* @dev minimum interface to define a RuleEngine
*/
interface ITransferEngine {
/**
* @dev Returns true if the operation is a success, and false otherwise.
*/
function operateOnTransfer(address from, address to, uint256 balanceFrom, uint256 balanceTo, uint256 totalSupply) external;

}
10 changes: 8 additions & 2 deletions contracts/interfaces/engine/draft-IERC1643.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ pragma solidity ^0.8.20;
/// @title IERC1643 Document Management
/// (part of the ERC1400 Security Token Standards)
interface IERC1643 {
struct Document {
string uri;
bytes32 documentHash;
uint256 lastModified;
}

// Document Management
function getDocument(bytes32 _name) external view returns (string memory , bytes32, uint256);
function getAllDocuments() external view returns (bytes32[] memory);
function getDocument(string memory name) external view returns (Document memory doc);
function getAllDocuments() external view returns (string[] memory);
}
2 changes: 2 additions & 0 deletions contracts/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ library Errors {
error CMTAT_AuthorizationModule_InvalidAuthorization();
error CMTAT_AuthorizationModule_AuthorizationEngineAlreadySet();

error CMTAT_TransferEngineModule_TransferEngineAlreadySet();

// DocumentModule
error CMTAT_DocumentModule_SameValue();

Expand Down
100 changes: 44 additions & 56 deletions contracts/mocks/DocumentEngineMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,94 +3,82 @@
pragma solidity ^0.8.20;
import "../interfaces/engine/draft-IERC1643.sol";
interface IERC1643Whole is IERC1643{

/// uri The URI of the document
/// @return documentHash The hash of the document contents
/// @return lastModified The timestamp of the last modification
struct DocumentInfo {
string name;
string uri;
bytes32 documentHash;
}
// Document Management
function setDocument(bytes32 _name, string memory _uri, bytes32 _documentHash) external;
function removeDocument(bytes32 _name) external;
function setDocument(DocumentInfo calldata doc) external;
function removeDocument(string memory name) external;

// Document Events
event DocumentRemoved(bytes32 indexed _name, string _uri, bytes32 _documentHash);
event DocumentUpdated(bytes32 indexed _name, string _uri, bytes32 _documentHash);
event DocumentRemoved(string indexed name, Document doc);
event DocumentUpdated(string indexed name, Document doc);

}
/*
* @title a DocumentEngine mock for testing, not suitable for production
*/
contract DocumentEngineMock is IERC1643Whole {
struct Document {
string uri;
bytes32 documentHash;
uint256 lastModified;
}

mapping(bytes32 => Document) private documents;
bytes32[] private documentNames;
/*struct DocumentStorage {
uint256 key;
Document doc;
}*/
mapping(string => Document) private documents;
mapping(string => uint256) private documentKey;
string[] private documentNames;

/// @dev Error thrown when a document does not exist
error DocumentDoesNotExist();

/// @notice Retrieves the document details by name
/// @param name_ The name of the document
/// @return uri The URI of the document
/// @return documentHash The hash of the document contents
/// @return lastModified The timestamp of the last modification
function getDocument(bytes32 name_)
/// @param name The name of the document
function getDocument(string memory name)
external
view
override
returns (string memory uri, bytes32 documentHash, uint256 lastModified)
returns (Document memory doc)
{
if (bytes(documents[name_].uri).length == 0) {
return("", 0x0, 0);
}

Document storage doc = documents[name_];
return (doc.uri, doc.documentHash, doc.lastModified);
return documents[name];
}

/// @notice Sets or updates a document
/// @param name_ The name of the document
/// @param uri_ The URI of the document
/// @param documentHash_ The hash of the document contents
function setDocument(bytes32 name_, string memory uri_, bytes32 documentHash_) external override {
Document storage doc = documents[name_];
bool isUpdate = bytes(doc.uri).length != 0;

doc.uri = uri_;
doc.documentHash = documentHash_;
/// @param doc_ the document
function setDocument(DocumentInfo calldata doc_) external override {
Document storage doc = documents[doc_.name];
doc.uri = doc_.uri;
doc.documentHash = doc_.documentHash;
doc.lastModified = block.timestamp;

if (!isUpdate) {
documentNames.push(name_);
if (documentKey[doc_.name] == 0) {
// To avoid key == 0
uint256 key = documentNames.length + 1;
documentKey[doc_.name] = key;
documentNames.push(doc_.name);
}

emit DocumentUpdated(name_, uri_, documentHash_);
emit DocumentUpdated(doc_.name, doc);
}

/// @notice Removes a document
/// @param name_ The name of the document
function removeDocument(bytes32 name_) external override {
if (bytes(documents[name_].uri).length == 0) {
/// @param name The name of the document
function removeDocument(string calldata name) external override {
if (documentKey[name] == 0) {
revert DocumentDoesNotExist();
}

Document memory doc = documents[name_];
delete documents[name_];

for (uint256 i = 0; i < documentNames.length; i++) {
if (documentNames[i] == name_) {
documentNames[i] = documentNames[documentNames.length - 1];
documentNames.pop();
break;
}
}

emit DocumentRemoved(name_, doc.uri, doc.documentHash);
Document memory doc = documents[name];
documentNames[documentKey[name] - 1] = documentNames[documentNames.length - 1];
documentNames.pop();
delete documents[name];
documentKey[name] = 0;
emit DocumentRemoved(name, doc);
}

/// @notice Retrieves all document names
/// @return An array of document names
function getAllDocuments() external view override returns (bytes32[] memory) {
function getAllDocuments() external view override returns (string[] memory) {
return documentNames;
}
}
167 changes: 167 additions & 0 deletions contracts/mocks/TransferEngineMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//SPDX-License-Identifier: MPL-2.0

pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../modules/internal/base/SnapshotModuleBase.sol";
import "../interfaces/ICMTATSnapshot.sol";
import "../interfaces/engine/ITransferEngine.sol";
/*
* @title a RuleEngine mock for testing, not suitable for production
*/
contract TransferEngineMock is SnapshotModuleBase, AccessControlUpgradeable, ITransferEngine {
ERC20Upgradeable erc20;
constructor(ERC20Upgradeable erc20_, address admin) {
erc20 = erc20_;
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
/* ============ State Variables ============ */
bytes32 public constant SNAPSHOOTER_ROLE = keccak256("SNAPSHOOTER_ROLE");

/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(
bytes32 role,
address account
) public view virtual override(AccessControlUpgradeable) returns (bool) {
// The Default Admin has all roles
if (AccessControlUpgradeable.hasRole(DEFAULT_ADMIN_ROLE, account)) {
return true;
}
return AccessControlUpgradeable.hasRole(role, account);
}
/**
* @dev Update balance and/or total supply snapshots before the values are modified. This is implemented
* in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations.
*/
function operateOnTransfer(address from, address to, uint256 balanceFrom, uint256 balanceTo, uint256 totalSupply) public override {
_setCurrentSnapshot();
if (from != address(0)) {
// for both burn and transfer
_updateAccountSnapshot(from, balanceFrom);
if (to != address(0)) {
// transfer
_updateAccountSnapshot(to, balanceTo);
} else {
// burn
_updateTotalSupplySnapshot(totalSupply);
}
} else {
// mint
_updateAccountSnapshot(to, balanceTo);
_updateTotalSupplySnapshot(totalSupply);
}
}

/*//////////////////////////////////////////////////////////////
PUBLIC/EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Return snapshotBalanceOf and snapshotTotalSupply to avoid multiple calls
* @return ownerBalance , totalSupply - see snapshotBalanceOf and snapshotTotalSupply
*/
function snapshotInfo(uint256 time, address owner) public view returns (uint256 ownerBalance, uint256 totalSupply) {
ownerBalance = snapshotBalanceOf(time, owner);
totalSupply = snapshotTotalSupply(time);
}

/**
* @notice Return snapshotBalanceOf for each address in the array and the total supply
* @return ownerBalances array with the balance of each address, the total supply
*/
function snapshotInfoBatch(uint256 time, address[] calldata addresses) public view returns (uint256[] memory ownerBalances, uint256 totalSupply) {
ownerBalances = new uint256[](addresses.length);
for(uint256 i = 0; i < addresses.length; ++i){
ownerBalances[i] = snapshotBalanceOf(time, addresses[i]);
}
totalSupply = snapshotTotalSupply(time);
}

/**
* @notice Return snapshotBalanceOf for each address in the array and the total supply
* @return ownerBalances array with the balance of each address, the total supply
*/
function snapshotInfoBatch(uint256[] calldata times, address[] calldata addresses) public view returns (uint256[][] memory ownerBalances, uint256[] memory totalSupply) {
ownerBalances = new uint256[][](times.length);
totalSupply = new uint256[](times.length);
for(uint256 iT = 0; iT < times.length; ++iT){
(ownerBalances[iT], totalSupply[iT]) = snapshotInfoBatch(times[iT],addresses);
}
}

/**
* @notice Return the number of tokens owned by the given owner at the time when the snapshot with the given time was created.
* @return value stored in the snapshot, or the actual balance if no snapshot
*/
function snapshotBalanceOf(
uint256 time,
address owner
) public view returns (uint256) {
return _snapshotBalanceOf(time, owner, erc20.balanceOf(owner));
}

/**
* @dev See {OpenZeppelin - ERC20Snapshot}
* Retrieves the total supply at the specified time.
* @return value stored in the snapshot, or the actual totalSupply if no snapshot
*/
function snapshotTotalSupply(uint256 time) public view returns (uint256) {
return _snapshotTotalSupply(time, erc20.totalSupply());
}
/*//////////////////////////////////////////////////////////////
PUBLIC/EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice
* Schedule a snapshot at the given time specified as a number of seconds since epoch.
* The time cannot be before the time of the latest scheduled, but not yet created snapshot.
*/
function scheduleSnapshot(uint256 time) public onlyRole(SNAPSHOOTER_ROLE) {
_scheduleSnapshot(time);
}

/**
* @notice
* Schedule a snapshot at the given time specified as a number of seconds since epoch.
* The time cannot be before the time of the latest scheduled, but not yet created snapshot.
*/
function scheduleSnapshotNotOptimized(
uint256 time
) public onlyRole(SNAPSHOOTER_ROLE) {
_scheduleSnapshotNotOptimized(time);
}

/**
* @notice
* Reschedule the scheduled snapshot, but not yet created snapshot with the given oldTime to be created at the given newTime specified as a number of seconds since epoch.
* The newTime cannot be before the time of the previous scheduled, but not yet created snapshot, or after the time fo the next scheduled snapshot.
*/
function rescheduleSnapshot(
uint256 oldTime,
uint256 newTime
) public onlyRole(SNAPSHOOTER_ROLE) {
_rescheduleSnapshot(oldTime, newTime);
}

/**
* @notice
* Cancel creation of the scheduled snapshot, but not yet created snapshot with the given time.
* There should not be any other snapshots scheduled after this one.
*/
function unscheduleLastSnapshot(
uint256 time
) public onlyRole(SNAPSHOOTER_ROLE) {
_unscheduleLastSnapshot(time);
}

/**
* @notice
* Cancel creation of the scheduled snapshot, but not yet created snapshot with the given time.
*/
function unscheduleSnapshotNotOptimized(
uint256 time
) public onlyRole(SNAPSHOOTER_ROLE) {
_unscheduleSnapshotNotOptimized(time);
}
}
Loading
Loading