diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0617669..199d877 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
Please follow https://changelog.md/ conventions.
+## v0.3.0
+
+- Add ERC-2771 support
+
## v0.2.0
- Add the constant VERSION
diff --git a/README.md b/README.md
index 488193f..380053e 100644
--- a/README.md
+++ b/README.md
@@ -95,6 +95,18 @@ function setDocument(address smartContract,bytes32 name_,string memory uri_, byt
| 🛑 | Function can modify state |
| 💵 | Function is payable |
+
+
+## Gasless support (ERC-2771)
+
+The DocumentEngine supports client-side gasless transactions using the [Gas Station Network](https://docs.opengsn.org/#the-problem) (GSN) pattern, the main open standard for transfering fee payment to another account than that of the transaction issuer. The contract uses the OpenZeppelin contract `ERC2771ContextUpgradeable`, which allows a contract to get the original client with `_msgSender()` instead of the fee payer given by `msg.sender` while allowing upgrades on the main contract (see *Deployment via a proxy* above).
+
+At deployment, the parameter `forwarder` inside the constructor has to be set with the defined address of the forwarder. Please note that the forwarder can not be changed after deployment.
+
+Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#receiving-a-relayed-call) for more details on what is done to support GSN in the contract.
+
+
+
## Dependencies
The toolchain includes the following components, where the versions are the latest ones that we tested:
diff --git a/doc/coverage/coverage/index-sort-b.html b/doc/coverage/coverage/index-sort-b.html
index b03de3c..3fe0eaf 100644
--- a/doc/coverage/coverage/index-sort-b.html
+++ b/doc/coverage/coverage/index-sort-b.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
src |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/index-sort-f.html b/doc/coverage/coverage/index-sort-f.html
index 577f764..7e88c84 100644
--- a/doc/coverage/coverage/index-sort-f.html
+++ b/doc/coverage/coverage/index-sort-f.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
src |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/index-sort-l.html b/doc/coverage/coverage/index-sort-l.html
index 7c65a94..00b9318 100644
--- a/doc/coverage/coverage/index-sort-l.html
+++ b/doc/coverage/coverage/index-sort-l.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
src |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/index.html b/doc/coverage/coverage/index.html
index a6dc604..138d820 100644
--- a/doc/coverage/coverage/index.html
+++ b/doc/coverage/coverage/index.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
src |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html b/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html
index 5f749f9..935fc26 100644
--- a/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html
+++ b/doc/coverage/coverage/src/DocumentEngine.sol.func-sort-c.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -69,48 +69,60 @@
Hit count |
- DocumentEngine.hasRole |
+ DocumentEngine._msgData |
+ 0 |
+
+
+ DocumentEngine.hasRole |
0 |
- DocumentEngine.removeDocument |
+ DocumentEngine.removeDocument |
2 |
- DocumentEngine._removeDocument |
+ DocumentEngine._removeDocument |
5 |
- DocumentEngine._removeDocumentName |
+ DocumentEngine._removeDocumentName |
5 |
- DocumentEngine.batchRemoveDocuments |
+ DocumentEngine.batchRemoveDocuments |
7 |
- DocumentEngine.getAllDocuments |
+ DocumentEngine.getAllDocuments |
8 |
- DocumentEngine.batchSetDocuments |
+ DocumentEngine.batchSetDocuments |
13 |
- DocumentEngine._getDocument |
+ DocumentEngine._getDocument |
20 |
- DocumentEngine.getDocument |
+ DocumentEngine.getDocument |
20 |
- DocumentEngine.setDocument |
- 27 |
+ DocumentEngine.setDocument |
+ 28 |
+
+
+ DocumentEngine._setDocument |
+ 39 |
+
+
+ DocumentEngine._contextSuffixLength |
+ 50 |
- DocumentEngine._setDocument |
- 38 |
+ DocumentEngine._msgSender |
+ 50 |
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.func.html b/doc/coverage/coverage/src/DocumentEngine.sol.func.html
index 27951b1..4139274 100644
--- a/doc/coverage/coverage/src/DocumentEngine.sol.func.html
+++ b/doc/coverage/coverage/src/DocumentEngine.sol.func.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -69,48 +69,60 @@
Hit count |
- DocumentEngine._getDocument |
+ DocumentEngine._contextSuffixLength |
+ 50 |
+
+
+ DocumentEngine._getDocument |
20 |
- DocumentEngine._removeDocument |
+ DocumentEngine._msgData |
+ 0 |
+
+
+ DocumentEngine._msgSender |
+ 50 |
+
+
+ DocumentEngine._removeDocument |
5 |
- DocumentEngine._removeDocumentName |
+ DocumentEngine._removeDocumentName |
5 |
- DocumentEngine._setDocument |
- 38 |
+ DocumentEngine._setDocument |
+ 39 |
- DocumentEngine.batchRemoveDocuments |
+ DocumentEngine.batchRemoveDocuments |
7 |
- DocumentEngine.batchSetDocuments |
+ DocumentEngine.batchSetDocuments |
13 |
- DocumentEngine.getAllDocuments |
+ DocumentEngine.getAllDocuments |
8 |
- DocumentEngine.getDocument |
+ DocumentEngine.getDocument |
20 |
- DocumentEngine.hasRole |
+ DocumentEngine.hasRole |
0 |
- DocumentEngine.removeDocument |
+ DocumentEngine.removeDocument |
2 |
- DocumentEngine.setDocument |
- 27 |
+ DocumentEngine.setDocument |
+ 28 |
diff --git a/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html b/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html
index 24db044..f77a5a3 100644
--- a/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html
+++ b/doc/coverage/coverage/src/DocumentEngine.sol.gcov.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -73,240 +73,298 @@
2 : : pragma solidity ^0.8.20;
3 : :
4 : : import "OZ/access/AccessControl.sol";
- 5 : : import "CMTAT/interfaces/engine/draft-IERC1643.sol";
- 6 : : import "./DocumentEngineInvariant.sol";
- 7 : :
- 8 : : contract DocumentEngine is IERC1643, DocumentEngineInvariant, AccessControl {
- 9 : : // Mapping from contract addresses to document names to their corresponding Document structs
- 10 : : mapping(address => mapping(bytes32 => Document)) private _documents;
- 11 : : mapping(address => bytes32[]) private _documentNames;
- 12 : :
- 13 : : // Constructor to initialize the admin role
- 14 : : constructor(address admin) {
- 15 : : _grantRole(DEFAULT_ADMIN_ROLE, admin);
- 16 : : }
- 17 : :
- 18 : : /*//////////////////////////////////////////////////////////////
- 19 : : PUBLIC/EXTERNAL FUNCTIONS
- 20 : : //////////////////////////////////////////////////////////////*/
- 21 : :
- 22 : : /**
- 23 : : * @notice Restricted function to set or update a document
- 24 : : */
- 25 : : function setDocument(
- 26 : : address smartContract,
- 27 : : bytes32 name_,
- 28 : : string memory uri_,
- 29 : : bytes32 documentHash_
- 30 : : ) public onlyRole(DOCUMENT_MANAGER_ROLE) {
- 31 : 52 : _setDocument(smartContract, name_, uri_, documentHash_);
- 32 : : }
- 33 : :
- 34 : : /**
- 35 : : * @notice Restricted function to remove a document for a given smart contract and name
- 36 : : */
- 37 : : function removeDocument(
- 38 : : address smartContract,
- 39 : : bytes32 name_
- 40 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
- 41 : 2 : _removeDocument(smartContract, name_);
- 42 : : }
- 43 : :
- 44 : : /**
- 45 : : * @notice Batch version of setDocument to handle multiple documents at once
- 46 : : */
- 47 : : function batchSetDocuments(
- 48 : : address[] calldata smartContracts,
- 49 : : bytes32[] calldata names,
- 50 : : string[] calldata uris,
- 51 : : bytes32[] calldata hashes
- 52 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
- 53 [ + + ]: : if (
- 54 : 35 : smartContracts.length == 0 ||
- 55 : 12 : smartContracts.length != names.length ||
- 56 : 10 : names.length != uris.length ||
- 57 : 8 : uris.length != hashes.length
- 58 : : ) {
- 59 : 6 : revert InvalidInputLength();
- 60 : : }
- 61 : 28 : for (uint256 i = 0; i < smartContracts.length; i++) {
- 62 : 16 : _setDocument(smartContracts[i], names[i], uris[i], hashes[i]);
- 63 : : }
- 64 : : }
- 65 : :
- 66 : : /**
- 67 : : * @notice Batch version of setDocument to handle multiple documents at once
- 68 : : */
- 69 : : function batchSetDocuments(
- 70 : : address smartContract,
- 71 : : bytes32[] calldata names,
- 72 : : string[] calldata uris,
- 73 : : bytes32[] calldata hashes
- 74 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
- 75 [ + + ]: : if (
- 76 : 19 : names.length == 0 || names.length != uris.length ||
- 77 : 4 : uris.length != hashes.length
- 78 : : ) {
- 79 : 4 : revert InvalidInputLength();
- 80 : : }
- 81 : 14 : for (uint256 i = 0; i < names.length; ++i) {
- 82 : 8 : _setDocument(smartContract, names[i], uris[i], hashes[i]);
- 83 : : }
- 84 : : }
- 85 : :
- 86 : : /**
- 87 : : * @notice Batch version of removeDocument to handle multiple documents at once
- 88 : : */
- 89 : : function batchRemoveDocuments(
- 90 : : address[] calldata smartContracts,
- 91 : : bytes32[] calldata names
- 92 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
- 93 [ + + ]: : if (
- 94 : 9 : smartContracts.length == 0 ||
- 95 : : (smartContracts.length != names.length)
- 96 : : ) {
- 97 : 4 : revert InvalidInputLength();
- 98 : : }
- 99 : :
- 100 : 7 : for (uint256 i = 0; i < smartContracts.length; ++i) {
- 101 : 4 : _removeDocument(smartContracts[i], names[i]);
+ 5 : : import "OZ/metatx/ERC2771Context.sol";
+ 6 : : import "CMTAT/interfaces/engine/draft-IERC1643.sol";
+ 7 : : import "./DocumentEngineInvariant.sol";
+ 8 : :
+ 9 : : /**
+ 10 : : * @title DocumentEngine
+ 11 : : * @notice contract to manage documents on-chain through ERC-1643
+ 12 : : */
+ 13 : : contract DocumentEngine is
+ 14 : : IERC1643,
+ 15 : : DocumentEngineInvariant,
+ 16 : : AccessControl,
+ 17 : : ERC2771Context
+ 18 : : {
+ 19 : : /**
+ 20 : : * @notice
+ 21 : : * Get the current version of the smart contract
+ 22 : : */
+ 23 : : string public constant VERSION = "0.3.0";
+ 24 : : // Mapping from contract addresses to document names to their corresponding Document structs
+ 25 : : mapping(address => mapping(bytes32 => Document)) private _documents;
+ 26 : : mapping(address => bytes32[]) private _documentNames;
+ 27 : :
+ 28 : : // Constructor to initialize the admin role
+ 29 : : constructor(
+ 30 : : address admin,
+ 31 : : address forwarderIrrevocable
+ 32 : : ) ERC2771Context(forwarderIrrevocable) {
+ 33 : : if (admin == address(0)) {
+ 34 : : revert AdminWithAddressZeroNotAllowed();
+ 35 : : }
+ 36 : : _grantRole(DEFAULT_ADMIN_ROLE, admin);
+ 37 : : }
+ 38 : :
+ 39 : : /*//////////////////////////////////////////////////////////////
+ 40 : : PUBLIC/EXTERNAL FUNCTIONS
+ 41 : : //////////////////////////////////////////////////////////////*/
+ 42 : :
+ 43 : : /**
+ 44 : : * @notice Restricted function to set or update a document
+ 45 : : */
+ 46 : : function setDocument(
+ 47 : : address smartContract,
+ 48 : : bytes32 name_,
+ 49 : : string memory uri_,
+ 50 : : bytes32 documentHash_
+ 51 : : ) public onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 52 : 54 : _setDocument(smartContract, name_, uri_, documentHash_);
+ 53 : : }
+ 54 : :
+ 55 : : /**
+ 56 : : * @notice Restricted function to remove a document for a given smart contract and name
+ 57 : : */
+ 58 : : function removeDocument(
+ 59 : : address smartContract,
+ 60 : : bytes32 name_
+ 61 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 62 : 2 : _removeDocument(smartContract, name_);
+ 63 : : }
+ 64 : :
+ 65 : : /**
+ 66 : : * @notice Batch version of setDocument to handle multiple documents at once
+ 67 : : */
+ 68 : : function batchSetDocuments(
+ 69 : : address[] calldata smartContracts,
+ 70 : : bytes32[] calldata names,
+ 71 : : string[] calldata uris,
+ 72 : : bytes32[] calldata hashes
+ 73 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 74 [ + + ]: : if (
+ 75 : 35 : smartContracts.length == 0 ||
+ 76 : 12 : smartContracts.length != names.length ||
+ 77 : 10 : names.length != uris.length ||
+ 78 : 8 : uris.length != hashes.length
+ 79 : : ) {
+ 80 : 6 : revert InvalidInputLength();
+ 81 : : }
+ 82 : 28 : for (uint256 i = 0; i < smartContracts.length; i++) {
+ 83 : 16 : _setDocument(smartContracts[i], names[i], uris[i], hashes[i]);
+ 84 : : }
+ 85 : : }
+ 86 : :
+ 87 : : /**
+ 88 : : * @notice Batch version of setDocument to handle multiple documents at once
+ 89 : : */
+ 90 : : function batchSetDocuments(
+ 91 : : address smartContract,
+ 92 : : bytes32[] calldata names,
+ 93 : : string[] calldata uris,
+ 94 : : bytes32[] calldata hashes
+ 95 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 96 [ + + ]: : if (
+ 97 : 16 : names.length == 0 ||
+ 98 : 6 : names.length != uris.length ||
+ 99 : 4 : uris.length != hashes.length
+ 100 : : ) {
+ 101 : 4 : revert InvalidInputLength();
102 : : }
- 103 : : }
- 104 : :
- 105 : : /**
- 106 : : * @notice Batch version of removeDocument to handle multiple documents at once
- 107 : : */
- 108 : : function batchRemoveDocuments(
- 109 : : address smartContract,
- 110 : : bytes32[] calldata names
- 111 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
- 112 [ + + ]: : if (
- 113 : 4 : names.length == 0
- 114 : : ) {
- 115 : 2 : revert InvalidInputLength();
- 116 : : }
- 117 : :
- 118 : 7 : for (uint256 i = 0; i < names.length; ++i) {
- 119 : 4 : _removeDocument(smartContract, names[i]);
+ 103 : 14 : for (uint256 i = 0; i < names.length; ++i) {
+ 104 : 8 : _setDocument(smartContract, names[i], uris[i], hashes[i]);
+ 105 : : }
+ 106 : : }
+ 107 : :
+ 108 : : /**
+ 109 : : * @notice Batch version of removeDocument to handle multiple documents at once
+ 110 : : */
+ 111 : : function batchRemoveDocuments(
+ 112 : : address[] calldata smartContracts,
+ 113 : : bytes32[] calldata names
+ 114 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 115 [ + + ]: : if (
+ 116 : 9 : smartContracts.length == 0 ||
+ 117 : : (smartContracts.length != names.length)
+ 118 : : ) {
+ 119 : 4 : revert InvalidInputLength();
120 : : }
- 121 : : }
- 122 : :
- 123 : :
- 124 : : /**
- 125 : : * @notice Public function to get a document from msg.sender
- 126 : : */
- 127 : : function getDocument(
- 128 : : bytes32 name_
- 129 : : ) external view override returns (string memory, bytes32, uint256) {
- 130 : 3 : return _getDocument(msg.sender, name_);
- 131 : : }
- 132 : :
- 133 : : /**
- 134 : : * @notice Public function to get a document for a specific contract address
- 135 : : */
- 136 : : function getDocument(
- 137 : : address smartContract,
- 138 : : bytes32 name_
- 139 : : ) external view returns (string memory, bytes32, uint256) {
- 140 : 57 : return _getDocument(smartContract, name_);
+ 121 : :
+ 122 : 7 : for (uint256 i = 0; i < smartContracts.length; ++i) {
+ 123 : 4 : _removeDocument(smartContracts[i], names[i]);
+ 124 : : }
+ 125 : : }
+ 126 : :
+ 127 : : /**
+ 128 : : * @notice Batch version of removeDocument to handle multiple documents at once
+ 129 : : */
+ 130 : : function batchRemoveDocuments(
+ 131 : : address smartContract,
+ 132 : : bytes32[] calldata names
+ 133 : : ) external onlyRole(DOCUMENT_MANAGER_ROLE) {
+ 134 [ + + ]: 4 : if (names.length == 0) {
+ 135 : 2 : revert InvalidInputLength();
+ 136 : : }
+ 137 : :
+ 138 : 7 : for (uint256 i = 0; i < names.length; ++i) {
+ 139 : 4 : _removeDocument(smartContract, names[i]);
+ 140 : : }
141 : : }
142 : :
143 : : /**
- 144 : : * @notice Get all document names for msg.sender
+ 144 : : * @notice Public function to get a document from msg.sender
145 : : */
- 146 : : function getAllDocuments()
- 147 : : external
- 148 : : view
- 149 : : override
- 150 : : returns (bytes32[] memory)
- 151 : : {
- 152 : 2 : return _documentNames[msg.sender];
- 153 : : }
- 154 : :
- 155 : : /**
- 156 : : * @notice Get all document names for a specific smart contract
- 157 : : */
- 158 : : function getAllDocuments(
- 159 : : address smartContract
- 160 : : ) external view returns (bytes32[] memory) {
- 161 : 14 : return _documentNames[smartContract];
- 162 : : }
- 163 : :
- 164 : :
- 165 : : /* ============ ACCESS CONTROL ============ */
- 166 : : /*
- 167 : : * @dev Returns `true` if `account` has been granted `role`.
- 168 : : */
- 169 : : function hasRole(
- 170 : : bytes32 role,
- 171 : : address account
- 172 : : ) public view virtual override returns (bool) {
- 173 : : // The Default Admin has all roles
- 174 [ + + ]: 98 : if (AccessControl.hasRole(DEFAULT_ADMIN_ROLE, account)) {
- 175 : 86 : return true;
- 176 : : }
- 177 : 18 : return AccessControl.hasRole(role, account);
- 178 : : }
- 179 : :
- 180 : : /*//////////////////////////////////////////////////////////////
- 181 : : INTERNAL FUNCTIONS
- 182 : : //////////////////////////////////////////////////////////////*/
- 183 : :
- 184 : : /**
- 185 : : * @dev Internal function to fetch a document
+ 146 : : function getDocument(
+ 147 : : bytes32 name_
+ 148 : : ) external view override returns (string memory, bytes32, uint256) {
+ 149 : 3 : return _getDocument(msg.sender, name_);
+ 150 : : }
+ 151 : :
+ 152 : : /**
+ 153 : : * @notice Public function to get a document for a specific contract address
+ 154 : : */
+ 155 : : function getDocument(
+ 156 : : address smartContract,
+ 157 : : bytes32 name_
+ 158 : : ) external view returns (string memory, bytes32, uint256) {
+ 159 : 57 : return _getDocument(smartContract, name_);
+ 160 : : }
+ 161 : :
+ 162 : : /**
+ 163 : : * @notice Get all document names for msg.sender
+ 164 : : */
+ 165 : : function getAllDocuments()
+ 166 : : external
+ 167 : : view
+ 168 : : override
+ 169 : : returns (bytes32[] memory)
+ 170 : : {
+ 171 : 2 : return _documentNames[msg.sender];
+ 172 : : }
+ 173 : :
+ 174 : : /**
+ 175 : : * @notice Get all document names for a specific smart contract
+ 176 : : */
+ 177 : : function getAllDocuments(
+ 178 : : address smartContract
+ 179 : : ) external view returns (bytes32[] memory) {
+ 180 : 14 : return _documentNames[smartContract];
+ 181 : : }
+ 182 : :
+ 183 : : /* ============ ACCESS CONTROL ============ */
+ 184 : : /*
+ 185 : : * @dev Returns `true` if `account` has been granted `role`.
186 : : */
- 187 : : function _getDocument(
- 188 : : address smartContract,
- 189 : : bytes32 name_
- 190 : : ) internal view returns (string memory, bytes32, uint256) {
- 191 : 40 : Document memory doc = _documents[smartContract][name_];
- 192 : 40 : return (doc.uri, doc.documentHash, doc.lastModified);
- 193 : : }
- 194 : :
- 195 : : /**
- 196 : : * @dev Internal helper to remove the document name from the list of document names
- 197 : : */
- 198 : : function _removeDocumentName(
- 199 : : address smartContract,
- 200 : : bytes32 name_
- 201 : : ) internal {
- 202 : 10 : uint256 length = _documentNames[smartContract].length;
- 203 : 15 : for (uint256 i = 0; i < length; ++i) {
- 204 [ # + ]: 10 : if (_documentNames[smartContract][i] == name_) {
- 205 : 10 : _documentNames[smartContract][i] = _documentNames[
- 206 : : smartContract
- 207 : : ][length - 1];
- 208 : 10 : _documentNames[smartContract].pop();
- 209 : 10 : break;
- 210 : : }
- 211 : : }
- 212 : : }
- 213 : :
- 214 : : function _removeDocument(address smartContract, bytes32 name_) internal {
- 215 : 10 : Document memory doc = _documents[smartContract][name_];
- 216 : 10 : emit DocumentRemoved(smartContract, name_, doc.uri, doc.documentHash);
- 217 : :
- 218 : 10 : delete _documents[smartContract][name_];
- 219 : 10 : _removeDocumentName(smartContract, name_);
- 220 : : }
- 221 : :
- 222 : : function _setDocument(
- 223 : : address smartContract,
- 224 : : bytes32 name_,
- 225 : : string memory uri_,
- 226 : : bytes32 documentHash_
- 227 : : ) internal {
- 228 : 76 : Document storage doc = _documents[smartContract][name_];
- 229 [ + + ]: 76 : if (doc.lastModified == 0) {
- 230 : : // new document
- 231 : 58 : _documentNames[smartContract].push(name_);
- 232 : : }
- 233 : 76 : doc.uri = uri_;
- 234 : 76 : doc.documentHash = documentHash_;
- 235 : 76 : doc.lastModified = block.timestamp;
- 236 : 76 : emit DocumentUpdated(smartContract, name_, uri_, documentHash_);
- 237 : : }
- 238 : : }
+ 187 : : function hasRole(
+ 188 : : bytes32 role,
+ 189 : : address account
+ 190 : : ) public view virtual override returns (bool) {
+ 191 : : // The Default Admin has all roles
+ 192 [ + + ]: 100 : if (AccessControl.hasRole(DEFAULT_ADMIN_ROLE, account)) {
+ 193 : 88 : return true;
+ 194 : : }
+ 195 : 18 : return AccessControl.hasRole(role, account);
+ 196 : : }
+ 197 : :
+ 198 : : /*//////////////////////////////////////////////////////////////
+ 199 : : INTERNAL FUNCTIONS
+ 200 : : //////////////////////////////////////////////////////////////*/
+ 201 : :
+ 202 : : /**
+ 203 : : * @dev Internal function to fetch a document
+ 204 : : */
+ 205 : : function _getDocument(
+ 206 : : address smartContract,
+ 207 : : bytes32 name_
+ 208 : : ) internal view returns (string memory, bytes32, uint256) {
+ 209 : 40 : Document memory doc = _documents[smartContract][name_];
+ 210 : 40 : return (doc.uri, doc.documentHash, doc.lastModified);
+ 211 : : }
+ 212 : :
+ 213 : : /**
+ 214 : : * @dev Internal helper to remove the document name from the list of document names
+ 215 : : */
+ 216 : : function _removeDocumentName(
+ 217 : : address smartContract,
+ 218 : : bytes32 name_
+ 219 : : ) internal {
+ 220 : 10 : uint256 length = _documentNames[smartContract].length;
+ 221 : 15 : for (uint256 i = 0; i < length; ++i) {
+ 222 [ # + ]: 10 : if (_documentNames[smartContract][i] == name_) {
+ 223 : 10 : _documentNames[smartContract][i] = _documentNames[
+ 224 : : smartContract
+ 225 : : ][length - 1];
+ 226 : 10 : _documentNames[smartContract].pop();
+ 227 : 10 : break;
+ 228 : : }
+ 229 : : }
+ 230 : : }
+ 231 : :
+ 232 : : function _removeDocument(address smartContract, bytes32 name_) internal {
+ 233 : 10 : Document memory doc = _documents[smartContract][name_];
+ 234 : 10 : emit DocumentRemoved(smartContract, name_, doc.uri, doc.documentHash);
+ 235 : :
+ 236 : 10 : delete _documents[smartContract][name_];
+ 237 : 10 : _removeDocumentName(smartContract, name_);
+ 238 : : }
+ 239 : :
+ 240 : : function _setDocument(
+ 241 : : address smartContract,
+ 242 : : bytes32 name_,
+ 243 : : string memory uri_,
+ 244 : : bytes32 documentHash_
+ 245 : : ) internal {
+ 246 : 78 : Document storage doc = _documents[smartContract][name_];
+ 247 [ + + ]: 78 : if (doc.lastModified == 0) {
+ 248 : : // new document
+ 249 : 60 : _documentNames[smartContract].push(name_);
+ 250 : : }
+ 251 : 78 : doc.uri = uri_;
+ 252 : 78 : doc.documentHash = documentHash_;
+ 253 : 78 : doc.lastModified = block.timestamp;
+ 254 : 78 : emit DocumentUpdated(smartContract, name_, uri_, documentHash_);
+ 255 : : }
+ 256 : :
+ 257 : : /*//////////////////////////////////////////////////////////////
+ 258 : : ERC2771
+ 259 : : //////////////////////////////////////////////////////////////*/
+ 260 : :
+ 261 : : /**
+ 262 : : * @dev This surcharge is not necessary if you do not use ERC2771
+ 263 : : */
+ 264 : : function _msgSender()
+ 265 : : internal
+ 266 : : view
+ 267 : : override(ERC2771Context, Context)
+ 268 : : returns (address sender)
+ 269 : : {
+ 270 : 150 : return ERC2771Context._msgSender();
+ 271 : : }
+ 272 : :
+ 273 : : /**
+ 274 : : * @dev This surcharge is not necessary if you do not use ERC2771
+ 275 : : */
+ 276 : : function _msgData()
+ 277 : : internal
+ 278 : : view
+ 279 : : override(ERC2771Context, Context)
+ 280 : : returns (bytes calldata)
+ 281 : : {
+ 282 : 0 : return ERC2771Context._msgData();
+ 283 : : }
+ 284 : :
+ 285 : : /**
+ 286 : : * @dev This surcharge is not necessary if you do not use the MetaTxModule
+ 287 : : */
+ 288 : : function _contextSuffixLength()
+ 289 : : internal
+ 290 : : view
+ 291 : : override(ERC2771Context, Context)
+ 292 : : returns (uint256)
+ 293 : : {
+ 294 : 150 : return ERC2771Context._contextSuffixLength();
+ 295 : : }
+ 296 : : }
diff --git a/doc/coverage/coverage/src/index-sort-b.html b/doc/coverage/coverage/src/index-sort-b.html
index c50c7f5..2fc9e73 100644
--- a/doc/coverage/coverage/src/index-sort-b.html
+++ b/doc/coverage/coverage/src/index-sort-b.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
DocumentEngine.sol |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/src/index-sort-f.html b/doc/coverage/coverage/src/index-sort-f.html
index 78be304..6d71c92 100644
--- a/doc/coverage/coverage/src/index-sort-f.html
+++ b/doc/coverage/coverage/src/index-sort-f.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
DocumentEngine.sol |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/src/index-sort-l.html b/doc/coverage/coverage/src/index-sort-l.html
index 81a3037..9d0c2f2 100644
--- a/doc/coverage/coverage/src/index-sort-l.html
+++ b/doc/coverage/coverage/src/index-sort-l.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
DocumentEngine.sol |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/coverage/src/index.html b/doc/coverage/coverage/src/index.html
index 6573f91..64ab2cb 100644
--- a/doc/coverage/coverage/src/index.html
+++ b/doc/coverage/coverage/src/index.html
@@ -31,18 +31,18 @@
|
-
-
-
+
+
+
-
+
|
-
-
-
+
+
+
|
@@ -84,12 +84,12 @@
DocumentEngine.sol |
-
+
|
- 100.0 % |
- 48 / 48 |
- 90.9 % |
- 10 / 11 |
+ 98.1 % |
+ 51 / 52 |
+ 85.7 % |
+ 12 / 14 |
92.9 % |
13 / 14 |
diff --git a/doc/coverage/lcov.info b/doc/coverage/lcov.info
index ffd73b6..ba3dafb 100644
--- a/doc/coverage/lcov.info
+++ b/doc/coverage/lcov.info
@@ -1,169 +1,185 @@
TN:
SF:src/DocumentEngine.sol
-FN:25,DocumentEngine.setDocument
-FNDA:27,DocumentEngine.setDocument
-DA:31,26
-DA:31,26
-FN:37,DocumentEngine.removeDocument
+FN:46,DocumentEngine.setDocument
+FNDA:28,DocumentEngine.setDocument
+DA:52,27
+DA:52,27
+FN:58,DocumentEngine.removeDocument
FNDA:2,DocumentEngine.removeDocument
-DA:41,1
-DA:41,1
-FN:47,DocumentEngine.batchSetDocuments
+DA:62,1
+DA:62,1
+FN:68,DocumentEngine.batchSetDocuments
FNDA:8,DocumentEngine.batchSetDocuments
-DA:54,7
-DA:54,7
-DA:54,7
-DA:54,7
-DA:54,7
-DA:55,6
-DA:55,6
-DA:56,5
-DA:56,5
-DA:57,4
-DA:57,4
-BRDA:53,0,0,3
-BRDA:53,0,1,4
-DA:59,3
-DA:59,3
-DA:61,4
-DA:61,4
-DA:61,12
-DA:61,8
-DA:62,8
-DA:62,8
-FN:69,DocumentEngine.batchSetDocuments
-FNDA:5,DocumentEngine.batchSetDocuments
-DA:76,4
-DA:76,4
-DA:76,4
-DA:76,4
-DA:76,3
-DA:77,2
-DA:77,2
-BRDA:75,1,0,2
-BRDA:75,1,1,2
-DA:79,2
-DA:79,2
-DA:81,2
-DA:81,2
-DA:81,6
-DA:81,4
+DA:75,7
+DA:75,7
+DA:75,7
+DA:75,7
+DA:75,7
+DA:76,6
+DA:76,6
+DA:77,5
+DA:77,5
+DA:78,4
+DA:78,4
+BRDA:74,0,0,3
+BRDA:74,0,1,4
+DA:80,3
+DA:80,3
DA:82,4
DA:82,4
-FN:89,DocumentEngine.batchRemoveDocuments
-FNDA:4,DocumentEngine.batchRemoveDocuments
-DA:94,3
-DA:94,3
-DA:94,3
-BRDA:93,2,0,2
-BRDA:93,2,1,1
-DA:97,2
-DA:97,2
-DA:100,1
-DA:100,1
-DA:100,3
-DA:100,2
+DA:82,12
+DA:82,8
+DA:83,8
+DA:83,8
+FN:90,DocumentEngine.batchSetDocuments
+FNDA:5,DocumentEngine.batchSetDocuments
+DA:97,4
+DA:97,4
+DA:97,4
+DA:97,4
+DA:98,3
+DA:98,3
+DA:99,2
+DA:99,2
+BRDA:96,1,0,2
+BRDA:96,1,1,2
DA:101,2
DA:101,2
-FN:108,DocumentEngine.batchRemoveDocuments
-FNDA:3,DocumentEngine.batchRemoveDocuments
-DA:113,2
-DA:113,2
-BRDA:112,3,0,1
-BRDA:112,3,1,1
-DA:115,1
-DA:115,1
-DA:118,1
-DA:118,1
-DA:118,3
-DA:118,2
+DA:103,2
+DA:103,2
+DA:103,6
+DA:103,4
+DA:104,4
+DA:104,4
+FN:111,DocumentEngine.batchRemoveDocuments
+FNDA:4,DocumentEngine.batchRemoveDocuments
+DA:116,3
+DA:116,3
+DA:116,3
+BRDA:115,2,0,2
+BRDA:115,2,1,1
DA:119,2
DA:119,2
-FN:127,DocumentEngine.getDocument
+DA:122,1
+DA:122,1
+DA:122,3
+DA:122,2
+DA:123,2
+DA:123,2
+FN:130,DocumentEngine.batchRemoveDocuments
+FNDA:3,DocumentEngine.batchRemoveDocuments
+DA:134,2
+DA:134,2
+BRDA:134,3,0,1
+BRDA:134,3,1,1
+DA:135,1
+DA:135,1
+DA:138,1
+DA:138,1
+DA:138,3
+DA:138,2
+DA:139,2
+DA:139,2
+FN:146,DocumentEngine.getDocument
FNDA:1,DocumentEngine.getDocument
-DA:130,1
-DA:130,1
-DA:130,1
-FN:136,DocumentEngine.getDocument
+DA:149,1
+DA:149,1
+DA:149,1
+FN:155,DocumentEngine.getDocument
FNDA:19,DocumentEngine.getDocument
-DA:140,19
-DA:140,19
-DA:140,19
-FN:146,DocumentEngine.getAllDocuments
+DA:159,19
+DA:159,19
+DA:159,19
+FN:165,DocumentEngine.getAllDocuments
FNDA:1,DocumentEngine.getAllDocuments
-DA:152,1
-DA:152,1
-FN:158,DocumentEngine.getAllDocuments
+DA:171,1
+DA:171,1
+FN:177,DocumentEngine.getAllDocuments
FNDA:7,DocumentEngine.getAllDocuments
-DA:161,7
-DA:161,7
-FN:169,DocumentEngine.hasRole
+DA:180,7
+DA:180,7
+FN:187,DocumentEngine.hasRole
FNDA:0,DocumentEngine.hasRole
-DA:174,49
-DA:174,49
-BRDA:174,4,0,43
-BRDA:174,4,1,6
-DA:175,43
-DA:175,43
-DA:177,6
-DA:177,6
-DA:177,6
-FN:187,DocumentEngine._getDocument
+DA:192,50
+DA:192,50
+BRDA:192,4,0,44
+BRDA:192,4,1,6
+DA:193,44
+DA:193,44
+DA:195,6
+DA:195,6
+DA:195,6
+FN:205,DocumentEngine._getDocument
FNDA:20,DocumentEngine._getDocument
-DA:191,20
-DA:191,20
-DA:192,20
-DA:192,20
-FN:198,DocumentEngine._removeDocumentName
+DA:209,20
+DA:209,20
+DA:210,20
+DA:210,20
+FN:216,DocumentEngine._removeDocumentName
FNDA:5,DocumentEngine._removeDocumentName
-DA:202,5
-DA:202,5
-DA:203,5
-DA:203,5
-DA:203,5
-DA:203,0
-DA:204,5
-DA:204,5
-BRDA:204,5,0,-
-BRDA:204,5,1,5
-DA:205,5
-DA:205,5
-DA:208,5
-DA:208,5
-DA:209,5
-DA:209,5
-FN:214,DocumentEngine._removeDocument
+DA:220,5
+DA:220,5
+DA:221,5
+DA:221,5
+DA:221,5
+DA:221,0
+DA:222,5
+DA:222,5
+BRDA:222,5,0,-
+BRDA:222,5,1,5
+DA:223,5
+DA:223,5
+DA:226,5
+DA:226,5
+DA:227,5
+DA:227,5
+FN:232,DocumentEngine._removeDocument
FNDA:5,DocumentEngine._removeDocument
-DA:215,5
-DA:215,5
-DA:216,5
-DA:216,5
-DA:218,5
-DA:218,5
-DA:219,5
-DA:219,5
-FN:222,DocumentEngine._setDocument
-FNDA:38,DocumentEngine._setDocument
-DA:228,38
-DA:228,38
-DA:229,38
-DA:229,38
-BRDA:229,6,0,29
-BRDA:229,6,1,38
-DA:231,29
-DA:231,29
-DA:233,38
-DA:233,38
-DA:234,38
-DA:234,38
-DA:235,38
-DA:235,38
-DA:236,38
-DA:236,38
-FNF:15
-FNH:14
-LF:48
-LH:48
+DA:233,5
+DA:233,5
+DA:234,5
+DA:234,5
+DA:236,5
+DA:236,5
+DA:237,5
+DA:237,5
+FN:240,DocumentEngine._setDocument
+FNDA:39,DocumentEngine._setDocument
+DA:246,39
+DA:246,39
+DA:247,39
+DA:247,39
+BRDA:247,6,0,30
+BRDA:247,6,1,39
+DA:249,30
+DA:249,30
+DA:251,39
+DA:251,39
+DA:252,39
+DA:252,39
+DA:253,39
+DA:253,39
+DA:254,39
+DA:254,39
+FN:264,DocumentEngine._msgSender
+FNDA:50,DocumentEngine._msgSender
+DA:270,50
+DA:270,50
+DA:270,50
+FN:276,DocumentEngine._msgData
+FNDA:0,DocumentEngine._msgData
+DA:282,0
+DA:282,0
+DA:282,0
+FN:288,DocumentEngine._contextSuffixLength
+FNDA:50,DocumentEngine._contextSuffixLength
+DA:294,50
+DA:294,50
+DA:294,50
+FNF:18
+FNH:16
+LF:52
+LH:51
BRF:14
BRH:13
end_of_record
diff --git a/src/DocumentEngine.sol b/src/DocumentEngine.sol
index 1fd41ce..3f4fff9 100644
--- a/src/DocumentEngine.sol
+++ b/src/DocumentEngine.sol
@@ -10,7 +10,12 @@ import "./DocumentEngineInvariant.sol";
* @title DocumentEngine
* @notice contract to manage documents on-chain through ERC-1643
*/
-contract DocumentEngine is IERC1643, DocumentEngineInvariant, AccessControl, ERC2771Context {
+contract DocumentEngine is
+ IERC1643,
+ DocumentEngineInvariant,
+ AccessControl,
+ ERC2771Context
+{
/**
* @notice
* Get the current version of the smart contract
@@ -21,7 +26,10 @@ contract DocumentEngine is IERC1643, DocumentEngineInvariant, AccessControl, ERC
mapping(address => bytes32[]) private _documentNames;
// Constructor to initialize the admin role
- constructor(address admin, address forwarderIrrevocable) ERC2771Context(forwarderIrrevocable){
+ constructor(
+ address admin,
+ address forwarderIrrevocable
+ ) ERC2771Context(forwarderIrrevocable) {
if (admin == address(0)) {
revert AdminWithAddressZeroNotAllowed();
}
diff --git a/test/DocumentEngine.t.sol b/test/DocumentEngine.t.sol
index 99f8b76..fda9890 100644
--- a/test/DocumentEngine.t.sol
+++ b/test/DocumentEngine.t.sol
@@ -70,9 +70,7 @@ contract DocumentEngineTest is Test, DocumentEngineInvariant, AccessControl {
assertEq(documentEngine.isTrustedForwarder(forwarder), true);
// admin
vm.expectRevert(
- abi.encodeWithSelector(
- AdminWithAddressZeroNotAllowed.selector
- )
+ abi.encodeWithSelector(AdminWithAddressZeroNotAllowed.selector)
);
documentEngine = new DocumentEngine(AddressZero, forwarder);
}