-
-| Package | Version |
-| :----------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [`@lukso/lsp-smart-contracts`](https://github.com/lukso-network/lsp-smart-contracts) |
|
-| [`@lukso/lsp0-contracts`](https://github.com/lukso-network/lsp0-contracts) |
|
-| [`@lukso/lsp1-contracts`](https://github.com/lukso-network/lsp1-contracts) |
|
-| [`@lukso/lsp1delegate-contracts`](https://github.com/lukso-network/lsp1delegate-contracts) |
|
-| [`@lukso/lsp2-contracts`](https://github.com/lukso-network/lsp2-contracts) |
|
-| [`@lukso/lsp3-contracts`](https://github.com/lukso-network/lsp3-contracts) |
|
-| [`@lukso/lsp4-contracts`](https://github.com/lukso-network/lsp4-contracts) |
|
-| [`@lukso/lsp5-contracts`](https://github.com/lukso-network/lsp5-contracts) |
|
-| [`@lukso/lsp6-contracts`](https://github.com/lukso-network/lsp6-contracts) |
|
-| [`@lukso/lsp7-contracts`](https://github.com/lukso-network/lsp7-contracts) |
|
-| [`@lukso/lsp8-contracts`](https://github.com/lukso-network/lsp8-contracts) |
|
-| [`@lukso/lsp9-contracts`](https://github.com/lukso-network/lsp9-contracts) |
|
+
+
+| Package | Version |
+| :----------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [`@lukso/lsp-smart-contracts`](https://github.com/lukso-network/lsp-smart-contracts) |
|
+| [`@lukso/lsp0-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp0-contracts) |
|
+| [`@lukso/lsp1-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp1-contracts) |
|
+| [`@lukso/lsp1delegate-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp1delegate-contracts) |
|
+| [`@lukso/lsp2-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp2-contracts) |
|
+| [`@lukso/lsp3-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp3-contracts) |
|
+| [`@lukso/lsp4-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp4-contracts) |
|
+| [`@lukso/lsp5-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp5-contracts) |
|
+| [`@lukso/lsp6-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp6-contracts) |
|
+| [`@lukso/lsp7-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp7-contracts) |
|
+| [`@lukso/lsp8-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp8-contracts) |
|
+| [`@lukso/lsp9-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp9-contracts) |
|
-| Package | Version |
-| :------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [`@lukso/lsp10-contracts`](https://github.com/lukso-network/lsp10-contracts) |
|
-| [`@lukso/lsp11-contracts`](https://github.com/lukso-network/lsp11-contracts) |
|
-| [`@lukso/lsp12-contracts`](https://github.com/lukso-network/lsp12-contracts) |
|
-| [`@lukso/lsp14-contracts`](https://github.com/lukso-network/lsp14-contracts) |
|
-| [`@lukso/lsp16-contracts`](https://github.com/lukso-network/lsp16-contracts) |
|
-| [`@lukso/lsp17-contracts`](https://github.com/lukso-network/lsp17-contracts) |
|
-| [`@lukso/lsp17contractextension-contracts`](https://github.com/lukso-network/lsp17contractextension-contracts) |
|
-| [`@lukso/lsp20-contracts`](https://github.com/lukso-network/lsp20-contracts) |
|
-| [`@lukso/lsp23-contracts`](https://github.com/lukso-network/lsp23-contracts) |
|
-| [`@lukso/lsp25-contracts`](https://github.com/lukso-network/lsp25-contracts) |
|
-| [`@lukso/lsp26-contracts`](https://github.com/lukso-network/lsp26-contracts) |
|
-| [`@lukso/universalprofile-contracts`](https://github.com/lukso-network/universalprofile-contracts) |
|
+| Package | Version |
+| :------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [`@lukso/lsp10-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp10-contracts) |
|
+| [`@lukso/lsp11-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp11-contracts) | |
+| [`@lukso/lsp12-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp12-contracts) |
|
+| [`@lukso/lsp14-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp14-contracts) |
|
+| [`@lukso/lsp16-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp16-contracts) |
|
+| [`@lukso/lsp17-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp17-contracts) |
|
+| [`@lukso/lsp17contractextension-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp17contractextension-contracts) |
|
+| [`@lukso/lsp20-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp20-contracts) |
|
+| [`@lukso/lsp23-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp23-contracts) |
|
+| [`@lukso/lsp25-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp25-contracts) |
|
+| [`@lukso/lsp26-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/lsp26-contracts) | |
+| [`@lukso/universalprofile-contracts`](https://github.com/lukso-network/lsp-smart-contracts/tree/develop/packages/universalprofile-contracts) |
|
diff --git a/docs/contracts/overview/DigitalAssets.md b/docs/contracts/overview/DigitalAssets.md
index 942786b24..f37b27933 100644
--- a/docs/contracts/overview/DigitalAssets.md
+++ b/docs/contracts/overview/DigitalAssets.md
@@ -349,6 +349,27 @@ We have seen in the previous section [**how to set metadata for one or multiple
The two functions `setDataForTokenId(...)` and `setDataBatchForTokenIds(...)` emit a [`TokenIdDataChanged`](../contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#tokeniddatachanged) event. You can listen for this event in the LSP8 contract from your dApp, filtering for the `LSP4Metadata` data key to check if the metadata of a tokenId has been changed. You can do so by filtering the first parameter with the `tokenId` and the second parameter with the [bytes32 value of the `LSP4Metadata` data key](../../standards/tokens/LSP4-Digital-Asset-Metadata.md#lsp4metadata).
+## Extensions
+
+The smart contracts packages for `@lukso/lsp7-contracts` and `@lukso/lsp8-contracts` include token extensions (similarly to OpenZeppelin contracts) that enables to include functionalities for building your token through inheritance.
+
+**LSP7 Tokens extensions:**
+
+- [`LSP7Burnable.sol`](../contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md): exposes a public `burn(...)` function that allows any token holder or operator to burn any amount of tokens.
+- [`LSP7CappedSupply.sol`](../contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md): enable to specify a maximum supply on deployment / initialization, which cap the maximum amount of tokens that can be minted.
+
+**LSP8 NFTs extensions:**
+
+- [`LSP8Burnable.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md)`: exposes a public `burn(...)` function that allows any NFT holder or operator to burn a specific NFT tokenId.
+- [`LSP8CappedSupply.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md)`: enable to specify a maximum supply on deployment / initialization, which cap the maximum amount of NFT that can be minted in the collection.
+- [`LSP8Enumerable.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md)`: functionality to enumerate the list of NFTs in a collection.
+
+If your token contract uses the proxy pattern with initialize functions, use the `InitAbstract` version of these extension contracts (\_e.g: `LSP7Burnable` -> `LSP7BurnableInitAbstract`).
+
+## Custom logic for transfers
+
+The LSP7 and LSP8 implementations provide the `_beforeTokenTransfer` and `_afterTokenTransfer` function that offer the ability to specify custom logic that can run before or after the token transfer has happen (= before or after the balances in the contract state have been updated).
+
## Note on LSP7 and LSP8 implementations
`LSP7DigitalAsset.sol` and `LSP8IdentifiableDigitalAsset.sol` are `abstract` contracts that are not deployable as they are, because they do not contain any public functions by default to manage token supply (_e.g: no public `mint(...)` or `burn(...)` functions_). You can either:
diff --git a/docs/contracts/overview/KeyManager.md b/docs/contracts/overview/KeyManager.md
index af3d4486c..9c812c13c 100644
--- a/docs/contracts/overview/KeyManager.md
+++ b/docs/contracts/overview/KeyManager.md
@@ -99,6 +99,88 @@ Given the example above, the on-chain nonce is 4 and we are executing the relay
- Second relay call: nonce on-chain is 4 -> nonce used to sign was 5 = reverts ❌ with [`InvalidRelayNonce`](../contracts//LSP6KeyManager/LSP6KeyManager.md#invalidrelaynonce)
- Third relay call: nonce on-chain is 5 -> nonce used to sign was 6 = reverts ❌ with [`InvalidRelayNonce`](../contracts//LSP6KeyManager/LSP6KeyManager.md#invalidrelaynonce)
+## Implement custom permissions
+
+> **Note:** although custom permissions can be created, this might not prevent from collisions where third party applications may treat the same custom permission differently.
+
+The permission system of the Key Manager is versatile enough to allow new custom permissions to be created, for specific application use cases, aside from the default ones. This is possible thanks to `bytes32` type used for the permissions, the range being large enough to fit up to 256 permissions ([since there are 256 bits in 32 bytes](../../standards/access-control/lsp6-key-manager#address-permissions)).
+
+For instance, some data keys could be very sensitive for some specific dApp (_e.g: if on a decentralised social media you store the :up: user settings under a specific data key._) and a developer might not necessarily want to use the **Allowed ERC725Y Data Keys** for this specific data key.
+
+As mentioned in comment in the Key Manager's code, you can extend the verification logic to implement custom permissions.
+https://github.com/lukso-network/lsp-smart-contracts/blob/8f0cfb2c573c44702d3155375b2d935b043416b3/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol#L263-L275
+
+This can be done by simply overriding some of the functions in the Key Manager that check for permissions depending on the action being performed (the action being defined by the calldata sent and the function being called, like `setData(...)`, `execute(...)`, etc...). Below is an example of how to create a Key Manager that controls a token contract and requires a specific permission to update the `LSP4Metadata`.
+
+```solidity
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.5;
+
+// interfaces
+import {ILSP6KeyManager} from "@lukso/lsp6-contracts/contracts/ILSP6KeyManager.sol";
+
+// modules
+import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
+import {LSP6SetDataModule} from "@lukso/lsp6-contracts/contracts/LSP6SetDataModule.sol";
+import {LSP6OwnershipModule} from "@lukso/lsp6-contracts/contracts/LSP6OwnershipModule.sol";
+
+/// @title LSP6 Key Manager implementation to enable multiple owners with different permissions and roles
+/// to control an LSP7 or LSP8 Token (instead of having a single `owner()`).
+contract LSP6TokenManager is
+ ILSP6KeyManager,
+ ERC165,
+ LSP6SetDataModule,
+ LSP6OwnershipModule
+{
+ using Address for *;
+ using ECDSA for *;
+ using LSP6Utils for *;
+
+ /// @dev address of the LSP7/8 Token contract this Key Manager controls
+ address private immutable _linkedToken;
+
+ mapping(address => mapping(uint256 => uint256)) internal _nonceStore;
+
+ constructor(address linkedToken_) {
+ if (linkedToken_ == address(0)) revert InvalidLSP6Target();
+ _linkedToken = linkedToken_;
+ }
+
+ /// @dev permission required to update the `LSP4Metadata` data key via `setData(...)` on the token contract
+ bytes32 constant _PERMISSION_UPDATE_TOKEN_METADATA = 0x0000000000000000000000000000000000000000000000000000000000400000;
+
+ /// @dev keccak256('LSP4Metadata') --> from the LSP4 Standard
+ /// This is defined in `LSP4Constants.sol`, but we write it here for better understanding.
+ bytes32 constant _LSP4_METADATA_KEY = 0x9afb95cacc9f95858ec44aa8c3b685511002e30ae54415823f406128b85b238e;
+
+ /// @inheritdoc LSP6SetDataModule
+ /// @dev implement a custom check to verify if the controller
+ /// has the permission to update the token metadata
+ function _getPermissionRequiredToSetDataKey(
+ address controlledContract,
+ bytes32 controllerPermissions,
+ bytes32 inputDataKey,
+ bytes memory inputDataValue
+ ) internal view virtual override returns (bytes32) {
+ if (inputDataKey == _LSP4_METADATA_KEY) {
+ controllerPermissions.hasPermission(
+ _PERMISSION_UPDATE_TOKEN_METADATA
+ );
+ }
+
+ super._getPermissionRequiredToSetDataKey(
+ controlledContract,
+ controllerPermissions,
+ inputDataKey,
+ inputDataValue
+ );
+ }
+
+}
+```
+
+As you can see from the Solidity code snippet above, since the Key Manager is broken down in multiple modules for each set of permissions related to specific type of actions (`LSP6SetDataModule`, `LSP6OwnershipModule`), it is relatively easy to create a specific implementation by-reusing the same code and implement custom permissions check on top based on the examples above.
+
## Further Reading
- [The Bytecode episode #4 (Youtube) - overview of the Solidity code of the `LSP6KeyManagerCore.sol` by Jean Cavallera](https://www.youtube.com/watch?v=2Sm9LsCPjdE)