@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,178 @@
|
|
|
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 {ILSP8Revokable} from "./ILSP8Revokable.sol";
|
|
17
|
+
|
|
18
|
+
// errors
|
|
19
|
+
import {
|
|
20
|
+
AccessControlUnauthorizedAccount
|
|
21
|
+
} from "../AccessControlExtended/AccessControlExtendedErrors.sol";
|
|
22
|
+
import {LSP8RevokableFeatureDisabled} from "./LSP8RevokableErrors.sol";
|
|
23
|
+
|
|
24
|
+
/// @title LSP8RevokableInitAbstract
|
|
25
|
+
/// @dev Abstract contract implementing revokable functionality for LSP8 tokens (initializer version).
|
|
26
|
+
/// Allows addresses with the `REVOKER_ROLE` to revoke NFTs from any holder
|
|
27
|
+
/// back to the contract owner or any other address that also has revoke rights.
|
|
28
|
+
///
|
|
29
|
+
/// This version is for proxy deployments using the initializer pattern.
|
|
30
|
+
///
|
|
31
|
+
/// Use cases include:
|
|
32
|
+
/// - Memberships: Revoke membership NFTs when they expire or are terminated
|
|
33
|
+
/// - Role badges: Remove role badge NFTs from community members
|
|
34
|
+
/// - Compliance: Freeze or reverse NFTs for regulatory requirements
|
|
35
|
+
/// - Ticketing: Reclaim tickets or access NFTs when conditions are no longer met
|
|
36
|
+
abstract contract LSP8RevokableInitAbstract is
|
|
37
|
+
ILSP8Revokable,
|
|
38
|
+
LSP8IdentifiableDigitalAssetInitAbstract,
|
|
39
|
+
AccessControlExtendedInitAbstract
|
|
40
|
+
{
|
|
41
|
+
bool internal _isRevokable;
|
|
42
|
+
|
|
43
|
+
/// @dev keccak256("REVOKER_ROLE")
|
|
44
|
+
bytes32 public constant REVOKER_ROLE =
|
|
45
|
+
0xce3f34913921da558f105cefb578d87278debbbd073a8d552b5de0d168deee30;
|
|
46
|
+
|
|
47
|
+
/// @notice Initializes the LSP8Revokable contract with base token params.
|
|
48
|
+
/// @dev Initializes the LSP8IdentifiableDigitalAsset base contract.
|
|
49
|
+
/// @param name_ The name of the token.
|
|
50
|
+
/// @param symbol_ The symbol of the token.
|
|
51
|
+
/// @param newOwner_ The owner of the contract (implicitly a revoker).
|
|
52
|
+
/// @param lsp4TokenType_ The token type (see LSP4).
|
|
53
|
+
/// @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
|
|
54
|
+
/// @param isRevokable_ Whether token revocation is enabled.
|
|
55
|
+
function __LSP8Revokable_init(
|
|
56
|
+
string memory name_,
|
|
57
|
+
string memory symbol_,
|
|
58
|
+
address newOwner_,
|
|
59
|
+
uint256 lsp4TokenType_,
|
|
60
|
+
uint256 lsp8TokenIdFormat_,
|
|
61
|
+
bool isRevokable_
|
|
62
|
+
) internal virtual onlyInitializing {
|
|
63
|
+
LSP8IdentifiableDigitalAssetInitAbstract._initialize(
|
|
64
|
+
name_,
|
|
65
|
+
symbol_,
|
|
66
|
+
newOwner_,
|
|
67
|
+
lsp4TokenType_,
|
|
68
|
+
lsp8TokenIdFormat_
|
|
69
|
+
);
|
|
70
|
+
__AccessControlExtended_init();
|
|
71
|
+
__LSP8Revokable_init_unchained(isRevokable_);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// @notice Unchained initializer for LSP8Revokable.
|
|
75
|
+
function __LSP8Revokable_init_unchained(
|
|
76
|
+
bool isRevokable_
|
|
77
|
+
) internal virtual onlyInitializing {
|
|
78
|
+
_isRevokable = isRevokable_;
|
|
79
|
+
|
|
80
|
+
if (isRevokable_) {
|
|
81
|
+
_grantRole(REVOKER_ROLE, owner());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @inheritdoc ILSP8Revokable
|
|
86
|
+
function isRevokable() public view virtual override returns (bool) {
|
|
87
|
+
return _isRevokable;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// @inheritdoc ILSP8Revokable
|
|
91
|
+
/// @custom:warning Once this function is called, any address holding the `REVOKER_ROLE` will be inoperable.
|
|
92
|
+
/// @custom:info The list of addresses holding the `REVOKER_ROLE` remains populated after the revokable feature is switched off.
|
|
93
|
+
function disableRevokable() public virtual override onlyOwner {
|
|
94
|
+
require(isRevokable(), LSP8RevokableFeatureDisabled());
|
|
95
|
+
_isRevokable = false;
|
|
96
|
+
emit RevokableStatusChanged({enabled: false});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @inheritdoc ILSP8Revokable
|
|
100
|
+
function revoke(
|
|
101
|
+
address from,
|
|
102
|
+
address to,
|
|
103
|
+
bytes32 tokenId,
|
|
104
|
+
bytes memory data
|
|
105
|
+
) public virtual override onlyRole(REVOKER_ROLE) {
|
|
106
|
+
require(isRevokable(), LSP8RevokableFeatureDisabled());
|
|
107
|
+
require(
|
|
108
|
+
to == owner() || hasRole(REVOKER_ROLE, to),
|
|
109
|
+
AccessControlUnauthorizedAccount(to, REVOKER_ROLE)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// We assume revokers are trusted when specifying revocation destinations.
|
|
113
|
+
// Therefore, we bypass LSP1 receiver checks.
|
|
114
|
+
_transfer(from, to, tokenId, true, data);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function supportsInterface(
|
|
118
|
+
bytes4 interfaceId
|
|
119
|
+
)
|
|
120
|
+
public
|
|
121
|
+
view
|
|
122
|
+
virtual
|
|
123
|
+
override(
|
|
124
|
+
AccessControlExtendedInitAbstract,
|
|
125
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
|
126
|
+
)
|
|
127
|
+
returns (bool)
|
|
128
|
+
{
|
|
129
|
+
return
|
|
130
|
+
AccessControlExtendedInitAbstract.supportsInterface(interfaceId) ||
|
|
131
|
+
LSP8IdentifiableDigitalAssetInitAbstract.supportsInterface(
|
|
132
|
+
interfaceId
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// @dev Overridden function to ensure previous revokers do not persist after contract ownership has been transferred.
|
|
137
|
+
/// The only exception is if the old contract owner had the `REVOKER_ROLE`. This role will be given to the new owner.
|
|
138
|
+
///
|
|
139
|
+
/// @custom:warning This function clears the entire `REVOKER_ROLE` member set.
|
|
140
|
+
/// - Gas cost scales linearly with the number of addresses with the `REVOKER_ROLE`.
|
|
141
|
+
/// - If the number of addresses with the `REVOKER_ROLE` is large, it might consume a lot of gas,
|
|
142
|
+
/// leading the transaction to approach or exceed the block gas limit and fail.
|
|
143
|
+
/// Consider revoking addresses with the `REVOKER_ROLE` in batches in separate transactions to mitigate this.
|
|
144
|
+
function _transferOwnership(
|
|
145
|
+
address newOwner
|
|
146
|
+
)
|
|
147
|
+
internal
|
|
148
|
+
virtual
|
|
149
|
+
override(AccessControlExtendedInitAbstract, OwnableUpgradeable)
|
|
150
|
+
{
|
|
151
|
+
// restore default admin hierarchy so a previously-installed custom admin
|
|
152
|
+
// cannot grant REVOKER_ROLE to new accounts post-transfer
|
|
153
|
+
_setRoleAdmin(REVOKER_ROLE, DEFAULT_ADMIN_ROLE);
|
|
154
|
+
|
|
155
|
+
// Transfer all roles from old owner to new owner first (including the `REVOKER_ROLE`)
|
|
156
|
+
// before clearing the list of revokers.
|
|
157
|
+
super._transferOwnership(newOwner);
|
|
158
|
+
|
|
159
|
+
address[] memory revokers = getRoleMembers(REVOKER_ROLE);
|
|
160
|
+
|
|
161
|
+
// Exclude the new owner from the list of revokers to delete.
|
|
162
|
+
for (uint256 ii = 0; ii < revokers.length; ++ii) {
|
|
163
|
+
address revoker = revokers[ii];
|
|
164
|
+
if (revoker == newOwner) continue;
|
|
165
|
+
_revokeRole(REVOKER_ROLE, revoker);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
171
|
+
* variables without shifting down storage in the inheritance chain.
|
|
172
|
+
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
173
|
+
*
|
|
174
|
+
* @custom:info The size of the `__gap` array is calculated so that the amount of storage used by the contract
|
|
175
|
+
* always adds up to the same number (in this case 50 storage slots).
|
|
176
|
+
*/
|
|
177
|
+
uint256[49] private __gap;
|
|
178
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// SPDX-License-Identifier:
|
|
2
|
-
|
|
3
|
-
pragma solidity ^0.8.0;
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
5
|
LSP8IdentifiableDigitalAsset
|
|
7
|
-
} from "
|
|
6
|
+
} from "../../LSP8IdentifiableDigitalAsset.sol";
|
|
8
7
|
import {Votes} from "@openzeppelin/contracts/governance/utils/Votes.sol";
|
|
9
8
|
import {
|
|
10
9
|
_TYPEID_LSP8_VOTESDELEGATOR,
|
package/contracts/extensions/{LSP8VotesInitAbstract.sol → LSP8Votes/LSP8VotesInitAbstract.sol}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// SPDX-License-Identifier:
|
|
2
|
-
pragma solidity ^0.8.
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
LSP8IdentifiableDigitalAssetInitAbstract
|
|
6
|
-
} from "
|
|
6
|
+
} from "../../LSP8IdentifiableDigitalAssetInitAbstract.sol";
|
|
7
7
|
import {
|
|
8
8
|
VotesUpgradeable
|
|
9
9
|
} from "@openzeppelin/contracts-upgradeable/governance/utils/VotesUpgradeable.sol";
|
|
@@ -0,0 +1,277 @@
|
|
|
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
|
+
AccessControlExtendedAbstract
|
|
8
|
+
} from "../extensions/AccessControlExtended/AccessControlExtendedAbstract.sol";
|
|
9
|
+
import {
|
|
10
|
+
LSP8IdentifiableDigitalAsset
|
|
11
|
+
} from "../LSP8IdentifiableDigitalAsset.sol";
|
|
12
|
+
|
|
13
|
+
// extensions
|
|
14
|
+
import {LSP8Burnable} from "../extensions/LSP8Burnable/LSP8Burnable.sol";
|
|
15
|
+
import {
|
|
16
|
+
LSP8MintableAbstract
|
|
17
|
+
} from "../extensions/LSP8Mintable/LSP8MintableAbstract.sol";
|
|
18
|
+
import {
|
|
19
|
+
LSP8CappedSupplyAbstract
|
|
20
|
+
} from "../extensions/LSP8CappedSupply/LSP8CappedSupplyAbstract.sol";
|
|
21
|
+
import {
|
|
22
|
+
LSP8CappedBalanceAbstract
|
|
23
|
+
} from "../extensions/LSP8CappedBalance/LSP8CappedBalanceAbstract.sol";
|
|
24
|
+
import {
|
|
25
|
+
LSP8NonTransferableAbstract
|
|
26
|
+
} from "../extensions/LSP8NonTransferable/LSP8NonTransferableAbstract.sol";
|
|
27
|
+
import {
|
|
28
|
+
LSP8RevokableAbstract
|
|
29
|
+
} from "../extensions/LSP8Revokable/LSP8RevokableAbstract.sol";
|
|
30
|
+
|
|
31
|
+
// constants
|
|
32
|
+
import {
|
|
33
|
+
LSP8MintableParams,
|
|
34
|
+
LSP8NonTransferableParams,
|
|
35
|
+
LSP8CappedParams,
|
|
36
|
+
LSP8RevokableParams
|
|
37
|
+
} from "./LSP8CustomizableTokenConstants.sol";
|
|
38
|
+
|
|
39
|
+
// errors
|
|
40
|
+
import {
|
|
41
|
+
LSP8MintDisabled
|
|
42
|
+
} from "../extensions/LSP8Mintable/LSP8MintableErrors.sol";
|
|
43
|
+
import {
|
|
44
|
+
LSP8CappedSupplyCannotMintOverCap
|
|
45
|
+
} from "../extensions/LSP8CappedSupply/LSP8CappedSupplyErrors.sol";
|
|
46
|
+
|
|
47
|
+
/// @title LSP8CustomizableToken
|
|
48
|
+
/// @dev A customizable LSP8 token that implements multiple features and uses role-based exemptions.
|
|
49
|
+
/// Implements {LSP8Burnable} to allow burning.
|
|
50
|
+
/// Implements {LSP8Mintable} to allow minting.
|
|
51
|
+
/// Implements {LSP8CappedSupply} to set total supply cap.
|
|
52
|
+
/// Implements {LSP8CappedBalance} to set balance caps.
|
|
53
|
+
/// Implements {LSP8NonTransferable} to restrict transfers.
|
|
54
|
+
/// Implements {LSP8Revokable} to allow revoking tokens.
|
|
55
|
+
contract LSP8CustomizableToken is
|
|
56
|
+
LSP8Burnable,
|
|
57
|
+
LSP8MintableAbstract,
|
|
58
|
+
LSP8CappedSupplyAbstract,
|
|
59
|
+
LSP8CappedBalanceAbstract,
|
|
60
|
+
LSP8NonTransferableAbstract,
|
|
61
|
+
LSP8RevokableAbstract
|
|
62
|
+
{
|
|
63
|
+
/// @notice Initializes the token with name, symbol, owner, and customizable features.
|
|
64
|
+
/// @dev Sets up minting, balance cap, transfer restrictions and supply cap. Mints initial tokens if specified. Reverts if initialMintTokenIds length exceeds tokenSupplyCap. Inherits constructor logic from parent contracts.
|
|
65
|
+
/// @param name_ The name of the token.
|
|
66
|
+
/// @param symbol_ The symbol of the token.
|
|
67
|
+
/// @param newOwner_ The initial owner of the token.
|
|
68
|
+
/// @param lsp4TokenType_ The LSP4 token type (e.g., 1 for NFT, 2 for Collection).
|
|
69
|
+
/// @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
|
|
70
|
+
/// @param mintableParams Deployment configuration for minting feature (see above).
|
|
71
|
+
/// @param cappedParams Deployment configuration for capped balance and capped supply features (see above).
|
|
72
|
+
/// @param nonTransferableParams Deployment configuration for non-transferable feature (see above).
|
|
73
|
+
/// @param revokableParams Deployment configuration for revokable feature (see above).
|
|
74
|
+
constructor(
|
|
75
|
+
string memory name_,
|
|
76
|
+
string memory symbol_,
|
|
77
|
+
address newOwner_,
|
|
78
|
+
uint256 lsp4TokenType_,
|
|
79
|
+
uint256 lsp8TokenIdFormat_,
|
|
80
|
+
LSP8MintableParams memory mintableParams,
|
|
81
|
+
LSP8CappedParams memory cappedParams,
|
|
82
|
+
LSP8NonTransferableParams memory nonTransferableParams,
|
|
83
|
+
LSP8RevokableParams memory revokableParams
|
|
84
|
+
)
|
|
85
|
+
LSP8IdentifiableDigitalAsset(
|
|
86
|
+
name_,
|
|
87
|
+
symbol_,
|
|
88
|
+
newOwner_,
|
|
89
|
+
lsp4TokenType_,
|
|
90
|
+
lsp8TokenIdFormat_
|
|
91
|
+
)
|
|
92
|
+
AccessControlExtendedAbstract()
|
|
93
|
+
LSP8MintableAbstract(mintableParams.isMintable)
|
|
94
|
+
LSP8CappedSupplyAbstract(cappedParams.tokenSupplyCap)
|
|
95
|
+
LSP8CappedBalanceAbstract(cappedParams.tokenBalanceCap)
|
|
96
|
+
LSP8NonTransferableAbstract(
|
|
97
|
+
nonTransferableParams.transferLockStart,
|
|
98
|
+
nonTransferableParams.transferLockEnd
|
|
99
|
+
)
|
|
100
|
+
LSP8RevokableAbstract(revokableParams.isRevokable)
|
|
101
|
+
{
|
|
102
|
+
_initialMint({
|
|
103
|
+
to: newOwner_,
|
|
104
|
+
initialMintTokenIds: mintableParams.initialMintTokenIds
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// @inheritdoc LSP8CappedSupplyAbstract
|
|
109
|
+
/// @notice Returns the token supply cap.
|
|
110
|
+
/// @dev If minting is enabled, returns the configured supply cap defining the maximum number of NFTs that can be minted.
|
|
111
|
+
/// If minting is disabled, returns the current total supply as the effective cap (no more NFTs can be created).
|
|
112
|
+
function tokenSupplyCap() public view virtual override returns (uint256) {
|
|
113
|
+
return isMintable ? super.tokenSupplyCap() : totalSupply();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// @dev Required override to resolve multiple inheritance. Calls every parent {supportsInterface} functions
|
|
117
|
+
/// via `super` to aggregate the interface IDs supported across all inherited modules.
|
|
118
|
+
function supportsInterface(
|
|
119
|
+
bytes4 interfaceId
|
|
120
|
+
)
|
|
121
|
+
public
|
|
122
|
+
view
|
|
123
|
+
virtual
|
|
124
|
+
override(
|
|
125
|
+
LSP8IdentifiableDigitalAsset,
|
|
126
|
+
LSP8CappedBalanceAbstract,
|
|
127
|
+
LSP8MintableAbstract,
|
|
128
|
+
LSP8NonTransferableAbstract,
|
|
129
|
+
LSP8RevokableAbstract
|
|
130
|
+
)
|
|
131
|
+
returns (bool)
|
|
132
|
+
{
|
|
133
|
+
return super.supportsInterface(interfaceId);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// @dev Override to bypass the non transferable check when revokers revoke users' tokens.
|
|
137
|
+
function _nonTransferableCheck(
|
|
138
|
+
address from,
|
|
139
|
+
address to,
|
|
140
|
+
bytes32 tokenId,
|
|
141
|
+
bool force,
|
|
142
|
+
bytes memory data
|
|
143
|
+
) internal virtual override {
|
|
144
|
+
if (_isRevocationBypass(to)) return;
|
|
145
|
+
super._nonTransferableCheck(from, to, tokenId, force, data);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// @dev Override to bypass the token balance cap check when revokers revoke users' tokens.
|
|
149
|
+
function _tokenBalanceCapCheck(
|
|
150
|
+
address from,
|
|
151
|
+
address to,
|
|
152
|
+
bytes32 tokenId,
|
|
153
|
+
bool force,
|
|
154
|
+
bytes memory data
|
|
155
|
+
) internal virtual override {
|
|
156
|
+
if (_isRevocationBypass(to)) return;
|
|
157
|
+
super._tokenBalanceCapCheck(from, to, tokenId, force, data);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// @inheritdoc LSP8MintableAbstract
|
|
161
|
+
/// @dev Overridden function to allow minting only if:
|
|
162
|
+
/// - the minting feature is enabled, from {LSP8MintableAbstract}
|
|
163
|
+
/// - the total number of NFTs does not exceed the capped supply after minting, from {LSP8CappedSupplyAbstract}
|
|
164
|
+
function _mint(
|
|
165
|
+
address to,
|
|
166
|
+
bytes32 tokenId,
|
|
167
|
+
bool force,
|
|
168
|
+
bytes memory data
|
|
169
|
+
)
|
|
170
|
+
internal
|
|
171
|
+
virtual
|
|
172
|
+
override(
|
|
173
|
+
LSP8IdentifiableDigitalAsset,
|
|
174
|
+
LSP8MintableAbstract,
|
|
175
|
+
LSP8CappedSupplyAbstract
|
|
176
|
+
)
|
|
177
|
+
{
|
|
178
|
+
require(isMintable, LSP8MintDisabled());
|
|
179
|
+
LSP8CappedSupplyAbstract._mint(to, tokenId, force, data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// @notice Hook called before a token transfer to enforce restrictions.
|
|
183
|
+
/// @dev Combines checks from {LSP8CappedBalance} and {LSP8NonTransferable}.
|
|
184
|
+
/// - Bypasses {LSP8NonTransferable} checks for senders (`from`) holding the `NON_TRANSFERABLE_BYPASS_ROLE` role.
|
|
185
|
+
/// - Bypasses {LSP8CappedBalance} checks for recipients (`to`) holding the `UNCAPPED_BALANCE_ROLE` role.
|
|
186
|
+
/// - Allows minting (from address(0)) and burning to address(0) regardless of restrictions.
|
|
187
|
+
/// @param from The address sending the token.
|
|
188
|
+
/// @param to The address receiving the token.
|
|
189
|
+
/// @param tokenId The unique identifier of the token being transferred.
|
|
190
|
+
/// @param force Whether to force the transfer.
|
|
191
|
+
/// @param data Additional data for the transfer.
|
|
192
|
+
function _beforeTokenTransfer(
|
|
193
|
+
address from,
|
|
194
|
+
address to,
|
|
195
|
+
bytes32 tokenId,
|
|
196
|
+
bool force,
|
|
197
|
+
bytes memory data
|
|
198
|
+
)
|
|
199
|
+
internal
|
|
200
|
+
virtual
|
|
201
|
+
override(
|
|
202
|
+
LSP8IdentifiableDigitalAsset,
|
|
203
|
+
LSP8CappedBalanceAbstract,
|
|
204
|
+
LSP8NonTransferableAbstract
|
|
205
|
+
)
|
|
206
|
+
{
|
|
207
|
+
super._beforeTokenTransfer(from, to, tokenId, force, data);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// @dev Required override to resolve multiple inheritance. Calls every parent {_transferOwnership} function
|
|
211
|
+
/// via `super` so that each inherited module updates its ownership-dependent state.
|
|
212
|
+
///
|
|
213
|
+
/// When contract ownership changes, this will:
|
|
214
|
+
/// - clear the admin role for: `MINTER_ROLE`, `REVOKER_ROLE`, `NON_TRANSFERABLE_BYPASS_ROLE`, `UNCAPPED_BALANCE_ROLE`
|
|
215
|
+
/// - clear the list of addresses holding the `REVOKER_ROLE`
|
|
216
|
+
function _transferOwnership(
|
|
217
|
+
address newOwner
|
|
218
|
+
)
|
|
219
|
+
internal
|
|
220
|
+
virtual
|
|
221
|
+
override(
|
|
222
|
+
Ownable,
|
|
223
|
+
LSP8CappedBalanceAbstract,
|
|
224
|
+
LSP8MintableAbstract,
|
|
225
|
+
LSP8NonTransferableAbstract,
|
|
226
|
+
LSP8RevokableAbstract
|
|
227
|
+
)
|
|
228
|
+
{
|
|
229
|
+
super._transferOwnership(newOwner);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/// @dev Mint initial NFTs without enforcing check if the token contract is mintable or not.
|
|
233
|
+
/// Enforces the configured capped-supply value directly (not {tokenSupplyCap} when minting is disabled).
|
|
234
|
+
function _initialMint(
|
|
235
|
+
address to,
|
|
236
|
+
bytes32[] memory initialMintTokenIds
|
|
237
|
+
) private {
|
|
238
|
+
uint256 configuredTokenSupplyCap = LSP8CappedSupplyAbstract
|
|
239
|
+
.tokenSupplyCap();
|
|
240
|
+
bool isCappedSupplyConfigured = configuredTokenSupplyCap > 0;
|
|
241
|
+
|
|
242
|
+
if (isCappedSupplyConfigured) {
|
|
243
|
+
bool mintingExceedsSupplyCap = initialMintTokenIds.length >
|
|
244
|
+
configuredTokenSupplyCap;
|
|
245
|
+
|
|
246
|
+
require(
|
|
247
|
+
!mintingExceedsSupplyCap,
|
|
248
|
+
LSP8CappedSupplyCannotMintOverCap()
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (uint256 ii = 0; ii < initialMintTokenIds.length; ++ii) {
|
|
253
|
+
LSP8IdentifiableDigitalAsset._mint(
|
|
254
|
+
to,
|
|
255
|
+
initialMintTokenIds[ii],
|
|
256
|
+
true,
|
|
257
|
+
""
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// @dev Returns whether the current call is a legitimate revocation that should bypass the
|
|
263
|
+
/// {LSP8NonTransferable} and {LSP8CappedBalance} restrictions when revoking tokens from a token holder.
|
|
264
|
+
///
|
|
265
|
+
/// Returns `true` only when all of the following conditions are met:
|
|
266
|
+
/// - the function being called is {revoke}.
|
|
267
|
+
/// - the revoking feature is enabled.
|
|
268
|
+
/// - the caller (`msg.sender`) holds the `REVOKER_ROLE`.
|
|
269
|
+
/// - the recipient (`to`) is either the contract `owner()` or a revoker (an address holding the `REVOKER_ROLE`).
|
|
270
|
+
function _isRevocationBypass(address to) private view returns (bool) {
|
|
271
|
+
return
|
|
272
|
+
msg.sig == this.revoke.selector &&
|
|
273
|
+
isRevokable() &&
|
|
274
|
+
hasRole(REVOKER_ROLE, msg.sender) &&
|
|
275
|
+
(to == owner() || hasRole(REVOKER_ROLE, to));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
/// @dev Deployment configuration for minting feature.
|
|
5
|
+
/// @param isMintable True to enable minting after deployment, false to disable it forever.
|
|
6
|
+
/// @param initialMintTokenIds Array of tokenIds to mint to `newOwner_` on deployment.
|
|
7
|
+
struct LSP8MintableParams {
|
|
8
|
+
bool isMintable;
|
|
9
|
+
bytes32[] initialMintTokenIds;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// @dev Deployment configuration for capped balance and capped supply features.
|
|
13
|
+
/// @param tokenBalanceCap The maximum number of NFTs per address, 0 to disable.
|
|
14
|
+
/// @param tokenSupplyCap The maximum total supply of NFTs, 0 to disable.
|
|
15
|
+
struct LSP8CappedParams {
|
|
16
|
+
uint256 tokenBalanceCap;
|
|
17
|
+
uint256 tokenSupplyCap;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// @dev Deployment configuration for non-transferable feature.
|
|
21
|
+
/// @param transferLockStart The start timestamp of the transfer lock period, 0 to disable.
|
|
22
|
+
/// @param transferLockEnd The end timestamp of the transfer lock period, 0 to disable.
|
|
23
|
+
struct LSP8NonTransferableParams {
|
|
24
|
+
uint256 transferLockStart;
|
|
25
|
+
uint256 transferLockEnd;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// @dev Deployment configuration for revokable feature.
|
|
29
|
+
/// @param isRevokable True to enable token revocation after deployment, false to disable it.
|
|
30
|
+
struct LSP8RevokableParams {
|
|
31
|
+
bool isRevokable;
|
|
32
|
+
}
|