@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.
- package/LICENSE +201 -0
- package/README.md +55 -5
- package/artifacts/IAccessControlExtended.json +285 -0
- package/artifacts/ILSP8CappedBalance.json +27 -0
- package/artifacts/ILSP8CappedSupply.json +27 -0
- package/artifacts/ILSP8IdentifiableDigitalAsset.json +6 -3
- package/artifacts/ILSP8Mintable.json +62 -0
- package/artifacts/ILSP8NonTransferable.json +110 -0
- package/artifacts/ILSP8Revokable.json +75 -0
- package/artifacts/LSP8Burnable.json +7 -4
- package/artifacts/LSP8BurnableInitAbstract.json +7 -4
- package/artifacts/LSP8CappedBalanceAbstract.json +1285 -0
- package/artifacts/LSP8CappedBalanceInitAbstract.json +1293 -0
- package/artifacts/{LSP8CappedSupply.json → LSP8CappedSupplyAbstract.json} +8 -15
- package/artifacts/LSP8CappedSupplyInitAbstract.json +7 -14
- package/artifacts/LSP8CustomizableToken.json +1738 -0
- package/artifacts/LSP8CustomizableTokenInit.json +1733 -0
- package/artifacts/LSP8Enumerable.json +7 -4
- package/artifacts/LSP8EnumerableInitAbstract.json +7 -4
- package/artifacts/LSP8IdentifiableDigitalAsset.json +6 -3
- package/artifacts/LSP8IdentifiableDigitalAssetInitAbstract.json +6 -3
- package/artifacts/LSP8Mintable.json +369 -5
- package/artifacts/LSP8MintableAbstract.json +1328 -0
- package/artifacts/LSP8MintableInit.json +369 -5
- package/artifacts/LSP8MintableInitAbstract.json +1336 -0
- package/artifacts/LSP8NonTransferableAbstract.json +1367 -0
- package/artifacts/LSP8NonTransferableInitAbstract.json +1375 -0
- package/artifacts/LSP8RevokableAbstract.json +1317 -0
- package/artifacts/LSP8RevokableInitAbstract.json +1325 -0
- package/artifacts/LSP8Votes.json +7 -4
- package/artifacts/LSP8VotesInitAbstract.json +7 -4
- package/contracts/ILSP8IdentifiableDigitalAsset.sol +1 -1
- package/contracts/LSP8Constants.sol +1 -1
- package/contracts/LSP8Errors.sol +1 -1
- package/contracts/LSP8IdentifiableDigitalAsset.sol +73 -114
- package/contracts/LSP8IdentifiableDigitalAssetInitAbstract.sol +69 -116
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedAbstract.sol +378 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedConstants.sol +13 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedErrors.sol +23 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedInitAbstract.sol +390 -0
- package/contracts/extensions/AccessControlExtended/IAccessControlExtended.sol +51 -0
- package/contracts/extensions/{LSP8Burnable.sol → LSP8Burnable/LSP8Burnable.sol} +7 -6
- package/contracts/extensions/{LSP8BurnableInitAbstract.sol → LSP8Burnable/LSP8BurnableInitAbstract.sol} +7 -6
- package/contracts/extensions/LSP8CappedBalance/ILSP8CappedBalance.sol +11 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceAbstract.sol +124 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceErrors.sol +9 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceInitAbstract.sol +174 -0
- package/contracts/extensions/LSP8CappedSupply/ILSP8CappedSupply.sol +11 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyAbstract.sol +59 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyErrors.sol +6 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyInitAbstract.sol +97 -0
- package/contracts/extensions/{LSP8Enumerable.sol → LSP8Enumerable/LSP8Enumerable.sol} +2 -2
- package/contracts/extensions/{LSP8EnumerableInitAbstract.sol → LSP8Enumerable/LSP8EnumerableInitAbstract.sol} +2 -2
- package/contracts/extensions/LSP8Mintable/ILSP8Mintable.sol +27 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableAbstract.sol +105 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableErrors.sol +5 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableInitAbstract.sol +155 -0
- package/contracts/extensions/LSP8NonTransferable/ILSP8NonTransferable.sol +48 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableAbstract.sol +190 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableErrors.sol +14 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableInitAbstract.sol +246 -0
- package/contracts/extensions/LSP8Revokable/ILSP8Revokable.sol +28 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableAbstract.sol +132 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableErrors.sol +4 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableInitAbstract.sol +178 -0
- package/contracts/extensions/{LSP8Votes.sol → LSP8Votes/LSP8Votes.sol} +3 -4
- package/contracts/extensions/{LSP8VotesConstants.sol → LSP8Votes/LSP8VotesConstants.sol} +1 -1
- package/contracts/extensions/{LSP8VotesInitAbstract.sol → LSP8Votes/LSP8VotesInitAbstract.sol} +3 -3
- package/contracts/presets/LSP8CustomizableToken.sol +277 -0
- package/contracts/presets/LSP8CustomizableTokenConstants.sol +32 -0
- package/contracts/presets/LSP8CustomizableTokenInit.sol +318 -0
- package/contracts/presets/LSP8Mintable.sol +13 -28
- package/contracts/presets/LSP8MintableInit.sol +13 -6
- package/dist/abi.cjs +8233 -158
- package/dist/abi.d.cts +12004 -323
- package/dist/abi.d.mts +12004 -323
- package/dist/abi.d.ts +12004 -323
- package/dist/abi.mjs +8217 -158
- package/dist/constants.cjs +21 -0
- package/dist/constants.d.cts +12 -1
- package/dist/constants.d.mts +12 -1
- package/dist/constants.d.ts +12 -1
- package/dist/constants.mjs +16 -1
- package/package.json +38 -15
- package/contracts/extensions/LSP8CappedSupply.sol +0 -85
- package/contracts/extensions/LSP8CappedSupplyInitAbstract.sol +0 -88
- package/contracts/presets/ILSP8Mintable.sol +0 -33
- 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.
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
3
|
|
|
4
4
|
// modules
|
|
5
5
|
import {
|
|
6
6
|
LSP8IdentifiableDigitalAsset
|
|
7
|
-
} from "
|
|
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.
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
3
|
|
|
4
4
|
// modules
|
|
5
5
|
import {
|
|
6
6
|
LSP8IdentifiableDigitalAssetInitAbstract
|
|
7
|
-
} from "
|
|
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,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
|
+
}
|