@artblocks/contracts 1.0.4 → 1.1.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 +2 -2
- package/artifacts/contracts/AdminACLV1.sol/AdminACLV1.json +2 -2
- package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +97 -10
- package/artifacts/contracts/GenArt721CoreV3.sol/GenArt721CoreV3.json +4 -4
- package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine.sol/GenArt721CoreV3_Engine.json +4 -9
- package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +4 -9
- package/artifacts/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol/GenArt721CoreV3_Engine_Flex_PROOF.json +4 -9
- package/artifacts/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol/GenArt721CoreV3_Engine_Flex_PROHIBITION.json +4 -9
- package/artifacts/contracts/engine-registry/CoreRegistryV1.sol/CoreRegistryV1.json +12 -12
- package/artifacts/contracts/explorations/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +4 -4
- package/artifacts/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol/ICoreRegistryV1.json +6 -6
- package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +19 -0
- package/artifacts/contracts/interfaces/v0.8.x/IMinterFilterV1.sol/IMinterFilterV1.json +22 -22
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol/ISharedMinterDAExpSettlementV0.json +8 -101
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol/ISharedMinterDAExpV0.json +1 -57
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol/ISharedMinterDALinV0.json +1 -57
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol/ISharedMinterDAV0.json +2 -21
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol/ISharedMinterHolderERC20V0.json +158 -0
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol/ISharedMinterHolderV0.json +15 -90
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol/ISharedMinterMerkleV0.json +10 -36
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol/ISharedMinterSEAV0.json +4 -236
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol/ISharedMinterSimplePurchaseV0.json +5 -5
- package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterV0.sol/ISharedMinterV0.json +7 -423
- package/artifacts/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol/ISharedRandomizerV0.json +17 -17
- package/artifacts/contracts/libs/v0.8.x/ABHelpers.sol/ABHelpers.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/AuthLib.sol/AuthLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/Bytes32Strings.sol/Bytes32Strings.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol/DAExpLib.json +60 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/DALib.sol/DALib.json +30 -0
- package/artifacts/contracts/libs/v0.8.x/minter-libs/DALinLib.sol/DALinLib.json +60 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol/GenericMinterEventsLib.json +346 -0
- package/artifacts/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol/MaxInvocationsLib.json +29 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol/MerkleLib.json +30 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol/PolyptychLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SEALib.sol/SEALib.json +254 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol/SetPriceLib.json +48 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol/SettlementExpLib.json +41 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol/SplitFundsLib.json +35 -3
- package/artifacts/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol/TokenHolderLib.json +79 -3
- package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV1.sol/MinterFilterV1.json +2 -2
- package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV2.sol/MinterFilterV2.json +41 -41
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol/MinterDAExpHolderV5.json +66 -632
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +2 -20
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol/MinterDAExpSettlementV3.json +67 -594
- package/artifacts/contracts/minter-suite/Minters/MinterDAExpV5.sol/MinterDAExpV5.json +33 -524
- package/artifacts/contracts/minter-suite/Minters/MinterDALinHolderV5.sol/MinterDALinHolderV5.json +66 -632
- package/artifacts/contracts/minter-suite/Minters/MinterDALinV5.sol/MinterDALinV5.json +33 -524
- package/artifacts/contracts/minter-suite/Minters/MinterSEAV1.sol/MinterSEAV1.json +51 -717
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol/MinterSetPriceERC20V5.json +58 -454
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol/MinterSetPriceHolderV5.json +61 -552
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol/MinterSetPriceMerkleV5.json +55 -497
- package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol/MinterSetPricePolyptychERC20V5.json +110 -571
- package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol/MinterSetPricePolyptychV5.json +69 -560
- package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV5.sol/MinterSetPriceV5.json +28 -444
- package/artifacts/contracts/mock/DummySharedMinter.sol/DummySharedMinter.json +2 -2
- package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +4 -9
- package/artifacts/contracts/mock/OriginRegisterMock.sol/OriginRegisterMock.json +2 -2
- package/artifacts/contracts/mock/ReentrancyHolderMockShared.sol/ReentrancyHolderMockShared.json +136 -0
- package/artifacts/contracts/mock/ReentrancyMerkleMockShared.sol/ReentrancyMerkleMockShared.json +105 -0
- package/artifacts/contracts/mock/ReentrancyMockShared.sol/ReentrancyMockShared.json +100 -0
- package/artifacts/contracts/mock/ReentrancySEAMockShared.sol/ReentrancySEAAutoBidderMockShared.json +69 -0
- package/artifacts/contracts/randomizer/shared/SharedRandomizerV0.sol/SharedRandomizerV0.json +19 -19
- package/contracts/DependencyRegistryV0.sol +84 -16
- package/contracts/archive/readme.md +16 -0
- package/contracts/engine/README.md +21 -0
- package/contracts/engine/V3/GenArt721CoreV3_Engine.sol +4 -10
- package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +4 -10
- package/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol +4 -10
- package/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol +4 -10
- package/contracts/engine-registry/CoreRegistryV1.sol +58 -65
- package/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol +6 -8
- package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +3 -0
- package/contracts/interfaces/v0.8.x/IMinterFilterV1.sol +25 -25
- package/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol +11 -20
- package/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol +1 -14
- package/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol +1 -15
- package/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol +2 -8
- package/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol +47 -0
- package/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol +15 -43
- package/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol +10 -20
- package/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol +4 -67
- package/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol +5 -5
- package/contracts/interfaces/v0.8.x/ISharedMinterV0.sol +7 -155
- package/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol +37 -37
- package/contracts/libs/v0.8.x/ABHelpers.sol +14 -14
- package/contracts/libs/v0.8.x/AuthLib.sol +101 -102
- package/contracts/libs/v0.8.x/Bytes32Strings.sol +5 -5
- package/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol +163 -38
- package/contracts/libs/v0.8.x/minter-libs/DALib.sol +26 -0
- package/contracts/libs/v0.8.x/minter-libs/DALinLib.sol +144 -37
- package/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol +171 -0
- package/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol +250 -110
- package/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol +257 -48
- package/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol +138 -58
- package/contracts/libs/v0.8.x/minter-libs/SEALib.sol +778 -103
- package/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol +135 -13
- package/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol +567 -187
- package/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol +480 -322
- package/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol +214 -76
- package/contracts/minter-suite/MinterFilter/MinterFilterV2.sol +347 -304
- package/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol +391 -491
- package/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol +1 -54
- package/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol +388 -628
- package/contracts/minter-suite/Minters/MinterDAExpV5.sol +212 -273
- package/contracts/minter-suite/Minters/MinterDALinHolderV5.sol +393 -488
- package/contracts/minter-suite/Minters/MinterDALinV5.sol +220 -277
- package/contracts/minter-suite/Minters/MinterSEAV1.sol +327 -664
- package/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol +278 -303
- package/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol +355 -446
- package/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol +328 -416
- package/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol +538 -622
- package/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol +429 -553
- package/contracts/minter-suite/Minters/MinterSetPriceV5.sol +180 -227
- package/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol +3 -10
- package/contracts/mock/ReentrancyHolderMockShared.sol +59 -0
- package/contracts/mock/ReentrancyMerkleMockShared.sol +58 -0
- package/contracts/mock/ReentrancyMockShared.sol +48 -0
- package/contracts/mock/ReentrancySEAMockShared.sol +40 -0
- package/contracts/randomizer/shared/SharedRandomizerV0.sol +78 -70
- package/package.json +39 -33
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.0;
|
|
5
5
|
|
|
6
|
-
import "
|
|
6
|
+
import {IMinterFilterV1} from "../../../interfaces/v0.8.x/IMinterFilterV1.sol";
|
|
7
|
+
|
|
8
|
+
import {ABHelpers} from "../ABHelpers.sol";
|
|
9
|
+
import {SplitFundsLib} from "./SplitFundsLib.sol";
|
|
10
|
+
import {MaxInvocationsLib} from "./MaxInvocationsLib.sol";
|
|
11
|
+
|
|
12
|
+
import {IERC721} from "@openzeppelin-4.7/contracts/token/ERC721/IERC721.sol";
|
|
13
|
+
import {SafeCast} from "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
16
|
* @title Art Blocks SEA Minter Library
|
|
@@ -14,6 +21,135 @@ import "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";
|
|
|
14
21
|
|
|
15
22
|
library SEALib {
|
|
16
23
|
using SafeCast for uint256;
|
|
24
|
+
/**
|
|
25
|
+
* @notice Minimum auction length, in seconds, was updated to be the
|
|
26
|
+
* provided value.
|
|
27
|
+
* @param minAuctionDurationSeconds Minimum auction length, in seconds
|
|
28
|
+
*/
|
|
29
|
+
event MinAuctionDurationSecondsUpdated(uint256 minAuctionDurationSeconds);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @notice Admin-controlled time buffer updated
|
|
33
|
+
* @param minterTimeBufferSeconds Time buffer, in seconds
|
|
34
|
+
*/
|
|
35
|
+
event MinterTimeBufferUpdated(uint32 minterTimeBufferSeconds);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @notice Admin-controlled refund gas limit updated
|
|
39
|
+
* @param refundGasLimit Gas limit to use when refunding the previous
|
|
40
|
+
* highest bidder, prior to using fallback force-send to refund
|
|
41
|
+
*/
|
|
42
|
+
event MinterRefundGasLimitUpdated(uint24 refundGasLimit);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @notice Future auctions for project `projectId` on core contract
|
|
46
|
+
* `coreContract` configured.
|
|
47
|
+
* @param projectId Project Id configured
|
|
48
|
+
* @param coreContract Core contract address configured
|
|
49
|
+
* @param timestampStart Start timestamp after which future auctions may be
|
|
50
|
+
* started. If zero, auctions can start immediately.
|
|
51
|
+
* @param auctionDurationSeconds Duration of each new auction, before any
|
|
52
|
+
* extensions due to late bids
|
|
53
|
+
* @param basePrice Minimum bid price for the token auctions
|
|
54
|
+
* @param minBidIncrementPercentage Minimum bid increment percentage. Each
|
|
55
|
+
* subsequent bid must be at least this percentage greater than the
|
|
56
|
+
* previous bid. The value is expressed as a whole percentage, e.g. 5% is
|
|
57
|
+
* 5, 10% is 10, etc.
|
|
58
|
+
*/
|
|
59
|
+
event ConfiguredFutureAuctions(
|
|
60
|
+
uint256 indexed projectId,
|
|
61
|
+
address indexed coreContract,
|
|
62
|
+
uint64 timestampStart,
|
|
63
|
+
uint32 auctionDurationSeconds,
|
|
64
|
+
uint256 basePrice,
|
|
65
|
+
uint8 minBidIncrementPercentage
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @notice New token auction created, token created and sent to minter
|
|
70
|
+
* @param tokenId Token Id for which auction was created
|
|
71
|
+
* @param coreContract Core contract address for the given token
|
|
72
|
+
* @param bidder Bidder's address who initialized the auction
|
|
73
|
+
* @param bidAmount Bid amount
|
|
74
|
+
* @param endTime Auction end time
|
|
75
|
+
* @param minBidIncrementPercentage Minimum bid increment percentage. Each
|
|
76
|
+
* subsequent bid must be at least this percentage greater than the
|
|
77
|
+
* previous bid. The value is expressed as a whole percentage, e.g. 5% is
|
|
78
|
+
* 5, 10% is 10, etc.
|
|
79
|
+
*/
|
|
80
|
+
event AuctionInitialized(
|
|
81
|
+
uint256 indexed tokenId,
|
|
82
|
+
address indexed coreContract,
|
|
83
|
+
address indexed bidder,
|
|
84
|
+
uint256 bidAmount,
|
|
85
|
+
uint64 endTime,
|
|
86
|
+
uint8 minBidIncrementPercentage
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @notice Successful bid placed on token auction
|
|
91
|
+
* @param tokenId Token Id of auction bid on
|
|
92
|
+
* @param coreContract Core contract address for the given token
|
|
93
|
+
* @param bidder Bidder's address who placed the bid
|
|
94
|
+
* @param bidAmount Bid amount
|
|
95
|
+
*/
|
|
96
|
+
event AuctionBid(
|
|
97
|
+
uint256 indexed tokenId,
|
|
98
|
+
address indexed coreContract,
|
|
99
|
+
address indexed bidder,
|
|
100
|
+
uint256 bidAmount
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @notice Token auction was settled (token distributed to winner)
|
|
105
|
+
* @param tokenId Token Id of auction settled
|
|
106
|
+
* @param coreContract Core contract address for the given token
|
|
107
|
+
* @param winner Winner's address
|
|
108
|
+
* @param price Winning bid amount
|
|
109
|
+
*/
|
|
110
|
+
event AuctionSettled(
|
|
111
|
+
uint256 indexed tokenId,
|
|
112
|
+
address indexed coreContract,
|
|
113
|
+
address indexed winner,
|
|
114
|
+
uint256 price
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @notice Future auction details for project `projectId` reset
|
|
119
|
+
* @param projectId Project Id that was reset
|
|
120
|
+
* @param coreContract Core contract address that was reset
|
|
121
|
+
*/
|
|
122
|
+
event ResetAuctionDetails(
|
|
123
|
+
uint256 indexed projectId,
|
|
124
|
+
address indexed coreContract
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @notice Next token ID for project `projectId` updated
|
|
129
|
+
* @param projectId Project Id of next token updated
|
|
130
|
+
* @param coreContract Core contract address of next token updated
|
|
131
|
+
* @param tokenId Token Id of next token updated
|
|
132
|
+
*/
|
|
133
|
+
event ProjectNextTokenUpdated(
|
|
134
|
+
uint256 indexed projectId,
|
|
135
|
+
address indexed coreContract,
|
|
136
|
+
uint256 tokenId
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @notice Next token ID for project `projectId` ejected from the minter
|
|
141
|
+
* and is no longer populated
|
|
142
|
+
* @param projectId Project Id of next token ejected
|
|
143
|
+
* @param coreContract Core contract address of next token ejected
|
|
144
|
+
*/
|
|
145
|
+
event ProjectNextTokenEjected(
|
|
146
|
+
uint256 indexed projectId,
|
|
147
|
+
address indexed coreContract
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// position of SEA Lib storage, using a diamond storage pattern
|
|
151
|
+
// for this library
|
|
152
|
+
bytes32 constant SEA_LIB_STORAGE_POSITION = keccak256("sealib.storage");
|
|
17
153
|
|
|
18
154
|
uint256 constant ONE_MILLION = 1_000_000;
|
|
19
155
|
|
|
@@ -67,93 +203,436 @@ library SEALib {
|
|
|
67
203
|
bool settled;
|
|
68
204
|
}
|
|
69
205
|
|
|
206
|
+
// Diamond storage pattern is used in this library
|
|
207
|
+
struct SEALibStorage {
|
|
208
|
+
mapping(address coreContract => mapping(uint256 projectId => SEAProjectConfig)) SEAProjectConfigs;
|
|
209
|
+
}
|
|
210
|
+
|
|
70
211
|
/**
|
|
71
212
|
* @notice Updates the SEAProjectConfig to clear the current settings for
|
|
72
213
|
* future auctions on the project. Values are reset to their default
|
|
73
214
|
* initial values.
|
|
74
215
|
* Current auction details are not cleared or affected.
|
|
75
|
-
* @param
|
|
216
|
+
* @param projectId Project ID to reset
|
|
217
|
+
* @param coreContract Core contract address to reset
|
|
76
218
|
*/
|
|
77
219
|
function resetFutureAuctionDetails(
|
|
78
|
-
|
|
220
|
+
uint256 projectId,
|
|
221
|
+
address coreContract
|
|
79
222
|
) internal {
|
|
223
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
224
|
+
projectId: projectId,
|
|
225
|
+
coreContract: coreContract
|
|
226
|
+
});
|
|
80
227
|
// reset future auction details
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
228
|
+
SEAProjectConfig_.auctionDurationSeconds = 0;
|
|
229
|
+
SEAProjectConfig_.minBidIncrementPercentage = 0;
|
|
230
|
+
SEAProjectConfig_.basePrice = 0;
|
|
231
|
+
SEAProjectConfig_.timestampStart = 0;
|
|
232
|
+
|
|
233
|
+
emit ResetAuctionDetails({
|
|
234
|
+
projectId: projectId,
|
|
235
|
+
coreContract: coreContract
|
|
236
|
+
});
|
|
85
237
|
}
|
|
86
238
|
|
|
87
239
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @param
|
|
92
|
-
* @param
|
|
93
|
-
* @param
|
|
94
|
-
*
|
|
95
|
-
* @
|
|
240
|
+
* @notice Configures future auctions for project `projectId` on core
|
|
241
|
+
* contract `coreContract`.
|
|
242
|
+
* Reverts if `timestampStart` is in the past and not zero.
|
|
243
|
+
* @param projectId Project ID to configure
|
|
244
|
+
* @param coreContract Core contract address to configure
|
|
245
|
+
* @param timestampStart Timestamp to start future auctions at. If zero,
|
|
246
|
+
* auctions can start immediately.
|
|
247
|
+
* @param auctionDurationSeconds Duration of each new auction, before any
|
|
248
|
+
* extensions due to late bids
|
|
249
|
+
* @param basePrice Minimum bid price for the token auctions
|
|
250
|
+
* @param minBidIncrementPercentage Minimum bid increment percentage. Each
|
|
251
|
+
* subsequent bid must be at least this percentage greater than the
|
|
252
|
+
* previous bid. The value is expressed as a whole percentage, e.g. 5% is
|
|
253
|
+
* 5, 10% is 10, etc.
|
|
96
254
|
*/
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
uint256
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
255
|
+
function configureFutureAuctions(
|
|
256
|
+
uint256 projectId,
|
|
257
|
+
address coreContract,
|
|
258
|
+
uint256 timestampStart,
|
|
259
|
+
uint256 auctionDurationSeconds,
|
|
260
|
+
uint256 basePrice,
|
|
261
|
+
uint8 minBidIncrementPercentage
|
|
262
|
+
) internal {
|
|
263
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
264
|
+
projectId: projectId,
|
|
265
|
+
coreContract: coreContract
|
|
266
|
+
});
|
|
267
|
+
require(
|
|
268
|
+
timestampStart == 0 || block.timestamp < timestampStart,
|
|
269
|
+
"Only future start times or 0"
|
|
270
|
+
);
|
|
271
|
+
// EFFECTS
|
|
272
|
+
SEAProjectConfig_.timestampStart = timestampStart.toUint64(); // @dev rely on this safe-cast check in event emit below
|
|
273
|
+
SEAProjectConfig_.auctionDurationSeconds = auctionDurationSeconds
|
|
274
|
+
.toUint32();
|
|
275
|
+
SEAProjectConfig_.basePrice = basePrice;
|
|
276
|
+
SEAProjectConfig_.minBidIncrementPercentage = minBidIncrementPercentage;
|
|
277
|
+
|
|
278
|
+
emit ConfiguredFutureAuctions({
|
|
279
|
+
projectId: projectId,
|
|
280
|
+
coreContract: coreContract,
|
|
281
|
+
timestampStart: uint64(timestampStart), // @dev safe-casting already previously checked
|
|
282
|
+
auctionDurationSeconds: auctionDurationSeconds.toUint32(),
|
|
283
|
+
basePrice: basePrice,
|
|
284
|
+
minBidIncrementPercentage: minBidIncrementPercentage
|
|
115
285
|
});
|
|
116
|
-
return endTime;
|
|
117
286
|
}
|
|
118
287
|
|
|
119
288
|
/**
|
|
120
289
|
* @notice Updates the bid state of an Auction.
|
|
121
290
|
* Note that the auction's end time is extended if the bid is placed within
|
|
122
|
-
* the last `
|
|
291
|
+
* the last `timeBufferSeconds` of the auction.
|
|
123
292
|
* NOTE: This function does not check if the bid is valid. It is assumed
|
|
124
293
|
* that the bid has already been checked for validity by the caller.
|
|
125
|
-
* @param
|
|
126
|
-
* @param
|
|
127
|
-
* bid is placed within the last `
|
|
128
|
-
* @param
|
|
129
|
-
* @param
|
|
294
|
+
* @param auction Auction to update
|
|
295
|
+
* @param timeBufferSeconds Time buffer to extend the auction to if the
|
|
296
|
+
* bid is placed within the last `timeBufferSeconds` of the auction.
|
|
297
|
+
* @param bidAmount bid amount
|
|
298
|
+
* @param bidder bidder's payable address
|
|
130
299
|
*/
|
|
131
300
|
function auctionUpdateBid(
|
|
132
|
-
Auction storage
|
|
133
|
-
uint256
|
|
134
|
-
uint256
|
|
135
|
-
address payable
|
|
301
|
+
Auction storage auction,
|
|
302
|
+
uint256 timeBufferSeconds,
|
|
303
|
+
uint256 bidAmount,
|
|
304
|
+
address payable bidder
|
|
136
305
|
) internal {
|
|
137
306
|
// update auction state
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
uint256 minEndTime = block.timestamp +
|
|
141
|
-
if (
|
|
142
|
-
|
|
307
|
+
auction.currentBid = bidAmount;
|
|
308
|
+
auction.currentBidder = bidder;
|
|
309
|
+
uint256 minEndTime = block.timestamp + timeBufferSeconds;
|
|
310
|
+
if (auction.endTime < minEndTime) {
|
|
311
|
+
auction.endTime = minEndTime.toUint64();
|
|
143
312
|
}
|
|
144
313
|
}
|
|
145
314
|
|
|
315
|
+
/**
|
|
316
|
+
* @notice Ejects a project's "next token" from the minter and sends it to
|
|
317
|
+
* the input `to` address.
|
|
318
|
+
* This function is only intended for use in the edge case where the minter
|
|
319
|
+
* has a "next token" assigned to a project, but the project has been reset
|
|
320
|
+
* via `resetAuctionDetails`, and the artist does not want an auction to be
|
|
321
|
+
* started for the "next token". This function also protects against the
|
|
322
|
+
* unforseen case where the minter is in an unexpected state where it has a
|
|
323
|
+
* "next token" assigned to a project, but for some reason the project is
|
|
324
|
+
* unable to begin a new auction due to a bug.
|
|
325
|
+
* @param projectId The project ID being ejected
|
|
326
|
+
* @param coreContract The core contract address being ejected
|
|
327
|
+
* @param to The address to send the ejected token to
|
|
328
|
+
*/
|
|
329
|
+
function ejectNextTokenTo(
|
|
330
|
+
uint256 projectId,
|
|
331
|
+
address coreContract,
|
|
332
|
+
address to
|
|
333
|
+
) internal {
|
|
334
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
335
|
+
projectId: projectId,
|
|
336
|
+
coreContract: coreContract
|
|
337
|
+
});
|
|
338
|
+
// CHECKS
|
|
339
|
+
// only if project is not configured (i.e. artist called
|
|
340
|
+
// `resetAuctionDetails`)
|
|
341
|
+
require(
|
|
342
|
+
!projectIsConfigured({
|
|
343
|
+
projectId: projectId,
|
|
344
|
+
coreContract: coreContract
|
|
345
|
+
}),
|
|
346
|
+
"Only unconfigured projects"
|
|
347
|
+
);
|
|
348
|
+
// only if project has a next token assigned
|
|
349
|
+
require(
|
|
350
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated == true,
|
|
351
|
+
"No next token"
|
|
352
|
+
);
|
|
353
|
+
// EFFECTS
|
|
354
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated = false;
|
|
355
|
+
// INTERACTIONS
|
|
356
|
+
// @dev overflow automatically handled by Sol ^0.8.0
|
|
357
|
+
uint256 nextTokenId = ABHelpers.tokenIdFromProjectIdAndTokenNumber({
|
|
358
|
+
projectId: projectId,
|
|
359
|
+
tokenNumber: SEAProjectConfig_.nextTokenNumber
|
|
360
|
+
});
|
|
361
|
+
IERC721(coreContract).transferFrom({
|
|
362
|
+
from: address(this),
|
|
363
|
+
to: to,
|
|
364
|
+
tokenId: nextTokenId
|
|
365
|
+
});
|
|
366
|
+
emit ProjectNextTokenEjected({
|
|
367
|
+
projectId: projectId,
|
|
368
|
+
coreContract: coreContract
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* @notice Settles a completed auction for `tokenId`, if it exists and is
|
|
374
|
+
* not yet settled.
|
|
375
|
+
* Returns early (does not modify state) if
|
|
376
|
+
* - there is no initialized auction for the project
|
|
377
|
+
* - there is an auction that has already been settled for the project
|
|
378
|
+
* - there is an auction for a different token ID on the project
|
|
379
|
+
* (likely due to a front-run)
|
|
380
|
+
* This function reverts if the auction for `tokenId` exists, but has not
|
|
381
|
+
* yet ended.
|
|
382
|
+
* @param tokenId Token ID to settle auction for.
|
|
383
|
+
* @param coreContract Core contract address for the given token.
|
|
384
|
+
*/
|
|
385
|
+
function settleAuction(uint256 tokenId, address coreContract) internal {
|
|
386
|
+
uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
|
|
387
|
+
SEAProjectConfig storage _SEAProjectConfig = getSEAProjectConfig({
|
|
388
|
+
projectId: projectId,
|
|
389
|
+
coreContract: coreContract
|
|
390
|
+
});
|
|
391
|
+
Auction storage auction = _SEAProjectConfig.activeAuction;
|
|
392
|
+
// load from storage to memory for gas efficiency
|
|
393
|
+
address currentBidder = auction.currentBidder;
|
|
394
|
+
uint256 currentBid = auction.currentBid;
|
|
395
|
+
// CHECKS
|
|
396
|
+
// @dev this check is not strictly necessary, but is included for
|
|
397
|
+
// clear error messaging
|
|
398
|
+
require(auctionIsInitialized(auction), "Auction not initialized");
|
|
399
|
+
if (auction.settled || (auction.tokenId != tokenId)) {
|
|
400
|
+
// auction already settled or is for a different token ID, so
|
|
401
|
+
// return early and do not modify state
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
// @dev important that the following check is after the early return
|
|
405
|
+
// block above to maintain desired behavior
|
|
406
|
+
require(block.timestamp >= auction.endTime, "Auction not yet ended");
|
|
407
|
+
// EFFECTS
|
|
408
|
+
auction.settled = true;
|
|
409
|
+
// INTERACTIONS
|
|
410
|
+
// send token to the winning bidder
|
|
411
|
+
IERC721(coreContract).transferFrom({
|
|
412
|
+
from: address(this),
|
|
413
|
+
to: currentBidder,
|
|
414
|
+
tokenId: tokenId
|
|
415
|
+
});
|
|
416
|
+
// distribute revenues from auction
|
|
417
|
+
SplitFundsLib.splitRevenuesETHNoRefund({
|
|
418
|
+
projectId: projectId,
|
|
419
|
+
valueInWei: currentBid,
|
|
420
|
+
coreContract: coreContract
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
emit AuctionSettled({
|
|
424
|
+
tokenId: tokenId,
|
|
425
|
+
coreContract: coreContract,
|
|
426
|
+
winner: currentBidder,
|
|
427
|
+
price: currentBid
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @notice Enters a bid for token `tokenId`.
|
|
433
|
+
* If an auction for token `tokenId` does not exist, an auction will be
|
|
434
|
+
* initialized as long as any existing auction for the project has been
|
|
435
|
+
* settled.
|
|
436
|
+
* In order to successfully place the bid, the token bid must be:
|
|
437
|
+
* - greater than or equal to a project's minimum bid price if a new
|
|
438
|
+
* auction is initialized
|
|
439
|
+
* - sufficiently greater than the current highest bid, according to the
|
|
440
|
+
* minter's bid increment percentage `minterMinBidIncrementPercentage`,
|
|
441
|
+
* if an auction for the token already exists
|
|
442
|
+
* If the bid is unsuccessful, the transaction will revert.
|
|
443
|
+
* If the bid is successful, but outbid by another bid before the auction
|
|
444
|
+
* ends, the funds will be noncustodially returned to the bidder's address,
|
|
445
|
+
* `msg.sender`. A fallback method of sending funds back to the bidder via
|
|
446
|
+
* SELFDESTRUCT (SENDALL) prevents denial of service attacks, even if the
|
|
447
|
+
* original bidder reverts or runs out of gas during receive or fallback.
|
|
448
|
+
* ------------------------------------------------------------------------
|
|
449
|
+
* WARNING: bidders must be prepared to handle the case where their bid is
|
|
450
|
+
* outbid and their funds are returned to the original `msg.sender` address
|
|
451
|
+
* via SELFDESTRUCT (SENDALL).
|
|
452
|
+
* ------------------------------------------------------------------------
|
|
453
|
+
* Note that the use of `tokenId` is to prevent the possibility of
|
|
454
|
+
* transactions that are stuck in the pending pool for long periods of time
|
|
455
|
+
* from unintentionally bidding on auctions for future tokens.
|
|
456
|
+
* If a new auction is initialized during this call, the project's next
|
|
457
|
+
* token will be attempted to be minted to this minter contract, preparing
|
|
458
|
+
* it for the next auction. If the project's next token cannot be minted
|
|
459
|
+
* due to e.g. reaching the maximum invocations on the core contract or
|
|
460
|
+
* minter, the project's next token will not be minted.
|
|
461
|
+
* @param tokenId Token ID being bid on.
|
|
462
|
+
* @param coreContract Core contract address for the given project.
|
|
463
|
+
* @param minterTimeBufferSeconds Time buffer to extend the auction to if
|
|
464
|
+
* the bid is placed within the last `minterTimeBufferSeconds` of the
|
|
465
|
+
* auction.
|
|
466
|
+
* @param minterRefundGasLimit Gas limit to use when refunding the previous
|
|
467
|
+
* highest bidder.
|
|
468
|
+
* @param minterFilter Minter filter to use when minting the next token for
|
|
469
|
+
* the project.
|
|
470
|
+
* @dev nonReentrant modifier is used to prevent reentrancy attacks, e.g.
|
|
471
|
+
* an an auto-bidder that would be able to atomically outbid a user's
|
|
472
|
+
* new bid via a reentrant call to createBid.
|
|
473
|
+
*/
|
|
474
|
+
function createBid(
|
|
475
|
+
uint256 tokenId,
|
|
476
|
+
address coreContract,
|
|
477
|
+
uint256 minterTimeBufferSeconds,
|
|
478
|
+
uint256 minterRefundGasLimit,
|
|
479
|
+
IMinterFilterV1 minterFilter
|
|
480
|
+
) internal {
|
|
481
|
+
uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
|
|
482
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
483
|
+
projectId: projectId,
|
|
484
|
+
coreContract: coreContract
|
|
485
|
+
});
|
|
486
|
+
Auction storage auction = SEAProjectConfig_.activeAuction;
|
|
487
|
+
// CHECKS
|
|
488
|
+
// if no auction exists, or current auction is already settled, attempt
|
|
489
|
+
// to initialize a new auction for the input token ID and immediately
|
|
490
|
+
// return
|
|
491
|
+
if ((!auctionIsInitialized(auction)) || auction.settled) {
|
|
492
|
+
_initializeAuctionWithBid({
|
|
493
|
+
projectId: projectId,
|
|
494
|
+
coreContract: coreContract,
|
|
495
|
+
targetTokenId: tokenId,
|
|
496
|
+
minterFilter: minterFilter
|
|
497
|
+
});
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// @dev this branch is guaranteed to have an initialized auction that
|
|
501
|
+
// not settled, so no need to check for initialized or not settled
|
|
502
|
+
|
|
503
|
+
// ensure bids for a specific token ID are only applied to the auction
|
|
504
|
+
// for that token ID.
|
|
505
|
+
require(auction.tokenId == tokenId, "Token ID does not match auction");
|
|
506
|
+
|
|
507
|
+
// ensure auction is not already ended
|
|
508
|
+
require(auction.endTime > block.timestamp, "Auction already ended");
|
|
509
|
+
|
|
510
|
+
// require bid to be sufficiently greater than current highest bid
|
|
511
|
+
// @dev no overflow enforced automatically by solidity ^0.8.0
|
|
512
|
+
require(msg.value >= getMinimumNextBid(auction), "Bid is too low");
|
|
513
|
+
|
|
514
|
+
// EFFECTS
|
|
515
|
+
// record previous highest bidder and bid for refunding
|
|
516
|
+
address payable previousBidder = auction.currentBidder;
|
|
517
|
+
uint256 previousBid = auction.currentBid;
|
|
518
|
+
|
|
519
|
+
// update auction bid state
|
|
520
|
+
auctionUpdateBid({
|
|
521
|
+
auction: auction,
|
|
522
|
+
timeBufferSeconds: minterTimeBufferSeconds,
|
|
523
|
+
bidAmount: msg.value,
|
|
524
|
+
bidder: payable(msg.sender)
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// INTERACTIONS
|
|
528
|
+
// refund previous highest bidder
|
|
529
|
+
SplitFundsLib.forceSafeTransferETH({
|
|
530
|
+
to: previousBidder,
|
|
531
|
+
amount: previousBid,
|
|
532
|
+
minterRefundGasLimit: minterRefundGasLimit
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
emit AuctionBid({
|
|
536
|
+
tokenId: tokenId,
|
|
537
|
+
coreContract: coreContract,
|
|
538
|
+
bidder: msg.sender,
|
|
539
|
+
bidAmount: msg.value
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @notice Internal function that attempts to mint a new token to the next
|
|
545
|
+
* token slot for the project `projectId`.
|
|
546
|
+
* This function returns early and does not modify state if
|
|
547
|
+
* - the project has reached its maximum invocations on either the core
|
|
548
|
+
* contract or minter
|
|
549
|
+
* - the project config's `nextTokenNumberIsPopulated` is already true
|
|
550
|
+
* @param projectId The ID of the project to mint a new token for.
|
|
551
|
+
* @param coreContract The core contract address
|
|
552
|
+
* @param minterFilter The minter filter contract address
|
|
553
|
+
* @dev this calls mint with `msg.sender` as the sender, allowing artists
|
|
554
|
+
* to mint tokens to the next token slot for their project while a project
|
|
555
|
+
* is still paused. This happens when an artist is configuring their
|
|
556
|
+
* project's auction parameters or minter max invocations.
|
|
557
|
+
*/
|
|
558
|
+
function tryMintTokenToNextSlot(
|
|
559
|
+
uint256 projectId,
|
|
560
|
+
address coreContract,
|
|
561
|
+
IMinterFilterV1 minterFilter
|
|
562
|
+
) internal {
|
|
563
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
564
|
+
projectId: projectId,
|
|
565
|
+
coreContract: coreContract
|
|
566
|
+
});
|
|
567
|
+
if (SEAProjectConfig_.nextTokenNumberIsPopulated) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
// INTERACTIONS
|
|
571
|
+
// attempt to mint new token to this minter contract, only if max
|
|
572
|
+
// invocations has not been reached
|
|
573
|
+
// we require up-to-date invocation data to properly handle last token
|
|
574
|
+
// and avoid revert if relying on core to limit invocations, therefore
|
|
575
|
+
// use MaxInvocationsLib.invocationsRemain, which calls core contract
|
|
576
|
+
// to get latest invocation data
|
|
577
|
+
if (
|
|
578
|
+
!MaxInvocationsLib.invocationsRemain({
|
|
579
|
+
projectId: projectId,
|
|
580
|
+
coreContract: coreContract
|
|
581
|
+
})
|
|
582
|
+
) {
|
|
583
|
+
// we have reached the max invocations, so we do not mint a new
|
|
584
|
+
// token as the "next token", and leave the next token number as
|
|
585
|
+
// not populated
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
// @dev this is an effect after a trusted contract interaction
|
|
589
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated = true;
|
|
590
|
+
// mint a new token to this project's "next token" slot
|
|
591
|
+
// @dev this is an interaction with a trusted contract
|
|
592
|
+
uint256 nextTokenId = minterFilter.mint_joo({
|
|
593
|
+
to: address(this),
|
|
594
|
+
projectId: projectId,
|
|
595
|
+
coreContract: coreContract,
|
|
596
|
+
sender: msg.sender
|
|
597
|
+
});
|
|
598
|
+
// update state to reflect new token number
|
|
599
|
+
// @dev state changes after trusted contract interaction
|
|
600
|
+
// @dev unchecked is safe because mod 1e6 is guaranteed to be less than
|
|
601
|
+
// max uint24
|
|
602
|
+
unchecked {
|
|
603
|
+
SEAProjectConfig_.nextTokenNumber = uint24(
|
|
604
|
+
ABHelpers.tokenIdToTokenNumber(nextTokenId)
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
// update local maxHasBeenInvoked value if necessary
|
|
608
|
+
MaxInvocationsLib.validatePurchaseEffectsInvocations({
|
|
609
|
+
tokenId: nextTokenId,
|
|
610
|
+
coreContract: coreContract
|
|
611
|
+
});
|
|
612
|
+
emit ProjectNextTokenUpdated({
|
|
613
|
+
projectId: projectId,
|
|
614
|
+
coreContract: coreContract,
|
|
615
|
+
tokenId: nextTokenId
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
146
619
|
/**
|
|
147
620
|
* @notice Determines if a project is configured or not on this minter.
|
|
148
621
|
* Uses project config's `auctionDurationSeconds` to determine if project
|
|
149
622
|
* is configured, because `auctionDurationSeconds` is required to be
|
|
150
623
|
* non-zero when configured.
|
|
151
|
-
* @param
|
|
624
|
+
* @param projectId The project ID being queried
|
|
625
|
+
* @param coreContract The core contract address being queried
|
|
152
626
|
*/
|
|
153
627
|
function projectIsConfigured(
|
|
154
|
-
|
|
628
|
+
uint256 projectId,
|
|
629
|
+
address coreContract
|
|
155
630
|
) internal view returns (bool) {
|
|
156
|
-
|
|
631
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
632
|
+
projectId: projectId,
|
|
633
|
+
coreContract: coreContract
|
|
634
|
+
});
|
|
635
|
+
return SEAProjectConfig_.auctionDurationSeconds != 0;
|
|
157
636
|
}
|
|
158
637
|
|
|
159
638
|
/**
|
|
@@ -161,46 +640,51 @@ library SEALib {
|
|
|
161
640
|
* Uses auction's `currentBidder` address to determine if auction is
|
|
162
641
|
* initialized, because `currentBidder` is always non-zero after an auction
|
|
163
642
|
* has been initialized.
|
|
164
|
-
* @param
|
|
643
|
+
* @param auction The auction to check.
|
|
165
644
|
*/
|
|
166
645
|
function auctionIsInitialized(
|
|
167
|
-
Auction storage
|
|
646
|
+
Auction storage auction
|
|
168
647
|
) internal view returns (bool isInitialized) {
|
|
169
648
|
// auction is initialized if currentBidder is non-zero
|
|
170
|
-
return
|
|
649
|
+
return auction.currentBidder != address(0);
|
|
171
650
|
}
|
|
172
651
|
|
|
173
652
|
/**
|
|
174
|
-
*
|
|
653
|
+
* @notice Function returns bool representing if an auction is accepting bids above base
|
|
175
654
|
* price. It is accepting bids if it is initialized and has not reached its
|
|
176
655
|
* end time.
|
|
177
|
-
* @param
|
|
656
|
+
* @param auction The auction to check.
|
|
178
657
|
*/
|
|
179
658
|
function auctionIsAcceptingIncreasingBids(
|
|
180
|
-
Auction storage
|
|
659
|
+
Auction storage auction
|
|
181
660
|
) internal view returns (bool isAcceptingBids) {
|
|
182
661
|
// auction is accepting bids if it is initialized and has not reached
|
|
183
662
|
// its end time
|
|
184
|
-
isAcceptingBids = (auctionIsInitialized(
|
|
185
|
-
block.timestamp <
|
|
186
|
-
return isAcceptingBids;
|
|
663
|
+
isAcceptingBids = (auctionIsInitialized(auction) &&
|
|
664
|
+
block.timestamp < auction.endTime);
|
|
187
665
|
}
|
|
188
666
|
|
|
189
667
|
/**
|
|
190
668
|
* @notice SEAProjectConfig => active auction details.
|
|
191
669
|
* @dev reverts if no auction exists for the project.
|
|
192
|
-
* @param
|
|
670
|
+
* @param projectId The project ID being queried
|
|
671
|
+
* @param coreContract The core contract address being queried
|
|
193
672
|
*/
|
|
194
673
|
function projectActiveAuctionDetails(
|
|
195
|
-
|
|
674
|
+
uint256 projectId,
|
|
675
|
+
address coreContract
|
|
196
676
|
) internal view returns (Auction memory auction) {
|
|
197
|
-
|
|
677
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
678
|
+
projectId: projectId,
|
|
679
|
+
coreContract: coreContract
|
|
680
|
+
});
|
|
681
|
+
// load auction storage pointer to pass ref to utility function
|
|
682
|
+
Auction storage auction_ = SEAProjectConfig_.activeAuction;
|
|
198
683
|
// do not return uninitialized auctions (i.e. auctions that do not
|
|
199
684
|
// exist, where currentBidder is still the default value)
|
|
200
|
-
require(auctionIsInitialized(
|
|
201
|
-
// load entire auction into memory
|
|
202
|
-
auction =
|
|
203
|
-
return auction;
|
|
685
|
+
require(auctionIsInitialized(auction_), "No auction exists on project");
|
|
686
|
+
// load entire auction into memory to return
|
|
687
|
+
auction = SEAProjectConfig_.activeAuction;
|
|
204
688
|
}
|
|
205
689
|
|
|
206
690
|
/**
|
|
@@ -213,47 +697,55 @@ library SEALib {
|
|
|
213
697
|
* target bid token ID to be passed in as an argument.
|
|
214
698
|
* The function reverts if a project does not have an active auction and
|
|
215
699
|
* the next expected token ID has not been populated.
|
|
216
|
-
* @param
|
|
217
|
-
* @param
|
|
700
|
+
* @param projectId The project ID being queried
|
|
701
|
+
* @param coreContract The core contract address being queried
|
|
218
702
|
* @return The current token ID being auctioned, or the next token ID to be
|
|
219
703
|
* auctioned if a new auction is ready to be created.
|
|
220
704
|
*/
|
|
221
705
|
function getTokenToBid(
|
|
222
|
-
|
|
223
|
-
|
|
706
|
+
uint256 projectId,
|
|
707
|
+
address coreContract
|
|
224
708
|
) internal view returns (uint256) {
|
|
225
|
-
|
|
709
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
710
|
+
projectId: projectId,
|
|
711
|
+
coreContract: coreContract
|
|
712
|
+
});
|
|
713
|
+
Auction storage auction = SEAProjectConfig_.activeAuction;
|
|
226
714
|
// if project has an active token auction that is not settled, return
|
|
227
715
|
// that token ID
|
|
228
716
|
if (
|
|
229
|
-
auctionIsInitialized(
|
|
230
|
-
(_auction.endTime > block.timestamp)
|
|
717
|
+
auctionIsInitialized(auction) && (auction.endTime > block.timestamp)
|
|
231
718
|
) {
|
|
232
|
-
return
|
|
719
|
+
return auction.tokenId;
|
|
233
720
|
}
|
|
234
721
|
// otherwise, return the next expected token ID to be auctioned.
|
|
235
|
-
return getNextTokenId(
|
|
722
|
+
return getNextTokenId(projectId, coreContract);
|
|
236
723
|
}
|
|
237
724
|
|
|
238
725
|
/**
|
|
239
726
|
* @notice View function that returns the next token ID to be auctioned
|
|
240
|
-
* by this minter for project `
|
|
727
|
+
* by this minter for project `projectId`.
|
|
241
728
|
* Reverts if the next token ID has not been populated for the project.
|
|
242
|
-
* @param
|
|
243
|
-
* @param
|
|
729
|
+
* @param projectId The project ID being queried
|
|
730
|
+
* @param coreContract The core contract address being queried
|
|
244
731
|
*/
|
|
245
732
|
function getNextTokenId(
|
|
246
|
-
|
|
247
|
-
|
|
733
|
+
uint256 projectId,
|
|
734
|
+
address coreContract
|
|
248
735
|
) internal view returns (uint256 nextTokenId) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
736
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
737
|
+
projectId: projectId,
|
|
738
|
+
coreContract: coreContract
|
|
739
|
+
});
|
|
740
|
+
require(
|
|
741
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated,
|
|
742
|
+
"Next token not populated"
|
|
743
|
+
);
|
|
744
|
+
|
|
252
745
|
// @dev overflow automatically checked in Solidity ^0.8.0
|
|
253
746
|
nextTokenId =
|
|
254
|
-
(
|
|
255
|
-
|
|
256
|
-
return nextTokenId;
|
|
747
|
+
(projectId * ONE_MILLION) +
|
|
748
|
+
SEAProjectConfig_.nextTokenNumber;
|
|
257
749
|
}
|
|
258
750
|
|
|
259
751
|
/**
|
|
@@ -269,20 +761,29 @@ library SEALib {
|
|
|
269
761
|
* the project is configured, `isConfigured` will be true, and
|
|
270
762
|
* `tokenPriceInWei` will be the minimum initial bid price for the next
|
|
271
763
|
* token auction.
|
|
272
|
-
* @param
|
|
764
|
+
* @param projectId Project ID to get price info for
|
|
765
|
+
* @param coreContract Core contract address to get price info for
|
|
273
766
|
* @return isConfigured true only if project auctions are configured.
|
|
274
767
|
* @return tokenPriceInWei price in wei to become the leading bidder on a
|
|
275
768
|
* token auction.
|
|
276
769
|
*/
|
|
277
|
-
function
|
|
278
|
-
|
|
770
|
+
function getPriceInfo(
|
|
771
|
+
uint256 projectId,
|
|
772
|
+
address coreContract
|
|
279
773
|
) internal view returns (bool isConfigured, uint256 tokenPriceInWei) {
|
|
280
|
-
|
|
774
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
775
|
+
projectId: projectId,
|
|
776
|
+
coreContract: coreContract
|
|
777
|
+
});
|
|
778
|
+
Auction storage auction = SEAProjectConfig_.activeAuction;
|
|
281
779
|
// base price of zero not allowed when configuring auctions, so use it
|
|
282
780
|
// as indicator of whether auctions are configured for the project
|
|
283
|
-
bool projectIsConfigured_ = projectIsConfigured(
|
|
781
|
+
bool projectIsConfigured_ = projectIsConfigured({
|
|
782
|
+
projectId: projectId,
|
|
783
|
+
coreContract: coreContract
|
|
784
|
+
});
|
|
284
785
|
bool auctionIsAcceptingIncreasingBids_ = auctionIsAcceptingIncreasingBids(
|
|
285
|
-
|
|
786
|
+
auction
|
|
286
787
|
);
|
|
287
788
|
isConfigured =
|
|
288
789
|
projectIsConfigured_ ||
|
|
@@ -291,12 +792,12 @@ library SEALib {
|
|
|
291
792
|
if (isConfigured) {
|
|
292
793
|
if (auctionIsAcceptingIncreasingBids_) {
|
|
293
794
|
// return minimum next bid, given current bid
|
|
294
|
-
tokenPriceInWei = getMinimumNextBid(
|
|
795
|
+
tokenPriceInWei = getMinimumNextBid(auction);
|
|
295
796
|
} else {
|
|
296
797
|
// return base (starting) price if if current auction is not
|
|
297
798
|
// accepting bids (i.e. the minimum initial bid price for the
|
|
298
799
|
// next token auction)
|
|
299
|
-
tokenPriceInWei =
|
|
800
|
+
tokenPriceInWei = SEAProjectConfig_.basePrice;
|
|
300
801
|
}
|
|
301
802
|
}
|
|
302
803
|
// else leave tokenPriceInWei as default value of zero
|
|
@@ -304,17 +805,191 @@ library SEALib {
|
|
|
304
805
|
}
|
|
305
806
|
|
|
306
807
|
/**
|
|
307
|
-
*
|
|
808
|
+
* @notice Function returns the minimum next bid amount in a configured auction, given the
|
|
308
809
|
* current bid amount and the project's configured minimum bid increment
|
|
309
810
|
* percentage.
|
|
310
|
-
* @param
|
|
811
|
+
* @param auction Auction to query
|
|
311
812
|
*/
|
|
312
813
|
function getMinimumNextBid(
|
|
313
|
-
Auction storage
|
|
814
|
+
Auction storage auction
|
|
314
815
|
) internal view returns (uint256 minimumNextBid) {
|
|
315
|
-
// @dev
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
816
|
+
// @dev unchecked due to 0 <= minBidIncrementPercentage <= 255, and
|
|
817
|
+
// currentBid is capped at supply of ETH, so no risk of overflow
|
|
818
|
+
unchecked {
|
|
819
|
+
return
|
|
820
|
+
(auction.currentBid *
|
|
821
|
+
(100 + auction.minBidIncrementPercentage)) / 100;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* @dev Private function to initialize an auction for the next token ID
|
|
827
|
+
* on project `projectId` with a bid of `msg.value` from `msg.sender`.
|
|
828
|
+
* This function reverts in any of the following cases:
|
|
829
|
+
* - project is not configured on this minter
|
|
830
|
+
* - project is configured but has not yet reached its start time
|
|
831
|
+
* - project has a current active auction that is not settled
|
|
832
|
+
* - insufficient bid amount (msg.value < basePrice)
|
|
833
|
+
* - no next token has been minted for the project (artist may need to
|
|
834
|
+
* call `tryPopulateNextToken`)
|
|
835
|
+
* - `targetTokenId` does not match the next token ID for the project
|
|
836
|
+
* After initializing a new auction, this function attempts to mint a new
|
|
837
|
+
* token and assign it to the project's next token slot, in preparation for
|
|
838
|
+
* a future token auction. However, if the project has reached its maximum
|
|
839
|
+
* invocations on either the core contract or minter, the next token slot
|
|
840
|
+
* for the project will remain empty.
|
|
841
|
+
* @dev This should be executed in a nonReentrant context to provide redundant
|
|
842
|
+
* protection against reentrancy.
|
|
843
|
+
* @param projectId The project ID to initialize an auction for
|
|
844
|
+
* @param coreContract The core contract address to initialize an auction for
|
|
845
|
+
* @param targetTokenId The token ID to initialize an auction for
|
|
846
|
+
* @param minterFilter The minter filter contract address
|
|
847
|
+
*/
|
|
848
|
+
function _initializeAuctionWithBid(
|
|
849
|
+
uint256 projectId,
|
|
850
|
+
address coreContract,
|
|
851
|
+
uint256 targetTokenId,
|
|
852
|
+
IMinterFilterV1 minterFilter
|
|
853
|
+
) private {
|
|
854
|
+
SEAProjectConfig storage SEAProjectConfig_ = getSEAProjectConfig({
|
|
855
|
+
projectId: projectId,
|
|
856
|
+
coreContract: coreContract
|
|
857
|
+
});
|
|
858
|
+
Auction storage auction = SEAProjectConfig_.activeAuction;
|
|
859
|
+
// CHECKS
|
|
860
|
+
// ensure project auctions are configured
|
|
861
|
+
// @dev base price of zero indicates auctions are not configured
|
|
862
|
+
// because only base price of gt zero is allowed when configuring
|
|
863
|
+
require(
|
|
864
|
+
projectIsConfigured({
|
|
865
|
+
projectId: projectId,
|
|
866
|
+
coreContract: coreContract
|
|
867
|
+
}),
|
|
868
|
+
"Project not configured"
|
|
869
|
+
);
|
|
870
|
+
// only initialize new auctions if they meet the start time
|
|
871
|
+
// requirement
|
|
872
|
+
require(
|
|
873
|
+
block.timestamp >= SEAProjectConfig_.timestampStart,
|
|
874
|
+
"Only gte project start time"
|
|
875
|
+
);
|
|
876
|
+
// the following require statement is redundant based on how this
|
|
877
|
+
// internal function is called, but it is included for protection
|
|
878
|
+
// against future changes that could easily introduce a bug if this
|
|
879
|
+
// check is not present
|
|
880
|
+
// @dev no cover else branch of next line because unreachable
|
|
881
|
+
require(
|
|
882
|
+
(!auctionIsInitialized(auction)) || auction.settled,
|
|
883
|
+
"Existing auction not settled"
|
|
884
|
+
);
|
|
885
|
+
// require valid bid value
|
|
886
|
+
require(
|
|
887
|
+
msg.value >= SEAProjectConfig_.basePrice,
|
|
888
|
+
"Insufficient initial bid"
|
|
889
|
+
);
|
|
890
|
+
// require next token number is populated
|
|
891
|
+
// @dev this should only be encountered if the project has reached
|
|
892
|
+
// its maximum invocations on either the core contract or minter
|
|
893
|
+
require(
|
|
894
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated,
|
|
895
|
+
"No next token, check max invocations"
|
|
896
|
+
);
|
|
897
|
+
// require next token number is the target token ID
|
|
898
|
+
require(
|
|
899
|
+
SEAProjectConfig_.nextTokenNumber ==
|
|
900
|
+
ABHelpers.tokenIdToTokenNumber(targetTokenId),
|
|
901
|
+
"Incorrect target token ID"
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
// EFFECTS
|
|
905
|
+
// create new auction, overwriting previous auction if it exists
|
|
906
|
+
uint64 endTime = overwriteProjectActiveAuction({
|
|
907
|
+
SEAProjectConfig_: SEAProjectConfig_,
|
|
908
|
+
targetTokenId: targetTokenId,
|
|
909
|
+
bidAmount: msg.value,
|
|
910
|
+
bidder: payable(msg.sender)
|
|
911
|
+
});
|
|
912
|
+
// mark next token number as not populated
|
|
913
|
+
// @dev intentionally not setting nextTokenNumber to zero to avoid
|
|
914
|
+
// unnecessary gas costs
|
|
915
|
+
SEAProjectConfig_.nextTokenNumberIsPopulated = false;
|
|
916
|
+
|
|
917
|
+
// @dev we intentionally emit event here due to potential of early
|
|
918
|
+
// return in INTERACTIONS section
|
|
919
|
+
emit AuctionInitialized({
|
|
920
|
+
tokenId: targetTokenId,
|
|
921
|
+
coreContract: coreContract,
|
|
922
|
+
bidder: msg.sender,
|
|
923
|
+
bidAmount: msg.value,
|
|
924
|
+
endTime: endTime,
|
|
925
|
+
minBidIncrementPercentage: SEAProjectConfig_
|
|
926
|
+
.activeAuction
|
|
927
|
+
.minBidIncrementPercentage
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
// INTERACTIONS
|
|
931
|
+
// attempt to mint new token to this minter contract, only if max
|
|
932
|
+
// invocations has not been reached
|
|
933
|
+
tryMintTokenToNextSlot({
|
|
934
|
+
projectId: projectId,
|
|
935
|
+
coreContract: coreContract,
|
|
936
|
+
minterFilter: minterFilter
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* @notice Function to overwrite the active auction for a project with a new auction.
|
|
942
|
+
* @dev This function is used to initialize a new auction. Care must be
|
|
943
|
+
* taken to ensure that the existing auction is fully complete and settled.
|
|
944
|
+
* @param SEAProjectConfig_ SEAProjectConfig to update
|
|
945
|
+
* @param targetTokenId token ID to create the auction for
|
|
946
|
+
* @param bidAmount initial bid amount
|
|
947
|
+
* @param bidder initial bidder's payable address
|
|
948
|
+
* @return endTime end time of the newly created auction
|
|
949
|
+
*/
|
|
950
|
+
function overwriteProjectActiveAuction(
|
|
951
|
+
SEAProjectConfig storage SEAProjectConfig_,
|
|
952
|
+
uint256 targetTokenId,
|
|
953
|
+
uint256 bidAmount,
|
|
954
|
+
address payable bidder
|
|
955
|
+
) private returns (uint64 endTime) {
|
|
956
|
+
// calculate auction end time
|
|
957
|
+
endTime = (block.timestamp + SEAProjectConfig_.auctionDurationSeconds)
|
|
958
|
+
.toUint64();
|
|
959
|
+
// set active auction on SEAProjectConfig
|
|
960
|
+
SEAProjectConfig_.activeAuction = Auction({
|
|
961
|
+
tokenId: targetTokenId,
|
|
962
|
+
currentBid: bidAmount,
|
|
963
|
+
currentBidder: bidder,
|
|
964
|
+
endTime: endTime,
|
|
965
|
+
minBidIncrementPercentage: SEAProjectConfig_
|
|
966
|
+
.minBidIncrementPercentage,
|
|
967
|
+
settled: false
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Loads the SEAProjectConfig for a given project and core
|
|
973
|
+
* contract.
|
|
974
|
+
* @param projectId Project Id to get config for
|
|
975
|
+
* @param coreContract Core contract address to get config for
|
|
976
|
+
*/
|
|
977
|
+
function getSEAProjectConfig(
|
|
978
|
+
uint256 projectId,
|
|
979
|
+
address coreContract
|
|
980
|
+
) internal view returns (SEAProjectConfig storage) {
|
|
981
|
+
return s().SEAProjectConfigs[coreContract][projectId];
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* @notice Return the storage struct for reading and writing. This library
|
|
986
|
+
* uses a diamond storage pattern when managing storage.
|
|
987
|
+
* @return storageStruct The SEALibStorage struct.
|
|
988
|
+
*/
|
|
989
|
+
function s() internal pure returns (SEALibStorage storage storageStruct) {
|
|
990
|
+
bytes32 position = SEA_LIB_STORAGE_POSITION;
|
|
991
|
+
assembly ("memory-safe") {
|
|
992
|
+
storageStruct.slot := position
|
|
993
|
+
}
|
|
319
994
|
}
|
|
320
995
|
}
|