@lukso/lsp8-contracts 0.16.5 → 0.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -5
  3. package/artifacts/IAccessControlExtended.json +285 -0
  4. package/artifacts/ILSP8CappedBalance.json +27 -0
  5. package/artifacts/ILSP8CappedSupply.json +27 -0
  6. package/artifacts/ILSP8IdentifiableDigitalAsset.json +6 -3
  7. package/artifacts/ILSP8Mintable.json +62 -0
  8. package/artifacts/ILSP8NonTransferable.json +110 -0
  9. package/artifacts/ILSP8Revokable.json +75 -0
  10. package/artifacts/LSP8Burnable.json +7 -4
  11. package/artifacts/LSP8BurnableInitAbstract.json +7 -4
  12. package/artifacts/LSP8CappedBalanceAbstract.json +1285 -0
  13. package/artifacts/LSP8CappedBalanceInitAbstract.json +1293 -0
  14. package/artifacts/{LSP8CappedSupply.json → LSP8CappedSupplyAbstract.json} +8 -15
  15. package/artifacts/LSP8CappedSupplyInitAbstract.json +7 -14
  16. package/artifacts/LSP8CustomizableToken.json +1738 -0
  17. package/artifacts/LSP8CustomizableTokenInit.json +1733 -0
  18. package/artifacts/LSP8Enumerable.json +7 -4
  19. package/artifacts/LSP8EnumerableInitAbstract.json +7 -4
  20. package/artifacts/LSP8IdentifiableDigitalAsset.json +6 -3
  21. package/artifacts/LSP8IdentifiableDigitalAssetInitAbstract.json +6 -3
  22. package/artifacts/LSP8Mintable.json +369 -5
  23. package/artifacts/LSP8MintableAbstract.json +1328 -0
  24. package/artifacts/LSP8MintableInit.json +369 -5
  25. package/artifacts/LSP8MintableInitAbstract.json +1336 -0
  26. package/artifacts/LSP8NonTransferableAbstract.json +1367 -0
  27. package/artifacts/LSP8NonTransferableInitAbstract.json +1375 -0
  28. package/artifacts/LSP8RevokableAbstract.json +1317 -0
  29. package/artifacts/LSP8RevokableInitAbstract.json +1325 -0
  30. package/artifacts/LSP8Votes.json +7 -4
  31. package/artifacts/LSP8VotesInitAbstract.json +7 -4
  32. package/contracts/ILSP8IdentifiableDigitalAsset.sol +1 -1
  33. package/contracts/LSP8Constants.sol +1 -1
  34. package/contracts/LSP8Errors.sol +1 -1
  35. package/contracts/LSP8IdentifiableDigitalAsset.sol +73 -114
  36. package/contracts/LSP8IdentifiableDigitalAssetInitAbstract.sol +69 -116
  37. package/contracts/extensions/AccessControlExtended/AccessControlExtendedAbstract.sol +378 -0
  38. package/contracts/extensions/AccessControlExtended/AccessControlExtendedConstants.sol +13 -0
  39. package/contracts/extensions/AccessControlExtended/AccessControlExtendedErrors.sol +23 -0
  40. package/contracts/extensions/AccessControlExtended/AccessControlExtendedInitAbstract.sol +390 -0
  41. package/contracts/extensions/AccessControlExtended/IAccessControlExtended.sol +51 -0
  42. package/contracts/extensions/{LSP8Burnable.sol → LSP8Burnable/LSP8Burnable.sol} +7 -6
  43. package/contracts/extensions/{LSP8BurnableInitAbstract.sol → LSP8Burnable/LSP8BurnableInitAbstract.sol} +7 -6
  44. package/contracts/extensions/LSP8CappedBalance/ILSP8CappedBalance.sol +11 -0
  45. package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceAbstract.sol +124 -0
  46. package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceErrors.sol +9 -0
  47. package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceInitAbstract.sol +174 -0
  48. package/contracts/extensions/LSP8CappedSupply/ILSP8CappedSupply.sol +11 -0
  49. package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyAbstract.sol +59 -0
  50. package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyErrors.sol +6 -0
  51. package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyInitAbstract.sol +97 -0
  52. package/contracts/extensions/{LSP8Enumerable.sol → LSP8Enumerable/LSP8Enumerable.sol} +2 -2
  53. package/contracts/extensions/{LSP8EnumerableInitAbstract.sol → LSP8Enumerable/LSP8EnumerableInitAbstract.sol} +2 -2
  54. package/contracts/extensions/LSP8Mintable/ILSP8Mintable.sol +27 -0
  55. package/contracts/extensions/LSP8Mintable/LSP8MintableAbstract.sol +105 -0
  56. package/contracts/extensions/LSP8Mintable/LSP8MintableErrors.sol +5 -0
  57. package/contracts/extensions/LSP8Mintable/LSP8MintableInitAbstract.sol +155 -0
  58. package/contracts/extensions/LSP8NonTransferable/ILSP8NonTransferable.sol +48 -0
  59. package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableAbstract.sol +190 -0
  60. package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableErrors.sol +14 -0
  61. package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableInitAbstract.sol +246 -0
  62. package/contracts/extensions/LSP8Revokable/ILSP8Revokable.sol +28 -0
  63. package/contracts/extensions/LSP8Revokable/LSP8RevokableAbstract.sol +132 -0
  64. package/contracts/extensions/LSP8Revokable/LSP8RevokableErrors.sol +4 -0
  65. package/contracts/extensions/LSP8Revokable/LSP8RevokableInitAbstract.sol +178 -0
  66. package/contracts/extensions/{LSP8Votes.sol → LSP8Votes/LSP8Votes.sol} +3 -4
  67. package/contracts/extensions/{LSP8VotesConstants.sol → LSP8Votes/LSP8VotesConstants.sol} +1 -1
  68. package/contracts/extensions/{LSP8VotesInitAbstract.sol → LSP8Votes/LSP8VotesInitAbstract.sol} +3 -3
  69. package/contracts/presets/LSP8CustomizableToken.sol +277 -0
  70. package/contracts/presets/LSP8CustomizableTokenConstants.sol +32 -0
  71. package/contracts/presets/LSP8CustomizableTokenInit.sol +318 -0
  72. package/contracts/presets/LSP8Mintable.sol +13 -28
  73. package/contracts/presets/LSP8MintableInit.sol +13 -6
  74. package/dist/abi.cjs +8233 -158
  75. package/dist/abi.d.cts +12004 -323
  76. package/dist/abi.d.mts +12004 -323
  77. package/dist/abi.d.ts +12004 -323
  78. package/dist/abi.mjs +8217 -158
  79. package/dist/constants.cjs +21 -0
  80. package/dist/constants.d.cts +12 -1
  81. package/dist/constants.d.mts +12 -1
  82. package/dist/constants.d.ts +12 -1
  83. package/dist/constants.mjs +16 -1
  84. package/package.json +38 -15
  85. package/contracts/extensions/LSP8CappedSupply.sol +0 -85
  86. package/contracts/extensions/LSP8CappedSupplyInitAbstract.sol +0 -88
  87. package/contracts/presets/ILSP8Mintable.sol +0 -33
  88. package/contracts/presets/LSP8MintableInitAbstract.sol +0 -62
@@ -0,0 +1,174 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ // modules
5
+ import {
6
+ OwnableUpgradeable
7
+ } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
8
+ import {
9
+ LSP8IdentifiableDigitalAssetInitAbstract
10
+ } from "../../LSP8IdentifiableDigitalAssetInitAbstract.sol";
11
+ import {
12
+ AccessControlExtendedInitAbstract
13
+ } from "../AccessControlExtended/AccessControlExtendedInitAbstract.sol";
14
+
15
+ // interfaces
16
+ import {ILSP8CappedBalance} from "./ILSP8CappedBalance.sol";
17
+
18
+ // errors
19
+ import {LSP8CappedBalanceExceeded} from "./LSP8CappedBalanceErrors.sol";
20
+
21
+ /// @title LSP8CappedBalanceInitAbstract
22
+ /// @dev Abstract contract implementing a per-address NFT count cap for LSP8 tokens, with role-based exemptions.
23
+ abstract contract LSP8CappedBalanceInitAbstract is
24
+ ILSP8CappedBalance,
25
+ LSP8IdentifiableDigitalAssetInitAbstract,
26
+ AccessControlExtendedInitAbstract
27
+ {
28
+ /// @notice The dead address is also commonly used for burning tokens as an alternative to address(0).
29
+ address internal constant _DEAD_ADDRESS =
30
+ 0x000000000000000000000000000000000000dEaD;
31
+
32
+ /// @dev keccak256("UNCAPPED_BALANCE_ROLE")
33
+ bytes32 public constant UNCAPPED_BALANCE_ROLE =
34
+ 0x975773d1e0a917a74b57f36a377f439ffff6271648aebdbff75a52ab58eb7bad;
35
+
36
+ /// @notice The maximum number of NFTs allowed per address.
37
+ uint256 private _tokenBalanceCap;
38
+
39
+ /// @notice Initializes the LSP8CappedBalance contract with base token params and balance cap.
40
+ /// @dev Initializes the LSP8IdentifiableDigitalAsset base, the access control layer and the balance cap.
41
+ /// @param name_ The name of the token.
42
+ /// @param symbol_ The symbol of the token.
43
+ /// @param newOwner_ The owner of the contract.
44
+ /// @param lsp4TokenType_ The token type (see LSP4).
45
+ /// @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
46
+ /// @param tokenBalanceCap_ The maximum number of NFTs per address, 0 to disable.
47
+ function __LSP8CappedBalance_init(
48
+ string memory name_,
49
+ string memory symbol_,
50
+ address newOwner_,
51
+ uint256 lsp4TokenType_,
52
+ uint256 lsp8TokenIdFormat_,
53
+ uint256 tokenBalanceCap_
54
+ ) internal virtual onlyInitializing {
55
+ LSP8IdentifiableDigitalAssetInitAbstract._initialize(
56
+ name_,
57
+ symbol_,
58
+ newOwner_,
59
+ lsp4TokenType_,
60
+ lsp8TokenIdFormat_
61
+ );
62
+ __AccessControlExtended_init();
63
+ __LSP8CappedBalance_init_unchained(tokenBalanceCap_);
64
+ }
65
+
66
+ /// @notice Unchained initializer for the balance cap.
67
+ /// @dev Sets the balance cap. If set, grants the initial uncapped balance role exemption to the contract owner.
68
+ /// @param tokenBalanceCap_ The maximum number of NFTs allowed per address. Set to 0 to disable.
69
+ function __LSP8CappedBalance_init_unchained(
70
+ uint256 tokenBalanceCap_
71
+ ) internal virtual onlyInitializing {
72
+ _tokenBalanceCap = tokenBalanceCap_;
73
+
74
+ if (tokenBalanceCap_ != 0) {
75
+ _grantRole(UNCAPPED_BALANCE_ROLE, owner());
76
+ }
77
+ }
78
+
79
+ /// @inheritdoc ILSP8CappedBalance
80
+ function tokenBalanceCap() public view virtual override returns (uint256) {
81
+ return _tokenBalanceCap;
82
+ }
83
+
84
+ function supportsInterface(
85
+ bytes4 interfaceId
86
+ )
87
+ public
88
+ view
89
+ virtual
90
+ override(
91
+ AccessControlExtendedInitAbstract,
92
+ LSP8IdentifiableDigitalAssetInitAbstract
93
+ )
94
+ returns (bool)
95
+ {
96
+ return
97
+ AccessControlExtendedInitAbstract.supportsInterface(interfaceId) ||
98
+ LSP8IdentifiableDigitalAssetInitAbstract.supportsInterface(
99
+ interfaceId
100
+ );
101
+ }
102
+
103
+ /// @notice Checks if a token transfer complies with the balance cap.
104
+ /// @dev The address(0) is not subject to balance cap checks as this address is used for burning tokens. Reverts with {LSP8CappedBalanceExceeded} if the recipient's NFT count after receiving the token would exceed the maximum allowed.
105
+ /// @param from The address sending the token.
106
+ /// @param to The address receiving the token.
107
+ function _tokenBalanceCapCheck(
108
+ address from,
109
+ address to,
110
+ bytes32 /* tokenId */,
111
+ bool /* force */,
112
+ bytes memory /* data */
113
+ ) internal virtual {
114
+ // self-transfers do not increase the balance of the recipient, so we skip the check
115
+ if (from == to) return;
116
+
117
+ // Address(0) and 0x0000...dead addresses are used for burning tokens
118
+ if (to == address(0) || to == _DEAD_ADDRESS) return;
119
+
120
+ // Do not check for addresses exempted from balance cap
121
+ if (hasRole(UNCAPPED_BALANCE_ROLE, to)) return;
122
+
123
+ uint256 maxBalanceAllowed = tokenBalanceCap();
124
+ bool isBalanceCapEnabled = maxBalanceAllowed != 0;
125
+
126
+ if (!isBalanceCapEnabled) return;
127
+
128
+ require(
129
+ (balanceOf(to) + 1) <= tokenBalanceCap(),
130
+ LSP8CappedBalanceExceeded(to, balanceOf(to), tokenBalanceCap())
131
+ );
132
+ }
133
+
134
+ /// @notice Hook called before a token transfer to enforce balance cap restrictions.
135
+ /// @dev Bypasses balance cap checks for recipients holding `UNCAPPED_BALANCE_ROLE`. Applies cap checks for all other recipients.
136
+ /// @param from The address sending the token.
137
+ /// @param to The address receiving the token.
138
+ /// @param tokenId The unique identifier of the token being transferred.
139
+ /// @param force Whether to force the transfer.
140
+ /// @param data Additional data for the transfer.
141
+ function _beforeTokenTransfer(
142
+ address from,
143
+ address to,
144
+ bytes32 tokenId,
145
+ bool force,
146
+ bytes memory data
147
+ ) internal virtual override {
148
+ _tokenBalanceCapCheck(from, to, tokenId, force, data);
149
+ super._beforeTokenTransfer(from, to, tokenId, force, data);
150
+ }
151
+
152
+ function _transferOwnership(
153
+ address newOwner
154
+ )
155
+ internal
156
+ virtual
157
+ override(AccessControlExtendedInitAbstract, OwnableUpgradeable)
158
+ {
159
+ // restore default admin hierarchy so a previously-installed custom admin
160
+ // cannot grant UNCAPPED_BALANCE_ROLE to new accounts post-transfer
161
+ _setRoleAdmin(UNCAPPED_BALANCE_ROLE, DEFAULT_ADMIN_ROLE);
162
+ super._transferOwnership(newOwner);
163
+ }
164
+
165
+ /**
166
+ * @dev This empty reserved space is put in place to allow future versions to add new
167
+ * variables without shifting down storage in the inheritance chain.
168
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
169
+ *
170
+ * @custom:info The size of the `__gap` array is calculated so that the amount of storage used by the contract
171
+ * always adds up to the same number (in this case 50 storage slots).
172
+ */
173
+ uint256[49] private __gap;
174
+ }
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ /// @title ILSP8CappedSupply
5
+ /// @dev Interface for an LSP8 token extension that enforces a max token supply cap.
6
+ interface ILSP8CappedSupply {
7
+ /// @notice The maximum supply amount of tokens allowed to exist is `_TOKEN_SUPPLY_CAP`.
8
+ /// @dev Get the maximum number of tokens that can exist to circulate. Once {totalSupply} reaches {totalSupplyCap}, it is not possible to mint more tokens.
9
+ /// @return The maximum number of tokens that can exist in the contract.
10
+ function tokenSupplyCap() external view returns (uint256);
11
+ }
@@ -0,0 +1,59 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ // modules
5
+ import {
6
+ LSP8IdentifiableDigitalAsset
7
+ } from "../../LSP8IdentifiableDigitalAsset.sol";
8
+
9
+ // interfaces
10
+ import {ILSP8CappedSupply} from "./ILSP8CappedSupply.sol";
11
+
12
+ // errors
13
+ import {LSP8CappedSupplyCannotMintOverCap} from "./LSP8CappedSupplyErrors.sol";
14
+
15
+ /// @title LSP8CappedSupplyAbstract
16
+ /// @dev Abstract contract implementing a token supply cap for LSP8 tokens.
17
+ abstract contract LSP8CappedSupplyAbstract is
18
+ ILSP8CappedSupply,
19
+ LSP8IdentifiableDigitalAsset
20
+ {
21
+ /// @notice The immutable maximum token supply.
22
+ uint256 private immutable _TOKEN_SUPPLY_CAP;
23
+
24
+ /// @notice Deploying a `LSP8CappedSupply` token contract with max token supply cap set to `tokenSupplyCap_`.
25
+ /// @dev Deploy a `LSP8CappedSupply` token contract and set the maximum token supply token cap up to which it is not possible to mint more tokens.
26
+ /// @param tokenSupplyCap_ The maximum total supply, 0 to disable.
27
+ constructor(uint256 tokenSupplyCap_) {
28
+ _TOKEN_SUPPLY_CAP = tokenSupplyCap_;
29
+ }
30
+
31
+ /// @inheritdoc ILSP8CappedSupply
32
+ function tokenSupplyCap() public view virtual override returns (uint256) {
33
+ return _TOKEN_SUPPLY_CAP;
34
+ }
35
+
36
+ /// @dev Checks if minting a new token would exceed the token supply cap.
37
+ function _tokenSupplyCapCheck(
38
+ address /* to */,
39
+ bytes32 /* tokenId */,
40
+ bool /* force */,
41
+ bytes memory /* data */
42
+ ) internal virtual {
43
+ require(
44
+ tokenSupplyCap() == 0 || (totalSupply() + 1) <= tokenSupplyCap(),
45
+ LSP8CappedSupplyCannotMintOverCap()
46
+ );
47
+ }
48
+
49
+ /// @dev Same as {_mint} but allows to mint only if the {totalSupply} does not exceed the {tokenSupplyCap} after the token has been minted.
50
+ function _mint(
51
+ address to,
52
+ bytes32 tokenId,
53
+ bool force,
54
+ bytes memory data
55
+ ) internal virtual override {
56
+ _tokenSupplyCapCheck(to, tokenId, force, data);
57
+ super._mint(to, tokenId, force, data);
58
+ }
59
+ }
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ /// @notice Cannot mint anymore as total supply reached the maximum cap.
5
+ /// @dev Reverts when trying to mint tokens but the {totalSupply} has reached the maximum {tokenSupplyCap}.
6
+ error LSP8CappedSupplyCannotMintOverCap();
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ // modules
5
+ import {
6
+ LSP8IdentifiableDigitalAssetInitAbstract
7
+ } from "../../LSP8IdentifiableDigitalAssetInitAbstract.sol";
8
+
9
+ // interfaces
10
+ import {ILSP8CappedSupply} from "./ILSP8CappedSupply.sol";
11
+
12
+ // errors
13
+ import {LSP8CappedSupplyCannotMintOverCap} from "./LSP8CappedSupplyErrors.sol";
14
+
15
+ /// @title LSP8CappedSupplyInitAbstract
16
+ /// @dev Abstract contract implementing a token supply cap for LSP8 tokens.
17
+ abstract contract LSP8CappedSupplyInitAbstract is
18
+ ILSP8CappedSupply,
19
+ LSP8IdentifiableDigitalAssetInitAbstract
20
+ {
21
+ /// @notice The maximum token supply.
22
+ uint256 private _tokenSupplyCap;
23
+
24
+ /// @notice Initializes the LSP8CappedSupply contract with base token params and supply cap.
25
+ /// @dev Initializes the LSP8IdentifiableDigitalAsset base and sets the maximum token supply cap.
26
+ /// @param name_ The name of the token.
27
+ /// @param symbol_ The symbol of the token.
28
+ /// @param newOwner_ The owner of the contract.
29
+ /// @param lsp4TokenType_ The token type (see LSP4).
30
+ /// @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
31
+ /// @param tokenSupplyCap_ The maximum total supply, 0 to disable.
32
+ function __LSP8CappedSupply_init(
33
+ string memory name_,
34
+ string memory symbol_,
35
+ address newOwner_,
36
+ uint256 lsp4TokenType_,
37
+ uint256 lsp8TokenIdFormat_,
38
+ uint256 tokenSupplyCap_
39
+ ) internal virtual onlyInitializing {
40
+ LSP8IdentifiableDigitalAssetInitAbstract._initialize(
41
+ name_,
42
+ symbol_,
43
+ newOwner_,
44
+ lsp4TokenType_,
45
+ lsp8TokenIdFormat_
46
+ );
47
+ __LSP8CappedSupply_init_unchained(tokenSupplyCap_);
48
+ }
49
+
50
+ /// @notice Unchained initializer for the token supply cap.
51
+ /// @dev Sets the maximum token supply cap.
52
+ /// @param tokenSupplyCap_ The maximum total supply, 0 to disable.
53
+ function __LSP8CappedSupply_init_unchained(
54
+ uint256 tokenSupplyCap_
55
+ ) internal virtual onlyInitializing {
56
+ _tokenSupplyCap = tokenSupplyCap_;
57
+ }
58
+
59
+ /// @inheritdoc ILSP8CappedSupply
60
+ function tokenSupplyCap() public view virtual override returns (uint256) {
61
+ return _tokenSupplyCap;
62
+ }
63
+
64
+ /// @dev Checks if minting a new token would exceed the token supply cap.
65
+ function _tokenSupplyCapCheck(
66
+ address /* to */,
67
+ bytes32 /* tokenId */,
68
+ bool /* force */,
69
+ bytes memory /* data */
70
+ ) internal virtual {
71
+ require(
72
+ tokenSupplyCap() == 0 || (totalSupply() + 1) <= tokenSupplyCap(),
73
+ LSP8CappedSupplyCannotMintOverCap()
74
+ );
75
+ }
76
+
77
+ /// @dev Same as {_mint} but allows to mint only if the {totalSupply} does not exceed the {tokenSupplyCap} after the token has been minted.
78
+ function _mint(
79
+ address to,
80
+ bytes32 tokenId,
81
+ bool force,
82
+ bytes memory data
83
+ ) internal virtual override {
84
+ _tokenSupplyCapCheck(to, tokenId, force, data);
85
+ super._mint(to, tokenId, force, data);
86
+ }
87
+
88
+ /**
89
+ * @dev This empty reserved space is put in place to allow future versions to add new
90
+ * variables without shifting down storage in the inheritance chain.
91
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
92
+ *
93
+ * @custom:info The size of the `__gap` array is calculated so that the amount of storage used by the contract
94
+ * always adds up to the same number (in this case 50 storage slots).
95
+ */
96
+ uint256[49] private __gap;
97
+ }
@@ -1,10 +1,10 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
- pragma solidity ^0.8.12;
2
+ pragma solidity ^0.8.27;
3
3
 
4
4
  // modules
5
5
  import {
6
6
  LSP8IdentifiableDigitalAsset
7
- } from "../LSP8IdentifiableDigitalAsset.sol";
7
+ } from "../../LSP8IdentifiableDigitalAsset.sol";
8
8
 
9
9
  /**
10
10
  * @dev LSP8 extension that enables to enumerate over all the `tokenIds` ever minted.
@@ -1,10 +1,10 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
- pragma solidity ^0.8.12;
2
+ pragma solidity ^0.8.27;
3
3
 
4
4
  // modules
5
5
  import {
6
6
  LSP8IdentifiableDigitalAssetInitAbstract
7
- } from "../LSP8IdentifiableDigitalAssetInitAbstract.sol";
7
+ } from "../../LSP8IdentifiableDigitalAssetInitAbstract.sol";
8
8
 
9
9
  /**
10
10
  * @dev LSP8 extension.
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ /// @title ILSP8Mintable
5
+ /// @dev Interface for a mintable LSP8 token extension, allowing the owner to mint new tokens and disable minting.
6
+ interface ILSP8Mintable {
7
+ /// @dev Emitted when minting status is changed.
8
+ event MintingStatusChanged(bool indexed enabled);
9
+
10
+ /// @notice Disables minting of new tokens permanently.
11
+ /// @dev Can only be called by the contract owner. Prevents further calls to mint after invocation.
12
+ /// @custom:events {MintingDisabled} event.
13
+ function disableMinting() external;
14
+
15
+ /// @notice Mints a new token with the specified tokenId to a given address.
16
+ /// @dev Mints a token with `tokenId` to `to`, callable only by the contract owner. Emits a Transfer event as defined in ILSP8IdentifiableDigitalAsset. Reverts if `to` is the zero address, if minting is disabled, or if the tokenId already exists.
17
+ /// @param to The address to receive the minted token.
18
+ /// @param tokenId The unique identifier for the token to mint.
19
+ /// @param force When true, allows minting to any address; when false, requires `to` to support LSP1 UniversalReceiver.
20
+ /// @param data Additional data included in the Transfer event and sent to `to`'s UniversalReceiver hook, if applicable.
21
+ function mint(
22
+ address to,
23
+ bytes32 tokenId,
24
+ bool force,
25
+ bytes memory data
26
+ ) external;
27
+ }
@@ -0,0 +1,105 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ // modules
5
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
6
+ import {
7
+ LSP8IdentifiableDigitalAsset
8
+ } from "../../LSP8IdentifiableDigitalAsset.sol";
9
+ import {
10
+ AccessControlExtendedAbstract
11
+ } from "../AccessControlExtended/AccessControlExtendedAbstract.sol";
12
+
13
+ // interfaces
14
+ import {ILSP8Mintable} from "./ILSP8Mintable.sol";
15
+
16
+ // errors
17
+ import {LSP8MintDisabled} from "./LSP8MintableErrors.sol";
18
+
19
+ /// @title LSP8MintableAbstract
20
+ /// @dev Abstract contract implementing a mintable LSP8 token extension, allowing the owner to mint any address granted the `MINTER_ROLE` to mint new tokens until minting is disabled.
21
+ /// Inherits from LSP8IdentifiableDigitalAsset to provide core token functionality.
22
+ abstract contract LSP8MintableAbstract is
23
+ ILSP8Mintable,
24
+ LSP8IdentifiableDigitalAsset,
25
+ AccessControlExtendedAbstract
26
+ {
27
+ /// @notice Indicates whether minting is currently enabled.
28
+ bool public isMintable;
29
+
30
+ /// @dev keccak256("MINTER_ROLE")
31
+ bytes32 public constant MINTER_ROLE =
32
+ 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6;
33
+
34
+ /// @notice Initializes the contract with the minting status.
35
+ /// @dev Sets the initial minting status. Inherits LSP8IdentifiableDigitalAsset constructor logic.
36
+ /// @param mintable_ True to enable minting after deployment, false to disable it forever.
37
+ /// @custom:info If `mintable_` is set to `true` then it can be disabled using `disableMinting()` function later on.
38
+ constructor(bool mintable_) {
39
+ isMintable = mintable_;
40
+ emit MintingStatusChanged({enabled: mintable_});
41
+
42
+ if (mintable_) {
43
+ _grantRole(MINTER_ROLE, owner());
44
+ }
45
+ }
46
+
47
+ /// @inheritdoc ILSP8Mintable
48
+ /// @custom:warning Once this function is called, any address holding the `MINTER_ROLE` will be inoperable.
49
+ /// @custom:info The list of addresses holding the `MINTER_ROLE` remains populated after the minting feature is switched off.
50
+ function disableMinting() public virtual override onlyOwner {
51
+ require(isMintable, LSP8MintDisabled());
52
+ isMintable = false;
53
+ emit MintingStatusChanged({enabled: false});
54
+ }
55
+
56
+ /// @inheritdoc ILSP8Mintable
57
+ function mint(
58
+ address to,
59
+ bytes32 tokenId,
60
+ bool force,
61
+ bytes memory data
62
+ ) public virtual override onlyRole(MINTER_ROLE) {
63
+ _mint(to, tokenId, force, data);
64
+ }
65
+
66
+ function supportsInterface(
67
+ bytes4 interfaceId
68
+ )
69
+ public
70
+ view
71
+ virtual
72
+ override(AccessControlExtendedAbstract, LSP8IdentifiableDigitalAsset)
73
+ returns (bool)
74
+ {
75
+ return
76
+ AccessControlExtendedAbstract.supportsInterface(interfaceId) ||
77
+ LSP8IdentifiableDigitalAsset.supportsInterface(interfaceId);
78
+ }
79
+
80
+ /// @notice Internal function to mint tokens, overridden to enforce minting status.
81
+ /// @dev Checks if minting is enabled, reverting with LSP8MintDisabled if not. Calls the parent _mint function from LSP8IdentifiableDigitalAsset.
82
+ /// @param to The address to receive the minted token.
83
+ /// @param tokenId The unique identifier for the token to mint.
84
+ /// @param force When true, allows minting to any address; when false, requires `to` to support LSP1 UniversalReceiver.
85
+ /// @param data Additional data included in the Transfer event and sent to `to`'s UniversalReceiver hook, if applicable.
86
+ function _mint(
87
+ address to,
88
+ bytes32 tokenId,
89
+ bool force,
90
+ bytes memory data
91
+ ) internal virtual override {
92
+ require(isMintable, LSP8MintDisabled());
93
+
94
+ super._mint(to, tokenId, force, data);
95
+ }
96
+
97
+ function _transferOwnership(
98
+ address newOwner
99
+ ) internal virtual override(AccessControlExtendedAbstract, Ownable) {
100
+ // restore default admin hierarchy so a previously-installed custom admin
101
+ // cannot grant MINTER_ROLE to new accounts post-transfer
102
+ _setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
103
+ super._transferOwnership(newOwner);
104
+ }
105
+ }
@@ -0,0 +1,5 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ /// @dev Error thrown when attempting to mint tokens after minting has been disabled.
5
+ error LSP8MintDisabled();
@@ -0,0 +1,155 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.27;
3
+
4
+ // modules
5
+ import {
6
+ OwnableUpgradeable
7
+ } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
8
+ import {
9
+ LSP8IdentifiableDigitalAssetInitAbstract
10
+ } from "../../LSP8IdentifiableDigitalAssetInitAbstract.sol";
11
+ import {
12
+ AccessControlExtendedInitAbstract
13
+ } from "../AccessControlExtended/AccessControlExtendedInitAbstract.sol";
14
+
15
+ // interfaces
16
+ import {ILSP8Mintable} from "./ILSP8Mintable.sol";
17
+
18
+ // errors
19
+ import {LSP8MintDisabled} from "./LSP8MintableErrors.sol";
20
+
21
+ /// @title LSP8MintableInitAbstract
22
+ /// @dev Abstract contract implementing a mintable LSP8 token extension, allowing the owner to mint any address granted the `MINTER_ROLE` to mint new tokens until minting is disabled.
23
+ /// Inherits from LSP8IdentifiableDigitalAssetInitAbstract to provide core token functionality.
24
+ abstract contract LSP8MintableInitAbstract is
25
+ ILSP8Mintable,
26
+ LSP8IdentifiableDigitalAssetInitAbstract,
27
+ AccessControlExtendedInitAbstract
28
+ {
29
+ /// @notice Indicates whether minting is currently enabled.
30
+ bool public isMintable;
31
+
32
+ /// @dev keccak256("MINTER_ROLE")
33
+ bytes32 public constant MINTER_ROLE =
34
+ 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6;
35
+
36
+ /// @notice Initializes the LSP8Mintable contract with base token params and minting status.
37
+ /// @dev Initializes the LSP8IdentifiableDigitalAsset base and sets the initial minting status.
38
+ /// @param name_ The name of the token.
39
+ /// @param symbol_ The symbol of the token.
40
+ /// @param newOwner_ The owner of the contract.
41
+ /// @param lsp4TokenType_ The token type (see LSP4).
42
+ /// @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
43
+ /// @param mintable_ True to enable minting after deployment, false to disable it forever.
44
+ /// @custom:info If `mintable_` is set to `true` then it can be disabled using `disableMinting()` function later on.
45
+ function __LSP8Mintable_init(
46
+ string memory name_,
47
+ string memory symbol_,
48
+ address newOwner_,
49
+ uint256 lsp4TokenType_,
50
+ uint256 lsp8TokenIdFormat_,
51
+ bool mintable_
52
+ ) internal virtual onlyInitializing {
53
+ LSP8IdentifiableDigitalAssetInitAbstract._initialize(
54
+ name_,
55
+ symbol_,
56
+ newOwner_,
57
+ lsp4TokenType_,
58
+ lsp8TokenIdFormat_
59
+ );
60
+ __AccessControlExtended_init();
61
+ __LSP8Mintable_init_unchained(mintable_);
62
+ }
63
+
64
+ /// @notice Unchained initializer for the minting status.
65
+ /// @dev Sets the initial minting status.
66
+ /// @param mintable_ True to enable minting after deployment, false to disable it forever.
67
+ function __LSP8Mintable_init_unchained(
68
+ bool mintable_
69
+ ) internal virtual onlyInitializing {
70
+ isMintable = mintable_;
71
+ emit MintingStatusChanged({enabled: mintable_});
72
+
73
+ if (mintable_) {
74
+ _grantRole(MINTER_ROLE, owner());
75
+ }
76
+ }
77
+
78
+ /// @inheritdoc ILSP8Mintable
79
+ /// @custom:warning Once this function is called, any address holding the `MINTER_ROLE` will be inoperable.
80
+ /// @custom:info The list of addresses holding the `MINTER_ROLE` remains populated after the minting feature is switched off.
81
+ function disableMinting() public virtual override onlyOwner {
82
+ require(isMintable, LSP8MintDisabled());
83
+ isMintable = false;
84
+ emit MintingStatusChanged({enabled: false});
85
+ }
86
+
87
+ /// @inheritdoc ILSP8Mintable
88
+ function mint(
89
+ address to,
90
+ bytes32 tokenId,
91
+ bool force,
92
+ bytes memory data
93
+ ) public virtual override onlyRole(MINTER_ROLE) {
94
+ _mint(to, tokenId, force, data);
95
+ }
96
+
97
+ function supportsInterface(
98
+ bytes4 interfaceId
99
+ )
100
+ public
101
+ view
102
+ virtual
103
+ override(
104
+ AccessControlExtendedInitAbstract,
105
+ LSP8IdentifiableDigitalAssetInitAbstract
106
+ )
107
+ returns (bool)
108
+ {
109
+ return
110
+ AccessControlExtendedInitAbstract.supportsInterface(interfaceId) ||
111
+ LSP8IdentifiableDigitalAssetInitAbstract.supportsInterface(
112
+ interfaceId
113
+ );
114
+ }
115
+
116
+ /// @notice Internal function to mint tokens, overridden to enforce minting status.
117
+ /// @dev Checks if minting is enabled, reverting with LSP8MintDisabled if not. Calls the parent _mint function from LSP8IdentifiableDigitalAssetInitAbstract.
118
+ /// @param to The address to receive the minted token.
119
+ /// @param tokenId The unique identifier for the token to mint.
120
+ /// @param force When true, allows minting to any address; when false, requires `to` to support LSP1 UniversalReceiver.
121
+ /// @param data Additional data included in the Transfer event and sent to `to`'s UniversalReceiver hook, if applicable.
122
+ function _mint(
123
+ address to,
124
+ bytes32 tokenId,
125
+ bool force,
126
+ bytes memory data
127
+ ) internal virtual override {
128
+ require(isMintable, LSP8MintDisabled());
129
+
130
+ super._mint(to, tokenId, force, data);
131
+ }
132
+
133
+ function _transferOwnership(
134
+ address newOwner
135
+ )
136
+ internal
137
+ virtual
138
+ override(AccessControlExtendedInitAbstract, OwnableUpgradeable)
139
+ {
140
+ // restore default admin hierarchy so a previously-installed custom admin
141
+ // cannot grant MINTER_ROLE to new accounts post-transfer
142
+ _setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
143
+ super._transferOwnership(newOwner);
144
+ }
145
+
146
+ /**
147
+ * @dev This empty reserved space is put in place to allow future versions to add new
148
+ * variables without shifting down storage in the inheritance chain.
149
+ * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
150
+ *
151
+ * @custom:info The size of the `__gap` array is calculated so that the amount of storage used by the contract
152
+ * always adds up to the same number (in this case 50 storage slots).
153
+ */
154
+ uint256[49] private __gap;
155
+ }