• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lukso-network / lsp-smart-contracts / 4483662720

pending completion
4483662720

push

github

GitHub
Merge pull request #517 from lukso-network/develop

717 of 852 branches covered (84.15%)

Branch coverage included in aggregate %.

328 of 328 new or added lines in 19 files covered. (100.0%)

1217 of 1342 relevant lines covered (90.69%)

2349.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.36
/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol
1
// SPDX-License-Identifier: Apache-2.0
2
pragma solidity ^0.8.5;
3

4
// modules
5
import {ERC725Y} from "@erc725/smart-contracts/contracts/ERC725Y.sol";
6

7
// libraries
8
import {GasLib} from "../../Utils/GasLib.sol";
9
import {LSP6Utils} from "../LSP6Utils.sol";
10

11
// constants
12
import {
13
    _LSP6KEY_ADDRESSPERMISSIONS_ARRAY,
14
    _LSP6KEY_ADDRESSPERMISSIONS_ARRAY_PREFIX,
15
    _LSP6KEY_ADDRESSPERMISSIONS_PREFIX,
16
    _LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX,
17
    _LSP6KEY_ADDRESSPERMISSIONS_ALLOWEDCALLS_PREFIX,
18
    _LSP6KEY_ADDRESSPERMISSIONS_AllowedERC725YDataKeys_PREFIX,
19
    _PERMISSION_SETDATA,
20
    _PERMISSION_SUPER_SETDATA,
21
    _PERMISSION_ADDCONTROLLER,
22
    _PERMISSION_EDITPERMISSIONS,
23
    _PERMISSION_ADDEXTENSIONS,
24
    _PERMISSION_CHANGEEXTENSIONS,
25
    _PERMISSION_ADDUNIVERSALRECEIVERDELEGATE,
26
    _PERMISSION_CHANGEUNIVERSALRECEIVERDELEGATE
27
} from "../LSP6Constants.sol";
28
import {
29
    _LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX,
30
    _LSP1_UNIVERSAL_RECEIVER_DELEGATE_KEY
31
} from "../../LSP1UniversalReceiver/LSP1Constants.sol";
32
import {_LSP17_EXTENSION_PREFIX} from "../../LSP17ContractExtension/LSP17Constants.sol";
33

34
// errors
35
import {
36
    NotRecognisedPermissionKey,
37
    AddressPermissionArrayIndexValueNotAnAddress,
38
    InvalidEncodedAllowedCalls,
39
    InvalidEncodedAllowedERC725YDataKeys,
40
    NoERC725YDataKeysAllowed,
41
    NotAllowedERC725YDataKey,
42
    NotAuthorised
43
} from "../LSP6Errors.sol";
44

45
abstract contract LSP6SetDataModule {
46
    using LSP6Utils for *;
47

48
    /**
49
     * @dev verify if the `controllerAddress` has the permissions required to set a data key on the ERC725Y storage of the `controlledContract`.
50
     * @param controlledContract the address of the ERC725Y contract where the data key is set.
51
     * @param controllerAddress the address of the controller who wants to set the data key.
52
     * @param controllerPermissions the permissions of the controller address.
53
     * @param inputDataKey the data key to set on the `controlledContract`.
54
     * @param inputDataValue the data value to set for the `inputDataKey`.
55
     */
56
    function _verifyCanSetData(
57
        address controlledContract,
58
        address controllerAddress,
59
        bytes32 controllerPermissions,
60
        bytes32 inputDataKey,
61
        bytes memory inputDataValue
62
    ) internal view virtual {
63
        bytes32 requiredPermission = _getPermissionRequiredToSetDataKey(
6,003✔
64
            controlledContract,
65
            inputDataKey,
66
            inputDataValue
67
        );
68

69
        // CHECK if allowed to set an ERC725Y Data Key
70
        if (requiredPermission == _PERMISSION_SETDATA) {
5,891✔
71
            // Skip if caller has SUPER permissions
72
            if (controllerPermissions.hasPermission(_PERMISSION_SUPER_SETDATA)) return;
2,421✔
73

74
            _requirePermissions(controllerAddress, controllerPermissions, _PERMISSION_SETDATA);
1,881✔
75

76
            _verifyAllowedERC725YSingleKey(
1,833✔
77
                controllerAddress,
78
                inputDataKey,
79
                ERC725Y(controlledContract).getAllowedERC725YDataKeysFor(controllerAddress)
80
            );
81
        } else {
82
            // Otherwise CHECK the required permission if setting LSP6 permissions, LSP1 Delegate or LSP17 Extensions.
83
            _requirePermissions(controllerAddress, controllerPermissions, requiredPermission);
3,470✔
84
        }
85
    }
86

87
    /**
88
     * @dev verify if the `controllerAddress` has the permissions required to set an array of data keys on the ERC725Y storage of the `controlledContract`.
89
     * @param controlledContract the address of the ERC725Y contract where the data key is set.
90
     * @param controllerAddress the address of the controller who wants to set the data key.
91
     * @param controllerPermissions the permissions of the controller address.
92
     * @param inputDataKeys an array of data keys to set on the `controlledContract`.
93
     * @param inputDataValues an array of data values to set for the `inputDataKeys`.
94
     */
95
    function _verifyCanSetData(
96
        address controlledContract,
97
        address controllerAddress,
98
        bytes32 controllerPermissions,
99
        bytes32[] memory inputDataKeys,
100
        bytes[] memory inputDataValues
101
    ) internal view virtual {
102
        bool isSettingERC725YKeys;
11,299✔
103
        bool[] memory validatedInputDataKeys = new bool[](inputDataKeys.length);
11,299✔
104

105
        bytes32 requiredPermission;
11,299✔
106

107
        uint256 ii;
11,299✔
108
        do {
11,299✔
109
            requiredPermission = _getPermissionRequiredToSetDataKey(
110
                controlledContract,
111
                inputDataKeys[ii],
112
                inputDataValues[ii]
113
            );
114

115
            if (requiredPermission == _PERMISSION_SETDATA) {
116
                isSettingERC725YKeys = true;
117
            } else {
118
                // CHECK the required permissions if setting LSP6 permissions, LSP1 Delegate or LSP17 Extensions.
119
                _requirePermissions(controllerAddress, controllerPermissions, requiredPermission);
120
                validatedInputDataKeys[ii] = true;
121
            }
122

123
            ii = GasLib.uncheckedIncrement(ii);
124
        } while (ii < inputDataKeys.length);
125

126
        // CHECK if allowed to set one (or multiple) ERC725Y Data Keys
127
        if (isSettingERC725YKeys) {
11,209✔
128
            // Skip if caller has SUPER permissions
129
            if (controllerPermissions.hasPermission(_PERMISSION_SUPER_SETDATA)) return;
2,309✔
130

131
            _requirePermissions(controllerAddress, controllerPermissions, _PERMISSION_SETDATA);
998✔
132

133
            _verifyAllowedERC725YDataKeys(
954✔
134
                controllerAddress,
135
                inputDataKeys,
136
                ERC725Y(controlledContract).getAllowedERC725YDataKeysFor(controllerAddress),
137
                validatedInputDataKeys
138
            );
139
        }
140
    }
141

142
    /**
143
     * @dev retrieve the permission required based on the data key to be set on the `controlledContract`.
144
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
145
     * @param inputDataKey the data key to set on the `controlledContract`. Can be related to LSP6 Permissions, LSP1 Delegate or LSP17 Extensions.
146
     * @param inputDataValue the data value to set for the `inputDataKey`.
147
     * @return the permission required to set the `inputDataKey` on the `controlledContract`.
148
     */
149
    function _getPermissionRequiredToSetDataKey(
150
        address controlledContract,
151
        bytes32 inputDataKey,
152
        bytes memory inputDataValue
153
    ) internal view virtual returns (bytes32) {
154
        // AddressPermissions[] or AddressPermissions[index]
155
        if (bytes16(inputDataKey) == _LSP6KEY_ADDRESSPERMISSIONS_ARRAY_PREFIX) {
29,435✔
156
            return
1,435✔
157
                _getPermissionToSetPermissionsArray(
158
                    controlledContract,
159
                    inputDataKey,
160
                    inputDataValue
161
                );
162

163
            // AddressPermissions:...
164
        } else if (bytes6(inputDataKey) == _LSP6KEY_ADDRESSPERMISSIONS_PREFIX) {
28,000✔
165
            // AddressPermissions:Permissions:<address>
166
            if (bytes12(inputDataKey) == _LSP6KEY_ADDRESSPERMISSIONS_PERMISSIONS_PREFIX) {
14,962✔
167
                return _getPermissionToSetControllerPermissions(controlledContract, inputDataKey);
9,961✔
168

169
                // AddressPermissions:AllowedCalls:<address>
170
            } else if (bytes12(inputDataKey) == _LSP6KEY_ADDRESSPERMISSIONS_ALLOWEDCALLS_PREFIX) {
5,001✔
171
                return
2,651✔
172
                    _getPermissionToSetAllowedCalls(
173
                        controlledContract,
174
                        inputDataKey,
175
                        inputDataValue
176
                    );
177

178
                // AddressPermissions:AllowedERC725YKeys:<address>
179
            } else if (
2,350✔
180
                bytes12(inputDataKey) == _LSP6KEY_ADDRESSPERMISSIONS_AllowedERC725YDataKeys_PREFIX
181
            ) {
182
                return
2,334✔
183
                    _getPermissionToSetAllowedERC725YDataKeys(
184
                        controlledContract,
185
                        inputDataKey,
186
                        inputDataValue
187
                    );
188

189
                // if the first 6 bytes of the input data key are "AddressPermissions:..." but did not match
190
                // with anything above, this is not a standard LSP6 permission data key so we revert.
191
            } else {
192
                /**
193
                 * @dev more permissions types starting with `AddressPermissions:...` can be implemented by overriding this function.
194
                 *
195
                 *      // AddressPermissions:MyCustomPermissions:<address>
196
                 *      bytes12 CUSTOM_PERMISSION_PREFIX = 0x4b80742de2bf9e659ba40000
197
                 *
198
                 *      if (bytes12(dataKey) == CUSTOM_PERMISSION_PREFIX) {
199
                 *          // custom logic
200
                 *      }
201
                 *
202
                 *      super._getPermissionRequiredToSetDataKey(...)
203
                 */
204
                revert NotRecognisedPermissionKey(inputDataKey);
16✔
205
            }
206

207
            // LSP1UniversalReceiverDelegate or LSP1UniversalReceiverDelegate:<typeId>
208
        } else if (
13,038✔
209
            inputDataKey == _LSP1_UNIVERSAL_RECEIVER_DELEGATE_KEY ||
1,981✔
210
            bytes12(inputDataKey) == _LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX
211
        ) {
212
            return _getPermissionToSetLSP1Delegate(controlledContract, inputDataKey);
1,981✔
213

214
            // LSP17Extension:<bytes4>
215
        } else if (bytes12(inputDataKey) == _LSP17_EXTENSION_PREFIX) {
11,057✔
216
            return _getPermissionToSetLSP17Extension(controlledContract, inputDataKey);
1,164✔
217
        } else {
218
            return _PERMISSION_SETDATA;
9,893✔
219
        }
220
    }
221

222
    /**
223
     * @dev retrieve the permission required to update the `AddressPermissions[]` array data key defined in LSP6.
224
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
225
     * @param inputDataKey either `AddressPermissions[]` (array length) or `AddressPermissions[index]` (array index)
226
     * @param inputDataValue the updated value for the `inputDataKey`. MUST be:
227
     *  - a `uint256` for `AddressPermissions[]` (array length)
228
     *  - an `address` or `0x` for `AddressPermissions[index]` (array entry).
229
     *
230
     * @return either ADD or CHANGE PERMISSIONS.
231
     */
232
    function _getPermissionToSetPermissionsArray(
233
        address controlledContract,
234
        bytes32 inputDataKey,
235
        bytes memory inputDataValue
236
    ) internal view virtual returns (bytes32) {
237
        bytes memory currentValue = ERC725Y(controlledContract).getData(inputDataKey);
1,435✔
238

239
        // AddressPermissions[] -> array length
240
        if (inputDataKey == _LSP6KEY_ADDRESSPERMISSIONS_ARRAY) {
1,435✔
241
            uint128 newLength = uint128(bytes16(inputDataValue));
817✔
242

243
            return
817✔
244
                newLength > uint128(bytes16(currentValue))
245
                    ? _PERMISSION_ADDCONTROLLER
817✔
246
                    : _PERMISSION_EDITPERMISSIONS;
247
        }
248

249
        // AddressPermissions[index] -> array index
250

251
        // CHECK that we either ADD an address (20 bytes long) or REMOVE an address (0x)
252
        if (inputDataValue.length != 0 && inputDataValue.length != 20) {
618✔
253
            revert AddressPermissionArrayIndexValueNotAnAddress(inputDataKey, inputDataValue);
32✔
254
        }
255

256
        return currentValue.length == 0 ? _PERMISSION_ADDCONTROLLER : _PERMISSION_EDITPERMISSIONS;
586✔
257
    }
258

259
    /**
260
     * @dev retrieve the permission required to set permissions for a controller address.
261
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
262
     * @param inputPermissionDataKey `AddressPermissions:Permissions:<controller-address>`.
263
     * @return either ADD or CHANGE PERMISSIONS.
264
     */
265
    function _getPermissionToSetControllerPermissions(
266
        address controlledContract,
267
        bytes32 inputPermissionDataKey
268
    ) internal view virtual returns (bytes32) {
269
        return
9,961✔
270
            // if there is nothing stored under the data key, we are trying to ADD a new controller.
271
            // if there are already some permissions set under the data key, we are trying to CHANGE the permissions of a controller.
272
            bytes32(ERC725Y(controlledContract).getData(inputPermissionDataKey)) == bytes32(0)
273
                ? _PERMISSION_ADDCONTROLLER
9,961✔
274
                : _PERMISSION_EDITPERMISSIONS;
275
    }
276

277
    /**
278
     * @dev retrieve the permission required to set some AllowedCalls for a controller.
279
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
280
     * @param dataKey `AddressPermissions:AllowedCalls:<controller-address>`.
281
     * @param dataValue the updated value for the `dataKey`. MUST be a bytes28[CompactBytesArray] of Allowed Calls.
282
     * @return either ADD or CHANGE PERMISSIONS.
283
     */
284
    function _getPermissionToSetAllowedCalls(
285
        address controlledContract,
286
        bytes32 dataKey,
287
        bytes memory dataValue
288
    ) internal view virtual returns (bytes32) {
289
        if (!LSP6Utils.isCompactBytesArrayOfAllowedCalls(dataValue)) {
2,651✔
290
            revert InvalidEncodedAllowedCalls(dataValue);
48✔
291
        }
292

293
        // if there is nothing stored under the Allowed Calls of the controller,
294
        // we are trying to ADD a list of restricted calls (standards + address + function selector)
295
        //
296
        // if there are already some data set under the Allowed Calls of the controller,
297
        // we are trying to CHANGE (= edit) these restrictions.
298
        return
2,603✔
299
            ERC725Y(controlledContract).getData(dataKey).length == 0
300
                ? _PERMISSION_ADDCONTROLLER
2,603✔
301
                : _PERMISSION_EDITPERMISSIONS;
302
    }
303

304
    /**
305
     * @dev retrieve the permission required to set some AllowedCalls for a controller.
306
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
307
     * @param dataKey  or `AddressPermissions:AllowedERC725YDataKeys:<controller-address>`.
308
     * @param dataValue the updated value for the `dataKey`. MUST be a bytes[CompactBytesArray] of Allowed ERC725Y Data Keys.
309
     * @return either ADD or CHANGE PERMISSIONS.
310
     */
311
    function _getPermissionToSetAllowedERC725YDataKeys(
312
        address controlledContract,
313
        bytes32 dataKey,
314
        bytes memory dataValue
315
    ) internal view returns (bytes32) {
316
        if (!LSP6Utils.isCompactBytesArrayOfAllowedERC725YDataKeys(dataValue)) {
2,334✔
317
            revert InvalidEncodedAllowedERC725YDataKeys(
16✔
318
                dataValue,
319
                "couldn't VALIDATE the data value"
320
            );
321
        }
322

323
        // if there is nothing stored under the Allowed ERC725Y Data Keys of the controller,
324
        // we are trying to ADD a list of restricted ERC725Y Data Keys.
325
        //
326
        // if there are already some data set under the Allowed ERC725Y Data Keys of the controller,
327
        // we are trying to CHANGE (= edit) these restricted ERC725Y data keys.
328
        return
2,318✔
329
            ERC725Y(controlledContract).getData(dataKey).length == 0
330
                ? _PERMISSION_ADDCONTROLLER
2,318✔
331
                : _PERMISSION_EDITPERMISSIONS;
332
    }
333

334
    /**
335
     * @dev retrieve the permission required to either add or change the address
336
     * of a LSP1 Universal Receiver Delegate stored under a specific LSP1 data key.
337
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
338
     * @param lsp1DelegateDataKey either the data key for the default `LSP1UniversalReceiverDelegate`,
339
     * or a data key for a specific `LSP1UniversalReceiverDelegate:<typeId>`, starting with `_LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX`.
340
     * @return either ADD or CHANGE UNIVERSALRECEIVERDELEGATE.
341
     */
342
    function _getPermissionToSetLSP1Delegate(
343
        address controlledContract,
344
        bytes32 lsp1DelegateDataKey
345
    ) internal view virtual returns (bytes32) {
346
        return
1,981✔
347
            ERC725Y(controlledContract).getData(lsp1DelegateDataKey).length == 0
348
                ? _PERMISSION_ADDUNIVERSALRECEIVERDELEGATE
1,981✔
349
                : _PERMISSION_CHANGEUNIVERSALRECEIVERDELEGATE;
350
    }
351

352
    /**
353
     * @dev Verify if `controller` has the required permissions to either add or change the address
354
     * of an LSP0 Extension stored under a specific LSP17Extension data key
355
     * @param controlledContract the address of the ERC725Y contract where the data key is verified.
356
     * @param lsp17ExtensionDataKey the dataKey to set with `_LSP17_EXTENSION_PREFIX` as prefix.
357
     */
358
    function _getPermissionToSetLSP17Extension(
359
        address controlledContract,
360
        bytes32 lsp17ExtensionDataKey
361
    ) internal view virtual returns (bytes32) {
362
        return
1,164✔
363
            ERC725Y(controlledContract).getData(lsp17ExtensionDataKey).length == 0
364
                ? _PERMISSION_ADDEXTENSIONS
1,164✔
365
                : _PERMISSION_CHANGEEXTENSIONS;
366
    }
367

368
    /**
369
     * @dev Verify if the `inputKey` is present in the list of `allowedERC725KeysCompacted` for the `controllerAddress`.
370
     * @param controllerAddress the address of the controller.
371
     * @param inputDataKey the data key to verify against the allowed ERC725Y Data Keys for the `controllerAddress`.
372
     * @param allowedERC725YDataKeysCompacted a CompactBytesArray of allowed ERC725Y Data Keys for the `controllerAddress`.
373
     */
374
    function _verifyAllowedERC725YSingleKey(
375
        address controllerAddress,
376
        bytes32 inputDataKey,
377
        bytes memory allowedERC725YDataKeysCompacted
378
    ) internal pure virtual {
379
        if (allowedERC725YDataKeysCompacted.length == 0)
1,946✔
380
            revert NoERC725YDataKeysAllowed(controllerAddress);
32✔
381

382
        /**
383
         * The pointer will always land on the length of each bytes value:
384
         *
385
         * ↓↓
386
         * 03 a00000
387
         * 05 fff83a0011
388
         * 20 aa0000000000000000000000000000000000000000000000000000000000cafe
389
         * 12 bb000000000000000000000000000000beef
390
         * 19 cc00000000000000000000000000000000000000000000deed
391
         * ↑↑
392
         *
393
         */
394
        uint256 pointer;
1,914✔
395

396
        // information extracted from each Allowed ERC725Y Data Key.
397
        uint256 length;
1,914✔
398
        bytes32 allowedKey;
1,914✔
399
        bytes32 mask;
1,914✔
400

401
        /**
402
         * iterate over each data key and update the `pointer` variable with the index where to find the length of each data key.
403
         *
404
         * 0x 03 a00000 03 fff83a 20 aa00...00cafe
405
         *    ↑↑        ↑↑        ↑↑
406
         *  first  |  second  |  third
407
         *  length |  length  |  length
408
         */
409
        while (pointer < allowedERC725YDataKeysCompacted.length) {
1,914✔
410
            // save the length of the allowed data key to calculate the `mask`.
411
            length = uint16(
2,974✔
412
                bytes2(
413
                    abi.encodePacked(
414
                        allowedERC725YDataKeysCompacted[pointer],
415
                        allowedERC725YDataKeysCompacted[pointer + 1]
416
                    )
417
                )
418
            );
419

420
            /**
421
             * The length of a data key is 32 bytes.
422
             * Therefore we can have a fixed allowed data key which has
423
             * a length of 32 bytes or we can have a dynamic data key
424
             * which can have a length of up to 31 bytes.
425
             */
426
            if (length > 32)
2,974✔
427
                revert InvalidEncodedAllowedERC725YDataKeys(
3✔
428
                    allowedERC725YDataKeysCompacted,
429
                    "couldn't DECODE from storage"
430
                );
431

432
            /**
433
             * The bitmask discard the last `32 - length` bytes of the input data key via ANDing &
434
             * It is used to compare only the relevant parts of each input data key against dynamic allowed data keys.
435
             *
436
             * E.g.:
437
             *
438
             * allowed data key = 0xa00000
439
             *
440
             *                compare this part
441
             *                    vvvvvv
442
             * input data key = 0xa00000cafecafecafecafecafecafecafe000000000000000000000011223344
443
             *
444
             *             &                              discard this part
445
             *                       vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
446
             *           mask = 0xffffff0000000000000000000000000000000000000000000000000000000000
447
             */
448
            mask =
2,971✔
449
                bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) <<
450
                (8 * (32 - length));
451

452
            /*
453
             * transform the allowed data key situated from `pointer + 1` until `pointer + 1 + length` to a bytes32 value.
454
             * E.g. 0xfff83a -> 0xfff83a0000000000000000000000000000000000000000000000000000000000
455
             */
456
            // solhint-disable-next-line no-inline-assembly
457
            assembly {
2,971✔
458
                // the first 32 bytes word in memory (where allowedERC725YDataKeysCompacted is stored)
459
                // correspond to the total number of bytes in `allowedERC725YDataKeysCompacted`
460
                let offset := add(add(pointer, 2), 32)
461
                let memoryAt := mload(add(allowedERC725YDataKeysCompacted, offset))
462
                // MLOAD loads 32 bytes word, so we need to keep only the `length` number of bytes that makes up the allowed data key.
463
                allowedKey := and(memoryAt, mask)
464
            }
465

466
            // voila you found the key ;)
467
            if (allowedKey == (inputDataKey & mask)) return;
2,971✔
468

469
            // move the pointer to the index of the next allowed data key
470
            unchecked {
1,224✔
471
                pointer = pointer + (length + 2);
1,224✔
472
            }
473
        }
474

475
        revert NotAllowedERC725YDataKey(controllerAddress, inputDataKey);
164✔
476
    }
477

478
    /**
479
     * @dev Verify if all the `inputDataKeys` are present in the list of `allowedERC725KeysCompacted` of the `controllerAddress`.
480
     * @param controllerAddress the address of the controller.
481
     * @param inputDataKeys the data keys to verify against the allowed ERC725Y Data Keys of the `controllerAddress`.
482
     * @param allowedERC725YDataKeysCompacted a CompactBytesArray of allowed ERC725Y Data Keys of the `controllerAddress`.
483
     * @param validatedInputKeys an array of booleans to store the result of the verification of each data keys checked.
484
     */
485
    function _verifyAllowedERC725YDataKeys(
486
        address controllerAddress,
487
        bytes32[] memory inputDataKeys,
488
        bytes memory allowedERC725YDataKeysCompacted,
489
        bool[] memory validatedInputKeys
490
    ) internal pure virtual {
491
        if (allowedERC725YDataKeysCompacted.length == 0)
970✔
492
            revert NoERC725YDataKeysAllowed(controllerAddress);
12✔
493

494
        uint256 allowedKeysFound;
958✔
495

496
        // cache the input data keys from the start
497
        uint256 inputKeysLength = inputDataKeys.length;
958✔
498

499
        /**
500
         * The pointer will always land on the length of each bytes value:
501
         *
502
         * ↓↓
503
         * 03 a00000
504
         * 05 fff83a0011
505
         * 20 aa0000000000000000000000000000000000000000000000000000000000cafe
506
         * 12 bb000000000000000000000000000000beef
507
         * 19 cc00000000000000000000000000000000000000000000deed
508
         * ↑↑
509
         *
510
         */
511
        uint256 pointer;
958✔
512

513
        // information extracted from each Allowed ERC725Y Data Key.
514
        uint256 length;
958✔
515
        bytes32 allowedKey;
958✔
516
        bytes32 mask;
958✔
517

518
        /**
519
         * iterate over each data key and update the `pointer` variable with the index where to find the length of each data key.
520
         *
521
         * 0x 03 a00000 03 fff83a 20 aa00...00cafe
522
         *    ↑↑        ↑↑        ↑↑
523
         *  first  |  second  |  third
524
         *  length |  length  |  length
525
         */
526
        while (pointer < allowedERC725YDataKeysCompacted.length) {
958✔
527
            // save the length of the allowed data key to calculate the `mask`.
528
            length = uint16(
2,795✔
529
                bytes2(
530
                    abi.encodePacked(
531
                        allowedERC725YDataKeysCompacted[pointer],
532
                        allowedERC725YDataKeysCompacted[pointer + 1]
533
                    )
534
                )
535
            );
536

537
            /**
538
             * The length of a data key is 32 bytes.
539
             * Therefore we can have a fixed allowed data key which has
540
             * a length of 32 bytes or we can have a dynamic data key
541
             * which can have a length of up to 31 bytes.
542
             */
543
            if (length > 32)
2,795!
544
                revert InvalidEncodedAllowedERC725YDataKeys(
×
545
                    allowedERC725YDataKeysCompacted,
546
                    "couldn't DECODE from storage"
547
                );
548

549
            /**
550
             * The bitmask discard the last `32 - length` bytes of the input data key via ANDing &
551
             * It is used to compare only the relevant parts of each input data key against dynamic allowed data keys.
552
             *
553
             * E.g.:
554
             *
555
             * allowed data key = 0xa00000
556
             *
557
             *                compare this part
558
             *                    vvvvvv
559
             * input data key = 0xa00000cafecafecafecafecafecafecafe000000000000000000000011223344
560
             *
561
             *             &                              discard this part
562
             *                       vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
563
             *           mask = 0xffffff0000000000000000000000000000000000000000000000000000000000
564
             */
565
            mask =
2,795✔
566
                bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) <<
567
                (8 * (32 - length));
568

569
            /*
570
             * transform the allowed data key situated from `pointer + 1` until `pointer + 1 + length` to a bytes32 value.
571
             * E.g. 0xfff83a -> 0xfff83a0000000000000000000000000000000000000000000000000000000000
572
             */
573
            // solhint-disable-next-line no-inline-assembly
574
            assembly {
2,795✔
575
                // the first 32 bytes word in memory (where allowedERC725YDataKeysCompacted is stored)
576
                // correspond to the length of allowedERC725YDataKeysCompacted (= total number of bytes)
577
                let offset := add(add(pointer, 2), 32)
578
                let memoryAt := mload(add(allowedERC725YDataKeysCompacted, offset))
579
                allowedKey := and(memoryAt, mask)
580
            }
581

582
            /**
583
             * Iterate over the `inputDataKeys` to check them against the allowed data keys.
584
             * This until we have validated them all.
585
             */
586
            for (uint256 ii; ii < inputKeysLength; ii = GasLib.uncheckedIncrement(ii)) {
2,795✔
587
                // if the input data key has been marked as allowed previously,
588
                // SKIP it and move to the next input data key.
589
                if (validatedInputKeys[ii]) continue;
11,571✔
590

591
                // CHECK if the input data key is allowed.
592
                if ((inputDataKeys[ii] & mask) == allowedKey) {
7,472✔
593
                    // if the input data key is allowed, mark it as allowed
594
                    // and increment the number of allowed keys found.
595
                    validatedInputKeys[ii] = true;
2,946✔
596
                    allowedKeysFound = GasLib.uncheckedIncrement(allowedKeysFound);
2,946✔
597

598
                    // Continue checking until all the inputKeys` have been found.
599
                    if (allowedKeysFound == inputKeysLength) return;
2,946✔
600
                }
601
            }
602

603
            // Move the pointer to the next AllowedERC725YKey
604
            unchecked {
2,191✔
605
                pointer = pointer + (length + 2);
2,191✔
606
            }
607
        }
608

609
        // if we did not find all the input data keys, search for the first not allowed data key to revert.
610
        for (uint256 jj; jj < inputKeysLength; jj = GasLib.uncheckedIncrement(jj)) {
354✔
611
            if (!validatedInputKeys[jj]) {
1,150✔
612
                revert NotAllowedERC725YDataKey(controllerAddress, inputDataKeys[jj]);
114✔
613
            }
614
        }
615
    }
616

617
    /**
618
     * @dev revert if `controller`'s `addressPermissions` doesn't contain `permissionsRequired`
619
     * @param controller the caller address
620
     * @param addressPermissions the caller's permissions BitArray
621
     * @param permissionRequired the required permission
622
     */
623
    function _requirePermissions(
624
        address controller,
625
        bytes32 addressPermissions,
626
        bytes32 permissionRequired
627
    ) internal pure virtual {
628
        if (!LSP6Utils.hasPermission(addressPermissions, permissionRequired)) {
×
629
            string memory permissionErrorString = LSP6Utils.getPermissionName(permissionRequired);
×
630
            revert NotAuthorised(controller, permissionErrorString);
×
631
        }
632
    }
633
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc