@lukso/lsp8-contracts 0.15.0-rc.0
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/README.md +3 -0
- package/artifacts/LSP8CappedSupply.json +959 -0
- package/artifacts/LSP8CappedSupplyInitAbstract.json +972 -0
- package/artifacts/LSP8IdentifiableDigitalAsset.json +936 -0
- package/artifacts/LSP8IdentifiableDigitalAssetInitAbstract.json +949 -0
- package/artifacts/LSP8Mintable.json +1006 -0
- package/artifacts/LSP8MintableInit.json +1026 -0
- package/contracts/ILSP8IdentifiableDigitalAsset.sol +328 -0
- package/contracts/LSP8Constants.sol +41 -0
- package/contracts/LSP8Errors.sol +118 -0
- package/contracts/LSP8IdentifiableDigitalAsset.sol +241 -0
- package/contracts/LSP8IdentifiableDigitalAssetCore.sol +806 -0
- package/contracts/LSP8IdentifiableDigitalAssetInitAbstract.sol +248 -0
- package/contracts/extensions/LSP8Burnable.sol +30 -0
- package/contracts/extensions/LSP8BurnableInitAbstract.sol +24 -0
- package/contracts/extensions/LSP8CappedSupply.sol +85 -0
- package/contracts/extensions/LSP8CappedSupplyInitAbstract.sol +88 -0
- package/contracts/extensions/LSP8Enumerable.sol +69 -0
- package/contracts/extensions/LSP8EnumerableInitAbstract.sol +64 -0
- package/contracts/presets/ILSP8Mintable.sol +33 -0
- package/contracts/presets/LSP8Mintable.sol +60 -0
- package/contracts/presets/LSP8MintableInit.sol +43 -0
- package/contracts/presets/LSP8MintableInitAbstract.sol +62 -0
- package/dist/index.cjs +33 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.mjs +28 -0
- package/package.json +56 -0
- package/types/LSP8CappedSupply.ts +792 -0
- package/types/LSP8CappedSupplyInitAbstract.ts +824 -0
- package/types/LSP8IdentifiableDigitalAsset.ts +778 -0
- package/types/LSP8IdentifiableDigitalAssetInitAbstract.ts +813 -0
- package/types/LSP8Mintable.ts +797 -0
- package/types/LSP8MintableInit.ts +860 -0
- package/types/common.ts +131 -0
- package/types/contracts/LSP8IdentifiableDigitalAsset.ts +778 -0
- package/types/contracts/LSP8IdentifiableDigitalAssetInitAbstract.ts +813 -0
- package/types/contracts/extensions/LSP8CappedSupply.ts +792 -0
- package/types/contracts/extensions/LSP8CappedSupplyInitAbstract.ts +824 -0
- package/types/contracts/presets/LSP8Mintable.ts +797 -0
- package/types/contracts/presets/LSP8MintableInit.ts +860 -0
- package/types/index.ts +16 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
6
|
+
|
7
|
+
// modules
|
8
|
+
import {ERC725YCore} from "@erc725/smart-contracts/contracts/ERC725YCore.sol";
|
9
|
+
import {
|
10
|
+
LSP8IdentifiableDigitalAssetCore
|
11
|
+
} from "./LSP8IdentifiableDigitalAssetCore.sol";
|
12
|
+
import {
|
13
|
+
LSP4DigitalAssetMetadataInitAbstract
|
14
|
+
} from "@lukso/lsp4-contracts/contracts/LSP4DigitalAssetMetadataInitAbstract.sol";
|
15
|
+
|
16
|
+
import {
|
17
|
+
LSP4DigitalAssetMetadataCore
|
18
|
+
} from "@lukso/lsp4-contracts/contracts/LSP4DigitalAssetMetadataCore.sol";
|
19
|
+
|
20
|
+
import {
|
21
|
+
LSP17Extendable
|
22
|
+
} from "@lukso/lsp17contractextension-contracts/contracts/LSP17Extendable.sol";
|
23
|
+
|
24
|
+
// libraries
|
25
|
+
import {LSP2Utils} from "@lukso/lsp2-contracts/contracts/LSP2Utils.sol";
|
26
|
+
|
27
|
+
// constants
|
28
|
+
import {_INTERFACEID_LSP8, _LSP8_TOKENID_FORMAT_KEY} from "./LSP8Constants.sol";
|
29
|
+
|
30
|
+
// errors
|
31
|
+
import {
|
32
|
+
LSP8TokenContractCannotHoldValue,
|
33
|
+
LSP8TokenIdFormatNotEditable
|
34
|
+
} from "./LSP8Errors.sol";
|
35
|
+
|
36
|
+
import {
|
37
|
+
_LSP17_EXTENSION_PREFIX
|
38
|
+
} from "@lukso/lsp17contractextension-contracts/contracts/LSP17Constants.sol";
|
39
|
+
|
40
|
+
// errors
|
41
|
+
|
42
|
+
import {
|
43
|
+
NoExtensionFoundForFunctionSelector,
|
44
|
+
InvalidFunctionSelector,
|
45
|
+
InvalidExtensionAddress
|
46
|
+
} from "@lukso/lsp17contractextension-contracts/contracts/LSP17Errors.sol";
|
47
|
+
|
48
|
+
/**
|
49
|
+
* @title Implementation of a LSP8 Identifiable Digital Asset, a contract that represents a non-fungible token.
|
50
|
+
* @author Matthew Stevens
|
51
|
+
*
|
52
|
+
* @dev Inheritable proxy implementation contract of the LSP8 standard.
|
53
|
+
*
|
54
|
+
* Minting and transferring are done using by giving a unique `tokenId`.
|
55
|
+
* This implementation is agnostic to the way tokens are created.
|
56
|
+
* A supply mechanism has to be added in a derived contract using {_mint}
|
57
|
+
* For a generic mechanism, see {LSP7Mintable}.
|
58
|
+
*/
|
59
|
+
abstract contract LSP8IdentifiableDigitalAssetInitAbstract is
|
60
|
+
LSP4DigitalAssetMetadataInitAbstract,
|
61
|
+
LSP8IdentifiableDigitalAssetCore,
|
62
|
+
LSP17Extendable
|
63
|
+
{
|
64
|
+
/**
|
65
|
+
* @dev Initialize a `LSP8IdentifiableDigitalAsset` contract and set the tokenId format inside the ERC725Y storage of the contract.
|
66
|
+
* This will also set the token `name_` and `symbol_` under the ERC725Y data keys `LSP4TokenName` and `LSP4TokenSymbol`.
|
67
|
+
*
|
68
|
+
* @param name_ The name of the token
|
69
|
+
* @param symbol_ The symbol of the token
|
70
|
+
* @param newOwner_ The owner of the the token-Metadata
|
71
|
+
* @param lsp4TokenType_ The type of token this digital asset contract represents (`0` = Token, `1` = NFT, `2` = Collection).
|
72
|
+
* @param lsp8TokenIdFormat_ The format of tokenIds (= NFTs) that this contract will create.
|
73
|
+
*
|
74
|
+
* @custom:warning Make sure the tokenId format provided on deployment is correct, as it can only be set once
|
75
|
+
* and cannot be changed in the ERC725Y storage after the contract has been initialized.
|
76
|
+
*/
|
77
|
+
function _initialize(
|
78
|
+
string memory name_,
|
79
|
+
string memory symbol_,
|
80
|
+
address newOwner_,
|
81
|
+
uint256 lsp4TokenType_,
|
82
|
+
uint256 lsp8TokenIdFormat_
|
83
|
+
) internal virtual onlyInitializing {
|
84
|
+
LSP4DigitalAssetMetadataInitAbstract._initialize(
|
85
|
+
name_,
|
86
|
+
symbol_,
|
87
|
+
newOwner_,
|
88
|
+
lsp4TokenType_
|
89
|
+
);
|
90
|
+
|
91
|
+
LSP4DigitalAssetMetadataInitAbstract._setData(
|
92
|
+
_LSP8_TOKENID_FORMAT_KEY,
|
93
|
+
abi.encode(lsp8TokenIdFormat_)
|
94
|
+
);
|
95
|
+
}
|
96
|
+
|
97
|
+
// fallback function
|
98
|
+
|
99
|
+
/**
|
100
|
+
* @notice The `fallback` function was called with the following amount of native tokens: `msg.value`; and the following calldata: `callData`.
|
101
|
+
*
|
102
|
+
* @dev Achieves the goal of [LSP-17-ContractExtension] standard by extending the contract to handle calls of functions that do not exist natively,
|
103
|
+
* forwarding the function call to the extension address mapped to the function being called.
|
104
|
+
*
|
105
|
+
* This function is executed when:
|
106
|
+
* - Sending data of length less than 4 bytes to the contract.
|
107
|
+
* - The first 4 bytes of the calldata do not match any publicly callable functions from the contract ABI.
|
108
|
+
* - Receiving native tokens
|
109
|
+
*
|
110
|
+
* 1. If the data is equal or longer than 4 bytes, the [ERC-725Y] storage is queried with the following data key: [_LSP17_EXTENSION_PREFIX] + `bytes4(msg.sig)` (Check [LSP-2-ERC725YJSONSchema] for encoding the data key)
|
111
|
+
*
|
112
|
+
* - If there is no address stored under the following data key, revert with {NoExtensionFoundForFunctionSelector(bytes4)}. The data key relative to `bytes4(0)` is an exception, where no reverts occurs if there is no extension address stored under. This exception is made to allow users to send random data (graffiti) to the account and to be able to react on it.
|
113
|
+
*
|
114
|
+
* - If there is an address, forward the `msg.data` to the extension using the CALL opcode, appending 52 bytes (20 bytes of `msg.sender` and 32 bytes of `msg.value`). Return what the calls returns, or revert if the call failed.
|
115
|
+
*
|
116
|
+
* 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert.
|
117
|
+
*/
|
118
|
+
// solhint-disable-next-line no-complex-fallback
|
119
|
+
fallback(
|
120
|
+
bytes calldata callData
|
121
|
+
) external payable virtual returns (bytes memory) {
|
122
|
+
if (msg.data.length < 4) {
|
123
|
+
revert InvalidFunctionSelector(callData);
|
124
|
+
}
|
125
|
+
return _fallbackLSP17Extendable(callData);
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* @dev Reverts whenever someone tries to send native tokens to a LSP8 contract.
|
130
|
+
* @notice LSP8 contract cannot receive native tokens.
|
131
|
+
*/
|
132
|
+
receive() external payable virtual {
|
133
|
+
// revert on empty calls with no value
|
134
|
+
if (msg.value == 0) {
|
135
|
+
revert InvalidFunctionSelector(hex"00000000");
|
136
|
+
}
|
137
|
+
|
138
|
+
revert LSP8TokenContractCannotHoldValue();
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* @dev Forwards the call with the received value to an extension mapped to a function selector.
|
143
|
+
*
|
144
|
+
* Calls {_getExtensionAndForwardValue} to get the address of the extension mapped to the function selector being
|
145
|
+
* called on the account. If there is no extension, the address(0) will be returned.
|
146
|
+
* We will always forward the value to the extension, as the LSP8 contract is not supposed to hold any native tokens.
|
147
|
+
*
|
148
|
+
* Reverts if there is no extension for the function being called.
|
149
|
+
*
|
150
|
+
* If there is an extension for the function selector being called, it calls the extension with the
|
151
|
+
* CALL opcode, passing the {msg.data} appended with the 20 bytes of the {msg.sender} and
|
152
|
+
* 32 bytes of the {msg.value}
|
153
|
+
*
|
154
|
+
* @custom:info The LSP8 Token contract should not hold any native tokens. Any native tokens received by the contract
|
155
|
+
* will be forwarded to the extension address mapped to the selector from `msg.sig`.
|
156
|
+
*/
|
157
|
+
function _fallbackLSP17Extendable(
|
158
|
+
bytes calldata callData
|
159
|
+
) internal virtual override returns (bytes memory) {
|
160
|
+
// If there is a function selector
|
161
|
+
(address extension, ) = _getExtensionAndForwardValue(msg.sig);
|
162
|
+
|
163
|
+
// if no extension was found, revert
|
164
|
+
if (extension == address(0))
|
165
|
+
revert NoExtensionFoundForFunctionSelector(msg.sig);
|
166
|
+
|
167
|
+
(bool success, bytes memory result) = extension.call{value: msg.value}(
|
168
|
+
abi.encodePacked(callData, msg.sender, msg.value)
|
169
|
+
);
|
170
|
+
|
171
|
+
if (success) {
|
172
|
+
return result;
|
173
|
+
} else {
|
174
|
+
// `mload(result)` -> offset in memory where `result.length` is located
|
175
|
+
// `add(result, 32)` -> offset in memory where `result` data starts
|
176
|
+
// solhint-disable no-inline-assembly
|
177
|
+
/// @solidity memory-safe-assembly
|
178
|
+
assembly {
|
179
|
+
let resultdata_size := mload(result)
|
180
|
+
revert(add(result, 32), resultdata_size)
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
/**
|
186
|
+
* @dev Returns the extension address stored under the following data key:
|
187
|
+
* - {_LSP17_EXTENSION_PREFIX} + `<bytes4>` (Check [LSP2-ERC725YJSONSchema] for encoding the data key).
|
188
|
+
* - If no extension is stored, returns the address(0).
|
189
|
+
*/
|
190
|
+
function _getExtensionAndForwardValue(
|
191
|
+
bytes4 functionSelector
|
192
|
+
) internal view virtual override returns (address, bool) {
|
193
|
+
// Generate the data key relevant for the functionSelector being called
|
194
|
+
bytes32 mappedExtensionDataKey = LSP2Utils.generateMappingKey(
|
195
|
+
_LSP17_EXTENSION_PREFIX,
|
196
|
+
functionSelector
|
197
|
+
);
|
198
|
+
|
199
|
+
// Check if there is an extension stored under the generated data key
|
200
|
+
bytes memory extensionAddress = ERC725YCore._getData(
|
201
|
+
mappedExtensionDataKey
|
202
|
+
);
|
203
|
+
if (extensionAddress.length != 20 && extensionAddress.length != 0)
|
204
|
+
revert InvalidExtensionAddress(extensionAddress);
|
205
|
+
|
206
|
+
return (address(bytes20(extensionAddress)), true);
|
207
|
+
}
|
208
|
+
|
209
|
+
/**
|
210
|
+
* @dev See {IERC165-supportsInterface}.
|
211
|
+
*/
|
212
|
+
function supportsInterface(
|
213
|
+
bytes4 interfaceId
|
214
|
+
)
|
215
|
+
public
|
216
|
+
view
|
217
|
+
virtual
|
218
|
+
override(IERC165, ERC725YCore, LSP17Extendable)
|
219
|
+
returns (bool)
|
220
|
+
{
|
221
|
+
return
|
222
|
+
interfaceId == _INTERFACEID_LSP8 ||
|
223
|
+
super.supportsInterface(interfaceId) ||
|
224
|
+
LSP17Extendable._supportsInterfaceInERC165Extension(interfaceId);
|
225
|
+
}
|
226
|
+
|
227
|
+
/**
|
228
|
+
* @inheritdoc LSP4DigitalAssetMetadataInitAbstract
|
229
|
+
* @dev The ERC725Y data key `_LSP8_TOKENID_FORMAT_KEY` cannot be changed
|
230
|
+
* once the identifiable digital asset contract has been deployed.
|
231
|
+
*/
|
232
|
+
function _setData(
|
233
|
+
bytes32 dataKey,
|
234
|
+
bytes memory dataValue
|
235
|
+
)
|
236
|
+
internal
|
237
|
+
virtual
|
238
|
+
override(
|
239
|
+
LSP4DigitalAssetMetadataInitAbstract,
|
240
|
+
LSP4DigitalAssetMetadataCore
|
241
|
+
)
|
242
|
+
{
|
243
|
+
if (dataKey == _LSP8_TOKENID_FORMAT_KEY) {
|
244
|
+
revert LSP8TokenIdFormatNotEditable();
|
245
|
+
}
|
246
|
+
LSP4DigitalAssetMetadataInitAbstract._setData(dataKey, dataValue);
|
247
|
+
}
|
248
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
import {
|
5
|
+
LSP8IdentifiableDigitalAsset
|
6
|
+
} from "../LSP8IdentifiableDigitalAsset.sol";
|
7
|
+
|
8
|
+
// errors
|
9
|
+
import {LSP8NotTokenOperator} from "../LSP8Errors.sol";
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @dev LSP8 token extension that allows token holders to destroy both
|
13
|
+
* their own tokens and those that they have an allowance for as an operator.
|
14
|
+
*/
|
15
|
+
abstract contract LSP8Burnable is LSP8IdentifiableDigitalAsset {
|
16
|
+
/**
|
17
|
+
* @notice Burning tokenId `tokenId`. This tokenId will not be recoverable! (additional data sent: `data`).
|
18
|
+
*
|
19
|
+
* @dev See internal {_burn} function for details.
|
20
|
+
*
|
21
|
+
* @param tokenId The tokenId to burn.
|
22
|
+
* @param data Any extra data to be sent alongside burning the tokenId.
|
23
|
+
*/
|
24
|
+
function burn(bytes32 tokenId, bytes memory data) public virtual {
|
25
|
+
if (!_isOperatorOrOwner(msg.sender, tokenId)) {
|
26
|
+
revert LSP8NotTokenOperator(tokenId, msg.sender);
|
27
|
+
}
|
28
|
+
_burn(tokenId, data);
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
import {
|
5
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
6
|
+
} from "../LSP8IdentifiableDigitalAssetInitAbstract.sol";
|
7
|
+
|
8
|
+
// errors
|
9
|
+
import {LSP8NotTokenOperator} from "../LSP8Errors.sol";
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @dev LSP8 extension (proxy version) that allows token holders to destroy both
|
13
|
+
* their own tokens and those that they have an allowance for as an operator.
|
14
|
+
*/
|
15
|
+
abstract contract LSP8BurnableInitAbstract is
|
16
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
17
|
+
{
|
18
|
+
function burn(bytes32 tokenId, bytes memory data) public virtual {
|
19
|
+
if (!_isOperatorOrOwner(msg.sender, tokenId)) {
|
20
|
+
revert LSP8NotTokenOperator(tokenId, msg.sender);
|
21
|
+
}
|
22
|
+
_burn(tokenId, data);
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
// modules
|
5
|
+
import {
|
6
|
+
LSP8IdentifiableDigitalAsset
|
7
|
+
} from "../LSP8IdentifiableDigitalAsset.sol";
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @dev LSP8 token extension to add a max token supply cap.
|
11
|
+
*/
|
12
|
+
abstract contract LSP8CappedSupply is LSP8IdentifiableDigitalAsset {
|
13
|
+
// --- Errors
|
14
|
+
|
15
|
+
/**
|
16
|
+
* @notice The `tokenSupplyCap` must be set and cannot be `0`.
|
17
|
+
* @dev Reverts when setting `0` for the {tokenSupplyCap}. The max token supply MUST be set to a number greater than 0.
|
18
|
+
*/
|
19
|
+
error LSP8CappedSupplyRequired();
|
20
|
+
|
21
|
+
/**
|
22
|
+
* @notice Cannot mint anymore as total supply reached the maximum cap.
|
23
|
+
* @dev Reverts when trying to mint tokens but the {totalSupply} has reached the maximum {tokenSupplyCap}.
|
24
|
+
*/
|
25
|
+
error LSP8CappedSupplyCannotMintOverCap();
|
26
|
+
|
27
|
+
// --- Storage
|
28
|
+
uint256 private immutable _TOKEN_SUPPLY_CAP;
|
29
|
+
|
30
|
+
/**
|
31
|
+
* @notice Deploying a `LSP8CappedSupply` token contract with max token supply cap set to `tokenSupplyCap_`.
|
32
|
+
* @dev Deploy a `LSP8CappedSupply` token contract and set the maximum token supply token cap up to which
|
33
|
+
* it is not possible to mint more tokens.
|
34
|
+
*
|
35
|
+
* @param tokenSupplyCap_ The maximum token supply.
|
36
|
+
*
|
37
|
+
* @custom:requirements
|
38
|
+
* - `tokenSupplyCap_` MUST NOT be 0.
|
39
|
+
*/
|
40
|
+
constructor(uint256 tokenSupplyCap_) {
|
41
|
+
if (tokenSupplyCap_ == 0) {
|
42
|
+
revert LSP8CappedSupplyRequired();
|
43
|
+
}
|
44
|
+
|
45
|
+
_TOKEN_SUPPLY_CAP = tokenSupplyCap_;
|
46
|
+
}
|
47
|
+
|
48
|
+
// --- Token queries
|
49
|
+
|
50
|
+
/**
|
51
|
+
* @notice The maximum supply amount of tokens allowed to exist is `_TOKEN_SUPPLY_CAP`.
|
52
|
+
*
|
53
|
+
* @dev Get the maximum number of tokens that can exist to circulate. Once {totalSupply} reaches
|
54
|
+
* reaches {totalSuuplyCap}, it is not possible to mint more tokens.
|
55
|
+
*
|
56
|
+
* @return The maximum number of tokens that can exist in the contract.
|
57
|
+
*/
|
58
|
+
function tokenSupplyCap() public view virtual returns (uint256) {
|
59
|
+
return _TOKEN_SUPPLY_CAP;
|
60
|
+
}
|
61
|
+
|
62
|
+
// --- Transfer functionality
|
63
|
+
|
64
|
+
/**
|
65
|
+
* @dev Same as {_mint} but allows to mint only if newly minted `tokenId` does not increase the {totalSupply}
|
66
|
+
* over the {tokenSupplyCap}.
|
67
|
+
* after a new `tokenId` has of tokens have been minted.
|
68
|
+
*
|
69
|
+
* @custom:requirements
|
70
|
+
* - {totalSupply} + 1 must not be greater than the {tokenSupplyCap} when calling this function.
|
71
|
+
* - `to` cannot be the zero address.
|
72
|
+
*/
|
73
|
+
function _mint(
|
74
|
+
address to,
|
75
|
+
bytes32 tokenId,
|
76
|
+
bool force,
|
77
|
+
bytes memory data
|
78
|
+
) internal virtual override {
|
79
|
+
if (totalSupply() + 1 > tokenSupplyCap()) {
|
80
|
+
revert LSP8CappedSupplyCannotMintOverCap();
|
81
|
+
}
|
82
|
+
|
83
|
+
super._mint(to, tokenId, force, data);
|
84
|
+
}
|
85
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
// modules
|
5
|
+
import {
|
6
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
7
|
+
} from "../LSP8IdentifiableDigitalAssetInitAbstract.sol";
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @dev LSP8 token extension to add a max token supply cap.
|
11
|
+
*/
|
12
|
+
abstract contract LSP8CappedSupplyInitAbstract is
|
13
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
14
|
+
{
|
15
|
+
// --- Errors
|
16
|
+
|
17
|
+
/**
|
18
|
+
* @notice The `tokenSupplyCap` must be set and cannot be `0`.
|
19
|
+
* @dev Reverts when setting `0` for the {tokenSupplyCap}. The max token supply MUST be set to a number greater than 0.
|
20
|
+
*/
|
21
|
+
error LSP8CappedSupplyRequired();
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @notice Cannot mint anymore as total supply reached the maximum cap.
|
25
|
+
* @dev Reverts when trying to mint tokens but the {totalSupply} has reached the maximum {tokenSupplyCap}.
|
26
|
+
*/
|
27
|
+
error LSP8CappedSupplyCannotMintOverCap();
|
28
|
+
|
29
|
+
// --- Storage
|
30
|
+
uint256 private _tokenSupplyCap;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @dev Initialize a `LSP8CappedSupplyInitAbstract` token contract and set the maximum token supply token cap up to which
|
34
|
+
* it is not possible to mint more tokens.
|
35
|
+
*
|
36
|
+
* @param tokenSupplyCap_ The maximum token supply.
|
37
|
+
*
|
38
|
+
* @custom:requirements
|
39
|
+
* - `tokenSupplyCap_` MUST NOT be 0.
|
40
|
+
*/
|
41
|
+
function _initialize(
|
42
|
+
uint256 tokenSupplyCap_
|
43
|
+
) internal virtual onlyInitializing {
|
44
|
+
if (tokenSupplyCap_ == 0) {
|
45
|
+
revert LSP8CappedSupplyRequired();
|
46
|
+
}
|
47
|
+
|
48
|
+
_tokenSupplyCap = tokenSupplyCap_;
|
49
|
+
}
|
50
|
+
|
51
|
+
// --- Token queries
|
52
|
+
|
53
|
+
/**
|
54
|
+
* @notice The maximum supply amount of tokens allowed to exist is `_tokenSupplyCap`.
|
55
|
+
*
|
56
|
+
* @dev Get the maximum number of tokens that can exist to circulate. Once {totalSupply} reaches
|
57
|
+
* reaches {totalSuuplyCap}, it is not possible to mint more tokens.
|
58
|
+
*
|
59
|
+
* @return The maximum number of tokens that can exist in the contract.
|
60
|
+
*/
|
61
|
+
function tokenSupplyCap() public view virtual returns (uint256) {
|
62
|
+
return _tokenSupplyCap;
|
63
|
+
}
|
64
|
+
|
65
|
+
// --- Transfer functionality
|
66
|
+
|
67
|
+
/**
|
68
|
+
* @dev Same as {_mint} but allows to mint only if newly minted `tokenId` does not increase the {totalSupply}
|
69
|
+
* over the {tokenSupplyCap}.
|
70
|
+
* after a new `tokenId` has of tokens have been minted.
|
71
|
+
*
|
72
|
+
* @custom:requirements
|
73
|
+
* - {totalSupply} + 1 must not be greater than the {tokenSupplyCap} when calling this function.
|
74
|
+
* - `to` cannot be the zero address.
|
75
|
+
*/
|
76
|
+
function _mint(
|
77
|
+
address to,
|
78
|
+
bytes32 tokenId,
|
79
|
+
bool force,
|
80
|
+
bytes memory data
|
81
|
+
) internal virtual override {
|
82
|
+
if (totalSupply() + 1 > tokenSupplyCap()) {
|
83
|
+
revert LSP8CappedSupplyCannotMintOverCap();
|
84
|
+
}
|
85
|
+
|
86
|
+
super._mint(to, tokenId, force, data);
|
87
|
+
}
|
88
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
// modules
|
5
|
+
import {
|
6
|
+
LSP8IdentifiableDigitalAsset,
|
7
|
+
LSP8IdentifiableDigitalAssetCore
|
8
|
+
} from "../LSP8IdentifiableDigitalAsset.sol";
|
9
|
+
|
10
|
+
/**
|
11
|
+
* @dev LSP8 extension that enables to enumerate over all the `tokenIds` ever minted.
|
12
|
+
*
|
13
|
+
* @custom:warning Note that in this extension contract, any `tokenId` that been burnt is removed from the set of the enumerable tokenIds.
|
14
|
+
*/
|
15
|
+
abstract contract LSP8Enumerable is LSP8IdentifiableDigitalAsset {
|
16
|
+
// Mapping from token index to token id
|
17
|
+
mapping(uint256 => bytes32) private _indexToken;
|
18
|
+
|
19
|
+
// Mapping from token id to index
|
20
|
+
mapping(bytes32 => uint256) private _tokenIndex;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @notice Retrieving the `tokenId` for `msg.sender` located in its list at index number `index`.
|
24
|
+
*
|
25
|
+
* @dev Returns a token id at index. See {totalSupply} to get total number of minted tokens.
|
26
|
+
* @param index The index to search to search in the enumerable mapping.
|
27
|
+
* @return TokenId or `bytes32(0)` if no tokenId exist at `index`.
|
28
|
+
*/
|
29
|
+
function tokenAt(uint256 index) public view returns (bytes32) {
|
30
|
+
return _indexToken[index];
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* @inheritdoc LSP8IdentifiableDigitalAssetCore
|
35
|
+
*
|
36
|
+
* @param from The address sending the `tokenId` (`address(0)` when `tokenId` is being minted).
|
37
|
+
* @param to The address receiving the `tokenId` (`address(0)` when `tokenId` is being burnt).
|
38
|
+
* @param tokenId The bytes32 identifier of the token being transferred.
|
39
|
+
* @param data The data sent alongside the the token transfer.
|
40
|
+
*/
|
41
|
+
function _beforeTokenTransfer(
|
42
|
+
address from,
|
43
|
+
address to,
|
44
|
+
bytes32 tokenId,
|
45
|
+
bytes memory data
|
46
|
+
) internal virtual override(LSP8IdentifiableDigitalAssetCore) {
|
47
|
+
// `tokenId` being minted
|
48
|
+
if (from == address(0)) {
|
49
|
+
uint256 index = totalSupply();
|
50
|
+
_indexToken[index] = tokenId;
|
51
|
+
_tokenIndex[tokenId] = index;
|
52
|
+
}
|
53
|
+
|
54
|
+
// `tokenId` being burnt
|
55
|
+
if (to == address(0)) {
|
56
|
+
uint256 lastIndex = totalSupply() - 1;
|
57
|
+
uint256 index = _tokenIndex[tokenId];
|
58
|
+
if (index < lastIndex) {
|
59
|
+
bytes32 lastTokenId = _indexToken[lastIndex];
|
60
|
+
_indexToken[index] = lastTokenId;
|
61
|
+
_tokenIndex[lastTokenId] = index;
|
62
|
+
}
|
63
|
+
delete _indexToken[lastIndex];
|
64
|
+
delete _tokenIndex[tokenId];
|
65
|
+
}
|
66
|
+
|
67
|
+
super._beforeTokenTransfer(from, to, tokenId, data);
|
68
|
+
}
|
69
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.12;
|
3
|
+
|
4
|
+
// modules
|
5
|
+
import {
|
6
|
+
LSP8IdentifiableDigitalAssetInitAbstract,
|
7
|
+
LSP8IdentifiableDigitalAssetCore
|
8
|
+
} from "../LSP8IdentifiableDigitalAssetInitAbstract.sol";
|
9
|
+
|
10
|
+
/**
|
11
|
+
* @dev LSP8 extension.
|
12
|
+
*/
|
13
|
+
abstract contract LSP8EnumerableInitAbstract is
|
14
|
+
LSP8IdentifiableDigitalAssetInitAbstract
|
15
|
+
{
|
16
|
+
// Mapping from token index to token id
|
17
|
+
mapping(uint256 => bytes32) private _indexToken;
|
18
|
+
|
19
|
+
// Mapping from token id to index
|
20
|
+
mapping(bytes32 => uint256) private _tokenIndex;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @notice Retrieving the `tokenId` for `msg.sender` located in its list at index number `index`.
|
24
|
+
*
|
25
|
+
* @dev Returns a token id at index. See {totalSupply} to get total number of minted tokens.
|
26
|
+
* @param index The index to search to search in the enumerable mapping.
|
27
|
+
* @return TokenId or `bytes32(0)` if no tokenId exist at `index`.
|
28
|
+
*/
|
29
|
+
function tokenAt(uint256 index) public view returns (bytes32) {
|
30
|
+
return _indexToken[index];
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* @inheritdoc LSP8IdentifiableDigitalAssetCore
|
35
|
+
*
|
36
|
+
* @param from The address sending the `tokenId` (`address(0)` when `tokenId` is being minted).
|
37
|
+
* @param to The address receiving the `tokenId` (`address(0)` when `tokenId` is being burnt).
|
38
|
+
* @param tokenId The bytes32 identifier of the token being transferred.
|
39
|
+
* @param data The data sent alongside the the token transfer.
|
40
|
+
*/
|
41
|
+
function _beforeTokenTransfer(
|
42
|
+
address from,
|
43
|
+
address to,
|
44
|
+
bytes32 tokenId,
|
45
|
+
bytes memory data
|
46
|
+
) internal virtual override(LSP8IdentifiableDigitalAssetCore) {
|
47
|
+
if (from == address(0)) {
|
48
|
+
uint256 index = totalSupply();
|
49
|
+
_indexToken[index] = tokenId;
|
50
|
+
_tokenIndex[tokenId] = index;
|
51
|
+
} else if (to == address(0)) {
|
52
|
+
uint256 lastIndex = totalSupply() - 1;
|
53
|
+
uint256 index = _tokenIndex[tokenId];
|
54
|
+
if (index < lastIndex) {
|
55
|
+
bytes32 lastTokenId = _indexToken[lastIndex];
|
56
|
+
_indexToken[index] = lastTokenId;
|
57
|
+
_tokenIndex[lastTokenId] = index;
|
58
|
+
}
|
59
|
+
delete _indexToken[lastIndex];
|
60
|
+
delete _tokenIndex[tokenId];
|
61
|
+
}
|
62
|
+
super._beforeTokenTransfer(from, to, tokenId, data);
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
2
|
+
pragma solidity ^0.8.4;
|
3
|
+
|
4
|
+
// interfaces
|
5
|
+
import {
|
6
|
+
ILSP8IdentifiableDigitalAsset
|
7
|
+
} from "../ILSP8IdentifiableDigitalAsset.sol";
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @dev LSP8 extension, mintable.
|
11
|
+
*/
|
12
|
+
interface ILSP8Mintable is ILSP8IdentifiableDigitalAsset {
|
13
|
+
/**
|
14
|
+
* @param to The address to mint tokens
|
15
|
+
* @param tokenId The tokenId to mint
|
16
|
+
* @param force When set to TRUE, to may be any address but
|
17
|
+
* when set to FALSE to must be a contract that supports LSP1 UniversalReceiver
|
18
|
+
* @param data Additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses.
|
19
|
+
* @dev Mints `amount` tokens and transfers it to `to`.
|
20
|
+
*
|
21
|
+
* Requirements:
|
22
|
+
*
|
23
|
+
* - `to` cannot be the zero address.
|
24
|
+
*
|
25
|
+
* Emits a {Transfer} event.
|
26
|
+
*/
|
27
|
+
function mint(
|
28
|
+
address to,
|
29
|
+
bytes32 tokenId,
|
30
|
+
bool force,
|
31
|
+
bytes memory data
|
32
|
+
) external;
|
33
|
+
}
|