@evvm/testnet-contracts 1.0.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/LICENSE +166 -0
- package/README.md +216 -0
- package/package.json +51 -0
- package/src/contracts/evvm/Evvm.sol +1327 -0
- package/src/contracts/evvm/EvvmLegacy.sol +1553 -0
- package/src/contracts/evvm/lib/ErrorsLib.sol +17 -0
- package/src/contracts/evvm/lib/EvvmStorage.sol +60 -0
- package/src/contracts/evvm/lib/EvvmStructs.sol +64 -0
- package/src/contracts/evvm/lib/SignatureUtils.sol +124 -0
- package/src/contracts/nameService/NameService.sol +1751 -0
- package/src/contracts/nameService/lib/ErrorsLib.sol +27 -0
- package/src/contracts/nameService/lib/SignatureUtils.sol +239 -0
- package/src/contracts/staking/Estimator.sol +358 -0
- package/src/contracts/staking/Staking.sol +1148 -0
- package/src/contracts/staking/lib/ErrorsLib.sol +19 -0
- package/src/contracts/staking/lib/SignatureUtils.sol +68 -0
- package/src/contracts/treasury/Treasury.sol +104 -0
- package/src/contracts/treasury/lib/ErrorsLib.sol +11 -0
- package/src/contracts/treasuryTwoChains/TreasuryExternalChainStation.sol +551 -0
- package/src/contracts/treasuryTwoChains/TreasuryHostChainStation.sol +512 -0
- package/src/contracts/treasuryTwoChains/lib/ErrorsLib.sol +15 -0
- package/src/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol +41 -0
- package/src/contracts/treasuryTwoChains/lib/HostChainStationStructs.sol +52 -0
- package/src/contracts/treasuryTwoChains/lib/SignatureUtils.sol +47 -0
- package/src/interfaces/IEstimator.sol +102 -0
- package/src/interfaces/IEvvm.sol +195 -0
- package/src/interfaces/INameService.sol +283 -0
- package/src/interfaces/IStaking.sol +202 -0
- package/src/interfaces/ITreasury.sol +17 -0
- package/src/interfaces/ITreasuryExternalChainStation.sol +262 -0
- package/src/interfaces/ITreasuryHostChainStation.sol +251 -0
- package/src/lib/AdvancedStrings.sol +77 -0
- package/src/lib/Erc191TestBuilder.sol +402 -0
- package/src/lib/SignatureRecover.sol +56 -0
|
@@ -0,0 +1,1751 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EVVM-NONCOMMERCIAL-1.0
|
|
2
|
+
// Full license terms available at: https://www.evvm.info/docs/EVVMNoncommercialLicense
|
|
3
|
+
|
|
4
|
+
pragma solidity ^0.8.0;
|
|
5
|
+
/**
|
|
6
|
+
_ _
|
|
7
|
+
| \ | |
|
|
8
|
+
| \| | __ _ _ __ ___ ___
|
|
9
|
+
| . ` |/ _` | '_ ` _ \ / _ \
|
|
10
|
+
| |\ | (_| | | | | | | __/
|
|
11
|
+
\_| \_/\__,_|_| |_| |_|\___|
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_____ _
|
|
15
|
+
/ ___| (_)
|
|
16
|
+
\ `--. ___ _ ____ ___ ___ ___
|
|
17
|
+
`--. \/ _ | '__\ \ / | |/ __/ _ \
|
|
18
|
+
/\__/ | __| | \ V /| | (_| __/
|
|
19
|
+
\____/ \___|_| \_/ |_|\___\___|
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
████████╗███████╗███████╗████████╗███╗ ██╗███████╗████████╗
|
|
23
|
+
╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝████╗ ██║██╔════╝╚══██╔══╝
|
|
24
|
+
██║ █████╗ ███████╗ ██║ ██╔██╗ ██║█████╗ ██║
|
|
25
|
+
██║ ██╔══╝ ╚════██║ ██║ ██║╚██╗██║██╔══╝ ██║
|
|
26
|
+
██║ ███████╗███████║ ██║ ██║ ╚████║███████╗ ██║
|
|
27
|
+
╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
|
|
28
|
+
*
|
|
29
|
+
* @title EVVM Name Service Contract
|
|
30
|
+
* @author jistro.eth ariutokintumi.eth
|
|
31
|
+
* @notice This contract manages username registration and domain name services for the EVVM ecosystem
|
|
32
|
+
* @dev Provides a comprehensive domain name system with features including:
|
|
33
|
+
*
|
|
34
|
+
* Core Features:
|
|
35
|
+
* - Username registration with pre-registration protection against front-running
|
|
36
|
+
* - Custom metadata management with schema-based data storage
|
|
37
|
+
* - Username trading system with offers and marketplace functionality
|
|
38
|
+
* - Renewal system with dynamic pricing based on market demand
|
|
39
|
+
* - Time-delayed governance for administrative functions
|
|
40
|
+
*
|
|
41
|
+
* Registration Process:
|
|
42
|
+
* 1. Pre-register: Commit to a username hash to prevent front-running
|
|
43
|
+
* 2. Register: Reveal the username and complete registration within 30 minutes
|
|
44
|
+
* 3. Manage: Add custom metadata, handle offers, and renew as needed
|
|
45
|
+
*
|
|
46
|
+
* Security Features:
|
|
47
|
+
* - Signature verification for all operations
|
|
48
|
+
* - Nonce-based replay protection
|
|
49
|
+
* - Time-locked administrative changes
|
|
50
|
+
* - Integration with EVVM core for secure payments
|
|
51
|
+
*
|
|
52
|
+
* Economic Model:
|
|
53
|
+
* - Registration costs 100x EVVM reward amount
|
|
54
|
+
* - Custom metadata operations cost 10x EVVM reward amount
|
|
55
|
+
* - Renewal pricing varies based on market demand and timing
|
|
56
|
+
* - Marketplace takes 0.5% fee on username sales
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import {Evvm} from "@evvm/testnet-contracts/contracts/evvm/Evvm.sol";
|
|
60
|
+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
|
61
|
+
import {AdvancedStrings} from "@evvm/testnet-contracts/lib/AdvancedStrings.sol";
|
|
62
|
+
import {ErrorsLib} from "@evvm/testnet-contracts/contracts/nameService/lib/ErrorsLib.sol";
|
|
63
|
+
import {SignatureUtils} from "@evvm/testnet-contracts/contracts/nameService/lib/SignatureUtils.sol";
|
|
64
|
+
|
|
65
|
+
contract NameService {
|
|
66
|
+
/**
|
|
67
|
+
* @dev Struct for managing address change proposals with time delay
|
|
68
|
+
* @param current Currently active address
|
|
69
|
+
* @param proposal Proposed new address waiting for approval
|
|
70
|
+
* @param timeToAccept Timestamp when the proposal can be accepted
|
|
71
|
+
*/
|
|
72
|
+
struct AddressTypeProposal {
|
|
73
|
+
address current;
|
|
74
|
+
address proposal;
|
|
75
|
+
uint256 timeToAccept;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @dev Struct for managing uint256 value proposals with time delay
|
|
80
|
+
* @param current Currently active value
|
|
81
|
+
* @param proposal Proposed new value waiting for approval
|
|
82
|
+
* @param timeToAccept Timestamp when the proposal can be accepted
|
|
83
|
+
*/
|
|
84
|
+
struct UintTypeProposal {
|
|
85
|
+
uint256 current;
|
|
86
|
+
uint256 proposal;
|
|
87
|
+
uint256 timeToAccept;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @dev Struct for managing boolean flag changes with time delay
|
|
92
|
+
* @param flag Current boolean state
|
|
93
|
+
* @param timeToAcceptChange Timestamp when the flag change can be executed
|
|
94
|
+
*/
|
|
95
|
+
struct BoolTypeProposal {
|
|
96
|
+
bool flag;
|
|
97
|
+
uint256 timeToAcceptChange;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @dev Core metadata for each registered identity/username
|
|
102
|
+
* @param owner Address that owns this identity
|
|
103
|
+
* @param expireDate Timestamp when the registration expires
|
|
104
|
+
* @param customMetadataMaxSlots Number of custom metadata entries stored
|
|
105
|
+
* @param offerMaxSlots Maximum number of offers that have been made
|
|
106
|
+
* @param flagNotAUsername Flag indicating if this is a pre-registration (0x01) or actual username (0x00)
|
|
107
|
+
*/
|
|
108
|
+
struct IdentityBaseMetadata {
|
|
109
|
+
address owner;
|
|
110
|
+
uint256 expireDate;
|
|
111
|
+
uint256 customMetadataMaxSlots;
|
|
112
|
+
uint256 offerMaxSlots;
|
|
113
|
+
bytes1 flagNotAUsername;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// @dev Mapping from username to its core metadata and registration details
|
|
117
|
+
mapping(string username => IdentityBaseMetadata basicMetadata)
|
|
118
|
+
private identityDetails;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @dev Metadata for marketplace offers on usernames
|
|
122
|
+
* @param offerer Address making the offer
|
|
123
|
+
* @param expireDate Timestamp when the offer expires
|
|
124
|
+
* @param amount Amount offered in Principal Tokens (after 0.5% marketplace fee deduction)
|
|
125
|
+
*/
|
|
126
|
+
struct OfferMetadata {
|
|
127
|
+
address offerer;
|
|
128
|
+
uint256 expireDate;
|
|
129
|
+
uint256 amount;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// @dev Nested mapping: username => offer ID => offer details
|
|
133
|
+
mapping(string username => mapping(uint256 id => OfferMetadata))
|
|
134
|
+
private usernameOffers;
|
|
135
|
+
|
|
136
|
+
/// @dev Nested mapping: username => metadata key => custom value string
|
|
137
|
+
mapping(string username => mapping(uint256 numberKey => string customValue))
|
|
138
|
+
private identityCustomMetadata;
|
|
139
|
+
|
|
140
|
+
/// @dev Mapping to track used nonces per address to prevent replay attacks
|
|
141
|
+
mapping(address => mapping(uint256 => bool)) private nameServiceNonce;
|
|
142
|
+
|
|
143
|
+
/// @dev Proposal system for token withdrawal amounts with time delay
|
|
144
|
+
UintTypeProposal amountToWithdrawTokens;
|
|
145
|
+
|
|
146
|
+
/// @dev Proposal system for EVVM contract address changes with time delay
|
|
147
|
+
AddressTypeProposal evvmAddress;
|
|
148
|
+
|
|
149
|
+
/// @dev Proposal system for admin address changes with time delay
|
|
150
|
+
AddressTypeProposal admin;
|
|
151
|
+
|
|
152
|
+
/// @dev Constant address representing the Principal Token in the EVVM ecosystem
|
|
153
|
+
address private constant PRINCIPAL_TOKEN_ADDRESS =
|
|
154
|
+
0x0000000000000000000000000000000000000001;
|
|
155
|
+
|
|
156
|
+
/// @dev Amount of Principal Tokens locked in pending marketplace offers
|
|
157
|
+
uint256 private principalTokenTokenLockedForWithdrawOffers;
|
|
158
|
+
|
|
159
|
+
/// @dev Restricts function access to the current admin address only
|
|
160
|
+
modifier onlyAdmin() {
|
|
161
|
+
if (msg.sender != admin.current) revert ErrorsLib.SenderIsNotAdmin();
|
|
162
|
+
|
|
163
|
+
_;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// @dev Verifies that the caller owns the specified identity/username
|
|
167
|
+
modifier onlyOwnerOfIdentity(address _user, string memory _identity) {
|
|
168
|
+
if (identityDetails[_identity].owner != _user)
|
|
169
|
+
revert ErrorsLib.UserIsNotOwnerOfIdentity();
|
|
170
|
+
|
|
171
|
+
_;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// @dev Ensures the nonce hasn't been used before to prevent replay attacks
|
|
175
|
+
modifier verifyIfNonceIsAvailable(address _user, uint256 _nonce) {
|
|
176
|
+
if (nameServiceNonce[_user][_nonce])
|
|
177
|
+
revert ErrorsLib.NonceAlreadyUsed();
|
|
178
|
+
|
|
179
|
+
_;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @notice Initializes the NameService contract
|
|
184
|
+
* @dev Sets up the EVVM integration and initial admin
|
|
185
|
+
* @param _evvmAddress Address of the EVVM core contract for payment processing
|
|
186
|
+
* @param _initialOwner Address that will have admin privileges
|
|
187
|
+
*/
|
|
188
|
+
constructor(address _evvmAddress, address _initialOwner) {
|
|
189
|
+
evvmAddress.current = _evvmAddress;
|
|
190
|
+
admin.current = _initialOwner;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @notice Pre-registers a username hash to prevent front-running attacks
|
|
195
|
+
* @dev Creates a temporary reservation that can be registered 30 minutes later
|
|
196
|
+
* @param user Address of the user making the pre-registration
|
|
197
|
+
* @param hashPreRegisteredUsername Keccak256 hash of username + random number
|
|
198
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
199
|
+
* @param signature Signature proving authorization for this operation
|
|
200
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
201
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
202
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
203
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
204
|
+
*/
|
|
205
|
+
function preRegistrationUsername(
|
|
206
|
+
address user,
|
|
207
|
+
bytes32 hashPreRegisteredUsername,
|
|
208
|
+
uint256 nonce,
|
|
209
|
+
bytes memory signature,
|
|
210
|
+
uint256 priorityFee_EVVM,
|
|
211
|
+
uint256 nonce_EVVM,
|
|
212
|
+
bool priorityFlag_EVVM,
|
|
213
|
+
bytes memory signature_EVVM
|
|
214
|
+
) public verifyIfNonceIsAvailable(user, nonce) {
|
|
215
|
+
if (
|
|
216
|
+
!SignatureUtils.verifyMessageSignedForPreRegistrationUsername(
|
|
217
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
218
|
+
user,
|
|
219
|
+
hashPreRegisteredUsername,
|
|
220
|
+
nonce,
|
|
221
|
+
signature
|
|
222
|
+
)
|
|
223
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
224
|
+
|
|
225
|
+
if (priorityFee_EVVM > 0) {
|
|
226
|
+
makePay(
|
|
227
|
+
user,
|
|
228
|
+
0,
|
|
229
|
+
priorityFee_EVVM,
|
|
230
|
+
nonce_EVVM,
|
|
231
|
+
priorityFlag_EVVM,
|
|
232
|
+
signature_EVVM
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
string memory key = string.concat(
|
|
237
|
+
"@",
|
|
238
|
+
AdvancedStrings.bytes32ToString(hashPreRegisteredUsername)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
identityDetails[key] = IdentityBaseMetadata({
|
|
242
|
+
owner: user,
|
|
243
|
+
expireDate: block.timestamp + 30 minutes,
|
|
244
|
+
customMetadataMaxSlots: 0,
|
|
245
|
+
offerMaxSlots: 0,
|
|
246
|
+
flagNotAUsername: 0x01
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
nameServiceNonce[user][nonce] = true;
|
|
250
|
+
|
|
251
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
252
|
+
makeCaPay(
|
|
253
|
+
msg.sender,
|
|
254
|
+
Evvm(evvmAddress.current).getRewardAmount() + priorityFee_EVVM
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @notice Completes username registration using a pre-registration commitment
|
|
261
|
+
* @dev Must be called after the pre-registration period (30 minutes) has reached
|
|
262
|
+
* @param user Address of the user completing the registration
|
|
263
|
+
* @param username The actual username being registered (revealed from hash)
|
|
264
|
+
* @param clowNumber Random number used in the pre-registration hash
|
|
265
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
266
|
+
* @param signature Signature proving authorization for this operation
|
|
267
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
268
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
269
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
270
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
271
|
+
*/
|
|
272
|
+
function registrationUsername(
|
|
273
|
+
address user,
|
|
274
|
+
string memory username,
|
|
275
|
+
uint256 clowNumber,
|
|
276
|
+
uint256 nonce,
|
|
277
|
+
bytes memory signature,
|
|
278
|
+
uint256 priorityFee_EVVM,
|
|
279
|
+
uint256 nonce_EVVM,
|
|
280
|
+
bool priorityFlag_EVVM,
|
|
281
|
+
bytes memory signature_EVVM
|
|
282
|
+
) public verifyIfNonceIsAvailable(user, nonce) {
|
|
283
|
+
if (admin.current != user) isValidUsername(username);
|
|
284
|
+
|
|
285
|
+
if (!isUsernameAvailable(username)) {
|
|
286
|
+
revert ErrorsLib.UsernameAlreadyRegistered();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (
|
|
290
|
+
!SignatureUtils.verifyMessageSignedForRegistrationUsername(
|
|
291
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
292
|
+
user,
|
|
293
|
+
username,
|
|
294
|
+
clowNumber,
|
|
295
|
+
nonce,
|
|
296
|
+
signature
|
|
297
|
+
)
|
|
298
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
299
|
+
|
|
300
|
+
makePay(
|
|
301
|
+
user,
|
|
302
|
+
getPricePerRegistration(),
|
|
303
|
+
priorityFee_EVVM,
|
|
304
|
+
nonce_EVVM,
|
|
305
|
+
priorityFlag_EVVM,
|
|
306
|
+
signature_EVVM
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
string memory _key = string.concat(
|
|
310
|
+
"@",
|
|
311
|
+
AdvancedStrings.bytes32ToString(hashUsername(username, clowNumber))
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (
|
|
315
|
+
identityDetails[_key].owner != user ||
|
|
316
|
+
identityDetails[_key].expireDate > block.timestamp
|
|
317
|
+
) revert ErrorsLib.PreRegistrationNotValid();
|
|
318
|
+
|
|
319
|
+
identityDetails[username] = IdentityBaseMetadata({
|
|
320
|
+
owner: user,
|
|
321
|
+
expireDate: block.timestamp + 366 days,
|
|
322
|
+
customMetadataMaxSlots: 0,
|
|
323
|
+
offerMaxSlots: 0,
|
|
324
|
+
flagNotAUsername: 0x00
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
nameServiceNonce[user][nonce] = true;
|
|
328
|
+
|
|
329
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
330
|
+
makeCaPay(
|
|
331
|
+
msg.sender,
|
|
332
|
+
(50 * Evvm(evvmAddress.current).getRewardAmount()) +
|
|
333
|
+
priorityFee_EVVM
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
delete identityDetails[_key];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @notice Creates a marketplace offer to purchase a username
|
|
342
|
+
* @dev Locks the offer amount in the contract until withdrawn or accepted
|
|
343
|
+
* @param user Address making the offer
|
|
344
|
+
* @param username Target username for the offer
|
|
345
|
+
* @param expireDate Timestamp when the offer expires
|
|
346
|
+
* @param amount Amount being offered in Principal Tokens
|
|
347
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
348
|
+
* @param signature Signature proving authorization for this operation
|
|
349
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
350
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
351
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
352
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
353
|
+
* @return offerID Unique identifier for the created offer
|
|
354
|
+
*/
|
|
355
|
+
function makeOffer(
|
|
356
|
+
address user,
|
|
357
|
+
string memory username,
|
|
358
|
+
uint256 expireDate,
|
|
359
|
+
uint256 amount,
|
|
360
|
+
uint256 nonce,
|
|
361
|
+
bytes memory signature,
|
|
362
|
+
uint256 priorityFee_EVVM,
|
|
363
|
+
uint256 nonce_EVVM,
|
|
364
|
+
bool priorityFlag_EVVM,
|
|
365
|
+
bytes memory signature_EVVM
|
|
366
|
+
) public verifyIfNonceIsAvailable(user, nonce) returns (uint256 offerID) {
|
|
367
|
+
if (
|
|
368
|
+
identityDetails[username].flagNotAUsername == 0x01 ||
|
|
369
|
+
!verifyIfIdentityExists(username) ||
|
|
370
|
+
amount == 0 ||
|
|
371
|
+
expireDate <= block.timestamp
|
|
372
|
+
) revert ErrorsLib.PreRegistrationNotValid();
|
|
373
|
+
|
|
374
|
+
if (
|
|
375
|
+
!SignatureUtils.verifyMessageSignedForMakeOffer(
|
|
376
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
377
|
+
user,
|
|
378
|
+
username,
|
|
379
|
+
expireDate,
|
|
380
|
+
amount,
|
|
381
|
+
nonce,
|
|
382
|
+
signature
|
|
383
|
+
)
|
|
384
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
385
|
+
|
|
386
|
+
makePay(
|
|
387
|
+
user,
|
|
388
|
+
amount,
|
|
389
|
+
priorityFee_EVVM,
|
|
390
|
+
nonce_EVVM,
|
|
391
|
+
priorityFlag_EVVM,
|
|
392
|
+
signature_EVVM
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
while (usernameOffers[username][offerID].offerer != address(0)) {
|
|
396
|
+
offerID++;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
usernameOffers[username][offerID] = OfferMetadata({
|
|
400
|
+
offerer: user,
|
|
401
|
+
expireDate: expireDate,
|
|
402
|
+
amount: ((amount * 995) / 1000)
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
makeCaPay(
|
|
406
|
+
msg.sender,
|
|
407
|
+
Evvm(evvmAddress.current).getRewardAmount() +
|
|
408
|
+
((amount * 125) / 100_000) +
|
|
409
|
+
priorityFee_EVVM
|
|
410
|
+
);
|
|
411
|
+
principalTokenTokenLockedForWithdrawOffers +=
|
|
412
|
+
((amount * 995) / 1000) +
|
|
413
|
+
(amount / 800);
|
|
414
|
+
|
|
415
|
+
if (offerID > identityDetails[username].offerMaxSlots) {
|
|
416
|
+
identityDetails[username].offerMaxSlots++;
|
|
417
|
+
} else if (identityDetails[username].offerMaxSlots == 0) {
|
|
418
|
+
identityDetails[username].offerMaxSlots++;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
nameServiceNonce[user][nonce] = true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @notice Withdraws a marketplace offer and refunds the locked tokens
|
|
426
|
+
* @dev Can only be called by the offer creator before expiration
|
|
427
|
+
* @param user Address that made the original offer
|
|
428
|
+
* @param username Username the offer was made for
|
|
429
|
+
* @param offerID Unique identifier of the offer to withdraw
|
|
430
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
431
|
+
* @param signature Signature proving authorization for this operation
|
|
432
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
433
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
434
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
435
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
436
|
+
*/
|
|
437
|
+
function withdrawOffer(
|
|
438
|
+
address user,
|
|
439
|
+
string memory username,
|
|
440
|
+
uint256 offerID,
|
|
441
|
+
uint256 nonce,
|
|
442
|
+
bytes memory signature,
|
|
443
|
+
uint256 priorityFee_EVVM,
|
|
444
|
+
uint256 nonce_EVVM,
|
|
445
|
+
bool priorityFlag_EVVM,
|
|
446
|
+
bytes memory signature_EVVM
|
|
447
|
+
) public verifyIfNonceIsAvailable(user, nonce) {
|
|
448
|
+
if (usernameOffers[username][offerID].offerer != user)
|
|
449
|
+
revert ErrorsLib.UserIsNotOwnerOfOffer();
|
|
450
|
+
|
|
451
|
+
if (
|
|
452
|
+
!SignatureUtils.verifyMessageSignedForWithdrawOffer(
|
|
453
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
454
|
+
user,
|
|
455
|
+
username,
|
|
456
|
+
offerID,
|
|
457
|
+
nonce,
|
|
458
|
+
signature
|
|
459
|
+
)
|
|
460
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
461
|
+
|
|
462
|
+
if (priorityFee_EVVM > 0) {
|
|
463
|
+
makePay(
|
|
464
|
+
user,
|
|
465
|
+
0,
|
|
466
|
+
priorityFee_EVVM,
|
|
467
|
+
nonce_EVVM,
|
|
468
|
+
priorityFlag_EVVM,
|
|
469
|
+
signature_EVVM
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
makeCaPay(user, usernameOffers[username][offerID].amount);
|
|
474
|
+
|
|
475
|
+
usernameOffers[username][offerID].offerer = address(0);
|
|
476
|
+
|
|
477
|
+
makeCaPay(
|
|
478
|
+
msg.sender,
|
|
479
|
+
Evvm(evvmAddress.current).getRewardAmount() +
|
|
480
|
+
((usernameOffers[username][offerID].amount * 1) / 796) +
|
|
481
|
+
priorityFee_EVVM
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
principalTokenTokenLockedForWithdrawOffers -=
|
|
485
|
+
(usernameOffers[username][offerID].amount) +
|
|
486
|
+
(((usernameOffers[username][offerID].amount * 1) / 199) / 4);
|
|
487
|
+
|
|
488
|
+
nameServiceNonce[user][nonce] = true;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @notice Accepts a marketplace offer and transfers username ownership
|
|
493
|
+
* @dev Can only be called by the current username owner before offer expiration
|
|
494
|
+
* @param user Address of the current username owner
|
|
495
|
+
* @param username Username being sold
|
|
496
|
+
* @param offerID Unique identifier of the offer to accept
|
|
497
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
498
|
+
* @param signature Signature proving authorization for this operation
|
|
499
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
500
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
501
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
502
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
503
|
+
*/
|
|
504
|
+
function acceptOffer(
|
|
505
|
+
address user,
|
|
506
|
+
string memory username,
|
|
507
|
+
uint256 offerID,
|
|
508
|
+
uint256 nonce,
|
|
509
|
+
bytes memory signature,
|
|
510
|
+
uint256 priorityFee_EVVM,
|
|
511
|
+
uint256 nonce_EVVM,
|
|
512
|
+
bool priorityFlag_EVVM,
|
|
513
|
+
bytes memory signature_EVVM
|
|
514
|
+
)
|
|
515
|
+
public
|
|
516
|
+
onlyOwnerOfIdentity(user, username)
|
|
517
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
518
|
+
{
|
|
519
|
+
if (
|
|
520
|
+
usernameOffers[username][offerID].offerer == address(0) ||
|
|
521
|
+
usernameOffers[username][offerID].expireDate < block.timestamp
|
|
522
|
+
) revert ErrorsLib.AcceptOfferVerificationFailed();
|
|
523
|
+
|
|
524
|
+
if (
|
|
525
|
+
!SignatureUtils.verifyMessageSignedForAcceptOffer(
|
|
526
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
527
|
+
user,
|
|
528
|
+
username,
|
|
529
|
+
offerID,
|
|
530
|
+
nonce,
|
|
531
|
+
signature
|
|
532
|
+
)
|
|
533
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
534
|
+
|
|
535
|
+
if (priorityFee_EVVM > 0) {
|
|
536
|
+
makePay(
|
|
537
|
+
user,
|
|
538
|
+
0,
|
|
539
|
+
priorityFee_EVVM,
|
|
540
|
+
nonce_EVVM,
|
|
541
|
+
priorityFlag_EVVM,
|
|
542
|
+
signature_EVVM
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
makeCaPay(user, usernameOffers[username][offerID].amount);
|
|
547
|
+
|
|
548
|
+
identityDetails[username].owner = usernameOffers[username][offerID]
|
|
549
|
+
.offerer;
|
|
550
|
+
|
|
551
|
+
usernameOffers[username][offerID].offerer = address(0);
|
|
552
|
+
|
|
553
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
554
|
+
makeCaPay(
|
|
555
|
+
msg.sender,
|
|
556
|
+
(Evvm(evvmAddress.current).getRewardAmount()) +
|
|
557
|
+
(((usernameOffers[username][offerID].amount * 1) / 199) /
|
|
558
|
+
4) +
|
|
559
|
+
priorityFee_EVVM
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
principalTokenTokenLockedForWithdrawOffers -=
|
|
564
|
+
(usernameOffers[username][offerID].amount) +
|
|
565
|
+
(((usernameOffers[username][offerID].amount * 1) / 199) / 4);
|
|
566
|
+
|
|
567
|
+
nameServiceNonce[user][nonce] = true;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* @notice Renews a username registration for another year
|
|
572
|
+
* @dev Pricing varies based on timing and market demand for the username
|
|
573
|
+
*
|
|
574
|
+
* Pricing Rules:
|
|
575
|
+
* - Free renewal if done within 1 year of expiration (limited time offer)
|
|
576
|
+
* - Variable cost based on highest active offer (minimum 500 Principal Token)
|
|
577
|
+
* - Fixed 500,000 Principal Token if renewed more than 1 year before expiration
|
|
578
|
+
* - Can be renewed up to 100 years in advance
|
|
579
|
+
*
|
|
580
|
+
* @param user Address of the username owner
|
|
581
|
+
* @param username Username to renew
|
|
582
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
583
|
+
* @param signature Signature proving authorization for this operation
|
|
584
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
585
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
586
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
587
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
588
|
+
*/
|
|
589
|
+
function renewUsername(
|
|
590
|
+
address user,
|
|
591
|
+
string memory username,
|
|
592
|
+
uint256 nonce,
|
|
593
|
+
bytes memory signature,
|
|
594
|
+
uint256 priorityFee_EVVM,
|
|
595
|
+
uint256 nonce_EVVM,
|
|
596
|
+
bool priorityFlag_EVVM,
|
|
597
|
+
bytes memory signature_EVVM
|
|
598
|
+
)
|
|
599
|
+
public
|
|
600
|
+
onlyOwnerOfIdentity(user, username)
|
|
601
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
602
|
+
{
|
|
603
|
+
if (
|
|
604
|
+
identityDetails[username].flagNotAUsername == 0x01 ||
|
|
605
|
+
identityDetails[username].expireDate > block.timestamp + 36500 days
|
|
606
|
+
) revert ErrorsLib.RenewUsernameVerificationFailed();
|
|
607
|
+
|
|
608
|
+
if (
|
|
609
|
+
!SignatureUtils.verifyMessageSignedForRenewUsername(
|
|
610
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
611
|
+
user,
|
|
612
|
+
username,
|
|
613
|
+
nonce,
|
|
614
|
+
signature
|
|
615
|
+
)
|
|
616
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
617
|
+
|
|
618
|
+
uint256 priceOfRenew = seePriceToRenew(username);
|
|
619
|
+
|
|
620
|
+
makePay(
|
|
621
|
+
user,
|
|
622
|
+
priceOfRenew,
|
|
623
|
+
priorityFee_EVVM,
|
|
624
|
+
nonce_EVVM,
|
|
625
|
+
priorityFlag_EVVM,
|
|
626
|
+
signature_EVVM
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
630
|
+
makeCaPay(
|
|
631
|
+
msg.sender,
|
|
632
|
+
Evvm(evvmAddress.current).getRewardAmount() +
|
|
633
|
+
((priceOfRenew * 50) / 100) +
|
|
634
|
+
priorityFee_EVVM
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
identityDetails[username].expireDate += 366 days;
|
|
639
|
+
nameServiceNonce[user][nonce] = true;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* @notice Adds custom metadata to a username following a standardized schema format
|
|
644
|
+
* @dev Metadata follows format: [schema]:[subschema]>[value]
|
|
645
|
+
*
|
|
646
|
+
* Standard Format Examples:
|
|
647
|
+
* - memberOf:>EVVM
|
|
648
|
+
* - socialMedia:x>jistro (Twitter/X handle)
|
|
649
|
+
* - email:dev>jistro[at]evvm.org (development email)
|
|
650
|
+
* - email:callme>contact[at]jistro.xyz (contact email)
|
|
651
|
+
*
|
|
652
|
+
* Schema Guidelines:
|
|
653
|
+
* - Based on https://schema.org/docs/schemas.html
|
|
654
|
+
* - ':' separates schema from subschema
|
|
655
|
+
* - '>' separates metadata from value
|
|
656
|
+
* - Pad with spaces if schema/subschema < 5 characters
|
|
657
|
+
* - Use "socialMedia" for social networks with network name as subschema
|
|
658
|
+
*
|
|
659
|
+
* @param user Address of the username owner
|
|
660
|
+
* @param identity Username to add metadata to
|
|
661
|
+
* @param value Metadata string following the standardized format
|
|
662
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
663
|
+
* @param signature Signature proving authorization for this operation
|
|
664
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
665
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
666
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
667
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
668
|
+
*/
|
|
669
|
+
function addCustomMetadata(
|
|
670
|
+
address user,
|
|
671
|
+
string memory identity,
|
|
672
|
+
string memory value,
|
|
673
|
+
uint256 nonce,
|
|
674
|
+
bytes memory signature,
|
|
675
|
+
uint256 priorityFee_EVVM,
|
|
676
|
+
uint256 nonce_EVVM,
|
|
677
|
+
bool priorityFlag_EVVM,
|
|
678
|
+
bytes memory signature_EVVM
|
|
679
|
+
)
|
|
680
|
+
public
|
|
681
|
+
onlyOwnerOfIdentity(user, identity)
|
|
682
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
683
|
+
{
|
|
684
|
+
if (bytes(value).length == 0) revert ErrorsLib.EmptyCustomMetadata();
|
|
685
|
+
|
|
686
|
+
if (
|
|
687
|
+
!SignatureUtils.verifyMessageSignedForAddCustomMetadata(
|
|
688
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
689
|
+
user,
|
|
690
|
+
identity,
|
|
691
|
+
value,
|
|
692
|
+
nonce,
|
|
693
|
+
signature
|
|
694
|
+
)
|
|
695
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
696
|
+
|
|
697
|
+
makePay(
|
|
698
|
+
user,
|
|
699
|
+
getPriceToAddCustomMetadata(),
|
|
700
|
+
priorityFee_EVVM,
|
|
701
|
+
nonce_EVVM,
|
|
702
|
+
priorityFlag_EVVM,
|
|
703
|
+
signature_EVVM
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
707
|
+
makeCaPay(
|
|
708
|
+
msg.sender,
|
|
709
|
+
(5 * Evvm(evvmAddress.current).getRewardAmount()) +
|
|
710
|
+
((getPriceToAddCustomMetadata() * 50) / 100) +
|
|
711
|
+
priorityFee_EVVM
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
identityCustomMetadata[identity][
|
|
716
|
+
identityDetails[identity].customMetadataMaxSlots
|
|
717
|
+
] = value;
|
|
718
|
+
|
|
719
|
+
identityDetails[identity].customMetadataMaxSlots++;
|
|
720
|
+
nameServiceNonce[user][nonce] = true;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* @notice Removes a specific custom metadata entry by key and reorders the array
|
|
725
|
+
* @dev Shifts all subsequent metadata entries to fill the gap after removal
|
|
726
|
+
* @param user Address of the username owner
|
|
727
|
+
* @param identity Username to remove metadata from
|
|
728
|
+
* @param key Index of the metadata entry to remove
|
|
729
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
730
|
+
* @param signature Signature proving authorization for this operation
|
|
731
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
732
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
733
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
734
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
735
|
+
*/
|
|
736
|
+
function removeCustomMetadata(
|
|
737
|
+
address user,
|
|
738
|
+
string memory identity,
|
|
739
|
+
uint256 key,
|
|
740
|
+
uint256 nonce,
|
|
741
|
+
bytes memory signature,
|
|
742
|
+
uint256 priorityFee_EVVM,
|
|
743
|
+
uint256 nonce_EVVM,
|
|
744
|
+
bool priorityFlag_EVVM,
|
|
745
|
+
bytes memory signature_EVVM
|
|
746
|
+
)
|
|
747
|
+
public
|
|
748
|
+
onlyOwnerOfIdentity(user, identity)
|
|
749
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
750
|
+
{
|
|
751
|
+
if (
|
|
752
|
+
!SignatureUtils.verifyMessageSignedForRemoveCustomMetadata(
|
|
753
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
754
|
+
user,
|
|
755
|
+
identity,
|
|
756
|
+
key,
|
|
757
|
+
nonce,
|
|
758
|
+
signature
|
|
759
|
+
)
|
|
760
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
761
|
+
|
|
762
|
+
if (identityDetails[identity].customMetadataMaxSlots <= key)
|
|
763
|
+
revert ErrorsLib.InvalidKey();
|
|
764
|
+
|
|
765
|
+
makePay(
|
|
766
|
+
user,
|
|
767
|
+
getPriceToRemoveCustomMetadata(),
|
|
768
|
+
priorityFee_EVVM,
|
|
769
|
+
nonce_EVVM,
|
|
770
|
+
priorityFlag_EVVM,
|
|
771
|
+
signature_EVVM
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
if (identityDetails[identity].customMetadataMaxSlots == key) {
|
|
775
|
+
delete identityCustomMetadata[identity][key];
|
|
776
|
+
} else {
|
|
777
|
+
for (
|
|
778
|
+
uint256 i = key;
|
|
779
|
+
i < identityDetails[identity].customMetadataMaxSlots;
|
|
780
|
+
i++
|
|
781
|
+
) {
|
|
782
|
+
identityCustomMetadata[identity][i] = identityCustomMetadata[
|
|
783
|
+
identity
|
|
784
|
+
][i + 1];
|
|
785
|
+
}
|
|
786
|
+
delete identityCustomMetadata[identity][
|
|
787
|
+
identityDetails[identity].customMetadataMaxSlots
|
|
788
|
+
];
|
|
789
|
+
}
|
|
790
|
+
identityDetails[identity].customMetadataMaxSlots--;
|
|
791
|
+
nameServiceNonce[user][nonce] = true;
|
|
792
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
793
|
+
makeCaPay(
|
|
794
|
+
msg.sender,
|
|
795
|
+
(5 * Evvm(evvmAddress.current).getRewardAmount()) +
|
|
796
|
+
priorityFee_EVVM
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* @notice Removes all custom metadata entries for a username
|
|
803
|
+
* @dev More gas-efficient than removing entries individually
|
|
804
|
+
* @param user Address of the username owner
|
|
805
|
+
* @param identity Username to flush all metadata from
|
|
806
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
807
|
+
* @param signature Signature proving authorization for this operation
|
|
808
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
809
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
810
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
811
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
812
|
+
*/
|
|
813
|
+
function flushCustomMetadata(
|
|
814
|
+
address user,
|
|
815
|
+
string memory identity,
|
|
816
|
+
uint256 nonce,
|
|
817
|
+
bytes memory signature,
|
|
818
|
+
uint256 priorityFee_EVVM,
|
|
819
|
+
uint256 nonce_EVVM,
|
|
820
|
+
bool priorityFlag_EVVM,
|
|
821
|
+
bytes memory signature_EVVM
|
|
822
|
+
)
|
|
823
|
+
public
|
|
824
|
+
onlyOwnerOfIdentity(user, identity)
|
|
825
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
826
|
+
{
|
|
827
|
+
if (
|
|
828
|
+
!SignatureUtils.verifyMessageSignedForFlushCustomMetadata(
|
|
829
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
830
|
+
user,
|
|
831
|
+
identity,
|
|
832
|
+
nonce,
|
|
833
|
+
signature
|
|
834
|
+
)
|
|
835
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
836
|
+
|
|
837
|
+
if (identityDetails[identity].customMetadataMaxSlots == 0)
|
|
838
|
+
revert ErrorsLib.EmptyCustomMetadata();
|
|
839
|
+
|
|
840
|
+
makePay(
|
|
841
|
+
user,
|
|
842
|
+
getPriceToFlushCustomMetadata(identity),
|
|
843
|
+
priorityFee_EVVM,
|
|
844
|
+
nonce_EVVM,
|
|
845
|
+
priorityFlag_EVVM,
|
|
846
|
+
signature_EVVM
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
for (
|
|
850
|
+
uint256 i = 0;
|
|
851
|
+
i < identityDetails[identity].customMetadataMaxSlots;
|
|
852
|
+
i++
|
|
853
|
+
) {
|
|
854
|
+
delete identityCustomMetadata[identity][i];
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (Evvm(evvmAddress.current).isAddressStaker(msg.sender)) {
|
|
858
|
+
makeCaPay(
|
|
859
|
+
msg.sender,
|
|
860
|
+
((5 * Evvm(evvmAddress.current).getRewardAmount()) *
|
|
861
|
+
identityDetails[identity].customMetadataMaxSlots) +
|
|
862
|
+
priorityFee_EVVM
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
identityDetails[identity].customMetadataMaxSlots = 0;
|
|
867
|
+
nameServiceNonce[user][nonce] = true;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* @notice Completely removes a username registration and all associated data
|
|
872
|
+
* @dev Deletes the username, all custom metadata, and makes it available for re-registration
|
|
873
|
+
* @param user Address of the username owner
|
|
874
|
+
* @param username Username to completely remove from the system
|
|
875
|
+
* @param nonce Unique nonce to prevent replay attacks
|
|
876
|
+
* @param signature Signature proving authorization for this operation
|
|
877
|
+
* @param priorityFee_EVVM Priority fee for faster transaction processing
|
|
878
|
+
* @param nonce_EVVM Nonce for the EVVM payment transaction
|
|
879
|
+
* @param priorityFlag_EVVM True for async payment, false for sync payment
|
|
880
|
+
* @param signature_EVVM Signature for the EVVM payment transaction
|
|
881
|
+
*/
|
|
882
|
+
function flushUsername(
|
|
883
|
+
address user,
|
|
884
|
+
string memory username,
|
|
885
|
+
uint256 nonce,
|
|
886
|
+
bytes memory signature,
|
|
887
|
+
uint256 priorityFee_EVVM,
|
|
888
|
+
uint256 nonce_EVVM,
|
|
889
|
+
bool priorityFlag_EVVM,
|
|
890
|
+
bytes memory signature_EVVM
|
|
891
|
+
)
|
|
892
|
+
public
|
|
893
|
+
verifyIfNonceIsAvailable(user, nonce)
|
|
894
|
+
onlyOwnerOfIdentity(user, username)
|
|
895
|
+
{
|
|
896
|
+
if (
|
|
897
|
+
block.timestamp >= identityDetails[username].expireDate ||
|
|
898
|
+
identityDetails[username].flagNotAUsername == 0x01
|
|
899
|
+
) revert ErrorsLib.FlushUsernameVerificationFailed();
|
|
900
|
+
|
|
901
|
+
if (
|
|
902
|
+
!SignatureUtils.verifyMessageSignedForFlushUsername(
|
|
903
|
+
Evvm(evvmAddress.current).getEvvmID(),
|
|
904
|
+
user,
|
|
905
|
+
username,
|
|
906
|
+
nonce,
|
|
907
|
+
signature
|
|
908
|
+
)
|
|
909
|
+
) revert ErrorsLib.InvalidSignatureOnNameService();
|
|
910
|
+
|
|
911
|
+
makePay(
|
|
912
|
+
user,
|
|
913
|
+
getPriceToFlushUsername(username),
|
|
914
|
+
priorityFee_EVVM,
|
|
915
|
+
nonce_EVVM,
|
|
916
|
+
priorityFlag_EVVM,
|
|
917
|
+
signature_EVVM
|
|
918
|
+
);
|
|
919
|
+
|
|
920
|
+
for (
|
|
921
|
+
uint256 i = 0;
|
|
922
|
+
i < identityDetails[username].customMetadataMaxSlots;
|
|
923
|
+
i++
|
|
924
|
+
) {
|
|
925
|
+
delete identityCustomMetadata[username][i];
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
makeCaPay(
|
|
929
|
+
msg.sender,
|
|
930
|
+
((5 * Evvm(evvmAddress.current).getRewardAmount()) *
|
|
931
|
+
identityDetails[username].customMetadataMaxSlots) +
|
|
932
|
+
priorityFee_EVVM
|
|
933
|
+
);
|
|
934
|
+
|
|
935
|
+
identityDetails[username] = IdentityBaseMetadata({
|
|
936
|
+
owner: address(0),
|
|
937
|
+
expireDate: 0,
|
|
938
|
+
customMetadataMaxSlots: 0,
|
|
939
|
+
offerMaxSlots: identityDetails[username].offerMaxSlots,
|
|
940
|
+
flagNotAUsername: 0x00
|
|
941
|
+
});
|
|
942
|
+
nameServiceNonce[user][nonce] = true;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
//█ Administrative Functions with Time-Delayed Governance ████████████████████████████████████
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* @notice Proposes a new admin address with 1-day time delay
|
|
949
|
+
* @dev Part of the time-delayed governance system for admin changes
|
|
950
|
+
* @param _adminToPropose Address of the proposed new admin
|
|
951
|
+
*/
|
|
952
|
+
function proposeAdmin(address _adminToPropose) public onlyAdmin {
|
|
953
|
+
if (_adminToPropose == address(0) || _adminToPropose == admin.current) {
|
|
954
|
+
revert();
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
admin.proposal = _adminToPropose;
|
|
958
|
+
admin.timeToAccept = block.timestamp + 1 days;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* @notice Cancels the current admin proposal
|
|
963
|
+
* @dev Only the current admin can cancel pending proposals
|
|
964
|
+
*/
|
|
965
|
+
function cancelProposeAdmin() public onlyAdmin {
|
|
966
|
+
admin.proposal = address(0);
|
|
967
|
+
admin.timeToAccept = 0;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* @notice Accepts the admin proposal and becomes the new admin
|
|
972
|
+
* @dev Can only be called by the proposed admin after the time delay has passed
|
|
973
|
+
*/
|
|
974
|
+
function acceptProposeAdmin() public {
|
|
975
|
+
if (admin.proposal != msg.sender) {
|
|
976
|
+
revert();
|
|
977
|
+
}
|
|
978
|
+
if (block.timestamp < admin.timeToAccept) {
|
|
979
|
+
revert();
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
admin = AddressTypeProposal({
|
|
983
|
+
current: admin.proposal,
|
|
984
|
+
proposal: address(0),
|
|
985
|
+
timeToAccept: 0
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* @notice Proposes to withdraw Principal Tokens from the contract
|
|
991
|
+
* @dev Amount must be available after reserving funds for operations and locked offers
|
|
992
|
+
* @param _amount Amount of Principal Tokens to withdraw
|
|
993
|
+
*/
|
|
994
|
+
function proposeWithdrawPrincipalTokens(uint256 _amount) public onlyAdmin {
|
|
995
|
+
if (
|
|
996
|
+
Evvm(evvmAddress.current).getBalance(
|
|
997
|
+
address(this),
|
|
998
|
+
PRINCIPAL_TOKEN_ADDRESS
|
|
999
|
+
) -
|
|
1000
|
+
(5083 +
|
|
1001
|
+
Evvm(evvmAddress.current).getRewardAmount() +
|
|
1002
|
+
principalTokenTokenLockedForWithdrawOffers) <
|
|
1003
|
+
_amount ||
|
|
1004
|
+
_amount == 0
|
|
1005
|
+
) {
|
|
1006
|
+
revert();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
amountToWithdrawTokens.proposal = _amount;
|
|
1010
|
+
amountToWithdrawTokens.timeToAccept = block.timestamp + 1 days;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* @notice Cancels the pending token withdrawal proposal
|
|
1015
|
+
* @dev Only the current admin can cancel pending proposals
|
|
1016
|
+
*/
|
|
1017
|
+
function cancelWithdrawPrincipalTokens() public onlyAdmin {
|
|
1018
|
+
amountToWithdrawTokens.proposal = 0;
|
|
1019
|
+
amountToWithdrawTokens.timeToAccept = 0;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* @notice Executes the approved token withdrawal
|
|
1024
|
+
* @dev Can only be called after the time delay has passed
|
|
1025
|
+
*/
|
|
1026
|
+
function claimWithdrawPrincipalTokens() public onlyAdmin {
|
|
1027
|
+
if (block.timestamp < amountToWithdrawTokens.timeToAccept) {
|
|
1028
|
+
revert();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
makeCaPay(admin.current, amountToWithdrawTokens.proposal);
|
|
1032
|
+
|
|
1033
|
+
amountToWithdrawTokens.proposal = 0;
|
|
1034
|
+
amountToWithdrawTokens.timeToAccept = 0;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* @notice Proposes to change the EVVM contract address
|
|
1039
|
+
* @dev Critical function that affects payment processing integration
|
|
1040
|
+
* @param _newEvvmAddress Address of the new EVVM contract
|
|
1041
|
+
*/
|
|
1042
|
+
function proposeChangeEvvmAddress(
|
|
1043
|
+
address _newEvvmAddress
|
|
1044
|
+
) public onlyAdmin {
|
|
1045
|
+
if (_newEvvmAddress == address(0)) {
|
|
1046
|
+
revert();
|
|
1047
|
+
}
|
|
1048
|
+
evvmAddress.proposal = _newEvvmAddress;
|
|
1049
|
+
evvmAddress.timeToAccept = block.timestamp + 1 days;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* @notice Cancels the pending EVVM address change proposal
|
|
1054
|
+
* @dev Only the current admin can cancel pending proposals
|
|
1055
|
+
*/
|
|
1056
|
+
function cancelChangeEvvmAddress() public onlyAdmin {
|
|
1057
|
+
evvmAddress.proposal = address(0);
|
|
1058
|
+
evvmAddress.timeToAccept = 0;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* @notice Executes the approved EVVM address change
|
|
1063
|
+
* @dev Can only be called after the time delay has passed
|
|
1064
|
+
*/
|
|
1065
|
+
function acceptChangeEvvmAddress() public onlyAdmin {
|
|
1066
|
+
if (block.timestamp < evvmAddress.timeToAccept) {
|
|
1067
|
+
revert();
|
|
1068
|
+
}
|
|
1069
|
+
evvmAddress = AddressTypeProposal({
|
|
1070
|
+
current: evvmAddress.proposal,
|
|
1071
|
+
proposal: address(0),
|
|
1072
|
+
timeToAccept: 0
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
//█ Utility Functions ████████████████████████████████████████████████████████████████████████
|
|
1077
|
+
|
|
1078
|
+
//█ EVVM Payment Integration ██████████████████████████████████████████████
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* @notice Internal function to handle payments through the EVVM contract
|
|
1082
|
+
* @dev Supports both synchronous and asynchronous payment modes
|
|
1083
|
+
* @param user Address making the payment
|
|
1084
|
+
* @param amount Amount to pay in Principal Tokens
|
|
1085
|
+
* @param priorityFee Additional priority fee for faster processing
|
|
1086
|
+
* @param nonce Nonce for the EVVM transaction
|
|
1087
|
+
* @param priorityFlag True for async payment, false for sync payment
|
|
1088
|
+
* @param signature Signature authorizing the payment
|
|
1089
|
+
*/
|
|
1090
|
+
function makePay(
|
|
1091
|
+
address user,
|
|
1092
|
+
uint256 amount,
|
|
1093
|
+
uint256 priorityFee,
|
|
1094
|
+
uint256 nonce,
|
|
1095
|
+
bool priorityFlag,
|
|
1096
|
+
bytes memory signature
|
|
1097
|
+
) internal {
|
|
1098
|
+
Evvm(evvmAddress.current).pay(
|
|
1099
|
+
user,
|
|
1100
|
+
address(this),
|
|
1101
|
+
"",
|
|
1102
|
+
PRINCIPAL_TOKEN_ADDRESS,
|
|
1103
|
+
amount,
|
|
1104
|
+
priorityFee,
|
|
1105
|
+
nonce,
|
|
1106
|
+
priorityFlag,
|
|
1107
|
+
address(this),
|
|
1108
|
+
signature
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* @notice Internal function to distribute Principal Tokens to users
|
|
1114
|
+
* @dev Calls the EVVM contract's caPay function for token distribution
|
|
1115
|
+
* @param user Address to receive the tokens
|
|
1116
|
+
* @param amount Amount of Principal Tokens to distribute
|
|
1117
|
+
*/
|
|
1118
|
+
function makeCaPay(address user, uint256 amount) internal {
|
|
1119
|
+
Evvm(evvmAddress.current).caPay(user, PRINCIPAL_TOKEN_ADDRESS, amount);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
//█ Identity Validation Functions ███████████████████████████████████████████████████████████████
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* @notice Validates username format according to system rules
|
|
1126
|
+
* @dev Username must be at least 4 characters, start with a letter, and contain only letters/digits
|
|
1127
|
+
* @param username The username string to validate
|
|
1128
|
+
*/
|
|
1129
|
+
function isValidUsername(string memory username) internal pure {
|
|
1130
|
+
bytes memory usernameBytes = bytes(username);
|
|
1131
|
+
|
|
1132
|
+
// Check if username length is at least 4 characters
|
|
1133
|
+
if (usernameBytes.length < 4) revert ErrorsLib.InvalidUsername(0x01);
|
|
1134
|
+
|
|
1135
|
+
// Check if username begins with a letter
|
|
1136
|
+
if (!_isLetter(usernameBytes[0]))
|
|
1137
|
+
revert ErrorsLib.InvalidUsername(0x02);
|
|
1138
|
+
|
|
1139
|
+
// Iterate through each character in the username
|
|
1140
|
+
for (uint256 i = 0; i < usernameBytes.length; i++) {
|
|
1141
|
+
// Check if character is not a digit or letter
|
|
1142
|
+
if (!_isDigit(usernameBytes[i]) && !_isLetter(usernameBytes[i])) {
|
|
1143
|
+
revert ErrorsLib.InvalidUsername(0x03);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* @notice Validates phone number format
|
|
1150
|
+
* @dev Phone number must be 6-19 digits only
|
|
1151
|
+
* @param _phoneNumber The phone number string to validate
|
|
1152
|
+
* @return True if valid phone number format
|
|
1153
|
+
*/
|
|
1154
|
+
function isValidPhoneNumberNumber(
|
|
1155
|
+
string memory _phoneNumber
|
|
1156
|
+
) internal pure returns (bool) {
|
|
1157
|
+
bytes memory _telephoneNumberBytes = bytes(_phoneNumber);
|
|
1158
|
+
if (
|
|
1159
|
+
_telephoneNumberBytes.length < 20 &&
|
|
1160
|
+
_telephoneNumberBytes.length > 5
|
|
1161
|
+
) {
|
|
1162
|
+
revert();
|
|
1163
|
+
}
|
|
1164
|
+
for (uint256 i = 0; i < _telephoneNumberBytes.length; i++) {
|
|
1165
|
+
if (!_isDigit(_telephoneNumberBytes[i])) {
|
|
1166
|
+
revert();
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return true;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* @notice Validates email address format
|
|
1174
|
+
* @dev Checks for proper email structure: prefix(3+ chars) + @ + domain(3+ chars) + . + TLD(2+ chars)
|
|
1175
|
+
* @param _email The email address string to validate
|
|
1176
|
+
* @return True if valid email format
|
|
1177
|
+
*/
|
|
1178
|
+
function isValidEmail(string memory _email) internal pure returns (bool) {
|
|
1179
|
+
bytes memory _emailBytes = bytes(_email);
|
|
1180
|
+
uint256 lengthCount = 0;
|
|
1181
|
+
bytes1 flagVerify = 0x00;
|
|
1182
|
+
for (uint point = 0; point < _emailBytes.length; point++) {
|
|
1183
|
+
//step 1 0x00 prefix
|
|
1184
|
+
if (flagVerify == 0x00) {
|
|
1185
|
+
if (_isOnlyEmailPrefixCharacters(_emailBytes[point])) {
|
|
1186
|
+
lengthCount++;
|
|
1187
|
+
} else {
|
|
1188
|
+
if (_isAAt(_emailBytes[point])) {
|
|
1189
|
+
flagVerify = 0x01;
|
|
1190
|
+
} else {
|
|
1191
|
+
revert();
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
//step 2 0x01 count the prefix length
|
|
1197
|
+
if (flagVerify == 0x01) {
|
|
1198
|
+
if (lengthCount < 3) {
|
|
1199
|
+
revert();
|
|
1200
|
+
} else {
|
|
1201
|
+
flagVerify = 0x02;
|
|
1202
|
+
lengthCount = 0;
|
|
1203
|
+
point++;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
//step 3 0x02 domain name
|
|
1208
|
+
if (flagVerify == 0x02) {
|
|
1209
|
+
if (_isLetter(_emailBytes[point])) {
|
|
1210
|
+
lengthCount++;
|
|
1211
|
+
} else {
|
|
1212
|
+
if (_isAPoint(_emailBytes[point])) {
|
|
1213
|
+
flagVerify = 0x03;
|
|
1214
|
+
} else {
|
|
1215
|
+
revert();
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
//step 4 0x03 count the domain name length
|
|
1221
|
+
if (flagVerify == 0x03) {
|
|
1222
|
+
if (lengthCount < 3) {
|
|
1223
|
+
revert();
|
|
1224
|
+
} else {
|
|
1225
|
+
flagVerify = 0x04;
|
|
1226
|
+
lengthCount = 0;
|
|
1227
|
+
point++;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
//step 5 0x04 top level domain
|
|
1232
|
+
if (flagVerify == 0x04) {
|
|
1233
|
+
if (_isLetter(_emailBytes[point])) {
|
|
1234
|
+
lengthCount++;
|
|
1235
|
+
} else {
|
|
1236
|
+
if (_isAPoint(_emailBytes[point])) {
|
|
1237
|
+
if (lengthCount < 2) {
|
|
1238
|
+
revert();
|
|
1239
|
+
} else {
|
|
1240
|
+
lengthCount = 0;
|
|
1241
|
+
}
|
|
1242
|
+
} else {
|
|
1243
|
+
revert();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
if (flagVerify != 0x04) {
|
|
1250
|
+
revert();
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return true;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/// @dev Checks if a byte represents a digit (0-9)
|
|
1257
|
+
function _isDigit(bytes1 character) private pure returns (bool) {
|
|
1258
|
+
return (character >= 0x30 && character <= 0x39); // ASCII range for digits 0-9
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/// @dev Checks if a byte represents a letter (A-Z or a-z)
|
|
1262
|
+
function _isLetter(bytes1 character) private pure returns (bool) {
|
|
1263
|
+
return ((character >= 0x41 && character <= 0x5A) ||
|
|
1264
|
+
(character >= 0x61 && character <= 0x7A)); // ASCII ranges for letters A-Z and a-z
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/// @dev Checks if a byte represents any symbol character
|
|
1268
|
+
function _isAnySimbol(bytes1 character) private pure returns (bool) {
|
|
1269
|
+
return ((character >= 0x21 && character <= 0x2F) || /// @dev includes characters from "!" to "/"
|
|
1270
|
+
(character >= 0x3A && character <= 0x40) || /// @dev includes characters from ":" to "@"
|
|
1271
|
+
(character >= 0x5B && character <= 0x60) || /// @dev includes characters from "[" to "`"
|
|
1272
|
+
(character >= 0x7B && character <= 0x7E)); /// @dev includes characters from "{" to "~"
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/// @dev Checks if a byte is valid for email prefix (letters, digits, and specific symbols)
|
|
1276
|
+
function _isOnlyEmailPrefixCharacters(
|
|
1277
|
+
bytes1 character
|
|
1278
|
+
) private pure returns (bool) {
|
|
1279
|
+
return (_isLetter(character) ||
|
|
1280
|
+
_isDigit(character) ||
|
|
1281
|
+
(character >= 0x21 && character <= 0x2F) || /// @dev includes characters from "!" to "/"
|
|
1282
|
+
(character >= 0x3A && character <= 0x3F) || /// @dev includes characters from ":" to "?"
|
|
1283
|
+
(character >= 0x5B && character <= 0x60) || /// @dev includes characters from "[" to "`"
|
|
1284
|
+
(character >= 0x7B && character <= 0x7E)); /// @dev includes characters from "{" to "~"
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/// @dev Checks if a byte represents a period/dot character (.)
|
|
1288
|
+
function _isAPoint(bytes1 character) private pure returns (bool) {
|
|
1289
|
+
return character == 0x2E;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/// @dev Checks if a byte represents an at symbol (@)
|
|
1293
|
+
function _isAAt(bytes1 character) private pure returns (bool) {
|
|
1294
|
+
return character == 0x40;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
//█ Username Hashing Functions ███████████████████████████████████████████████████████████████████
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* @notice Creates a hash of username and random number for pre-registration
|
|
1301
|
+
* @dev Used in the commit-reveal scheme to prevent front-running attacks
|
|
1302
|
+
* @param _username The username to hash
|
|
1303
|
+
* @param _randomNumber Random number to add entropy
|
|
1304
|
+
* @return Hash of the username and random number
|
|
1305
|
+
*/
|
|
1306
|
+
function hashUsername(
|
|
1307
|
+
string memory _username,
|
|
1308
|
+
uint256 _randomNumber
|
|
1309
|
+
) public pure returns (bytes32) {
|
|
1310
|
+
return keccak256(abi.encodePacked(_username, _randomNumber));
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
//█ View Functions - Public Data Access ██████████████████████████████████████████████████████████
|
|
1314
|
+
|
|
1315
|
+
//█ Service Functions ████████████████████████████████████████████████████████████████
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* @notice Checks if an identity exists in the system
|
|
1319
|
+
* @dev Handles both pre-registrations and actual username registrations
|
|
1320
|
+
* @param _identity The identity/username to check
|
|
1321
|
+
* @return True if the identity exists and is valid
|
|
1322
|
+
*/
|
|
1323
|
+
function verifyIfIdentityExists(
|
|
1324
|
+
string memory _identity
|
|
1325
|
+
) public view returns (bool) {
|
|
1326
|
+
if (identityDetails[_identity].flagNotAUsername == 0x01) {
|
|
1327
|
+
if (
|
|
1328
|
+
identityDetails[_identity].owner == address(0) ||
|
|
1329
|
+
identityDetails[_identity].expireDate != 0
|
|
1330
|
+
) {
|
|
1331
|
+
return false;
|
|
1332
|
+
} else {
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
} else {
|
|
1336
|
+
if (identityDetails[_identity].expireDate == 0) {
|
|
1337
|
+
return false;
|
|
1338
|
+
} else {
|
|
1339
|
+
return true;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* @notice Strictly verifies if an identity exists and reverts if not found
|
|
1346
|
+
* @dev More strict version that reverts instead of returning false
|
|
1347
|
+
* @param _username The username to verify
|
|
1348
|
+
* @return True if the username exists (will revert if not)
|
|
1349
|
+
*/
|
|
1350
|
+
function strictVerifyIfIdentityExist(
|
|
1351
|
+
string memory _username
|
|
1352
|
+
) public view returns (bool) {
|
|
1353
|
+
if (identityDetails[_username].flagNotAUsername == 0x01) {
|
|
1354
|
+
if (
|
|
1355
|
+
identityDetails[_username].owner == address(0) ||
|
|
1356
|
+
identityDetails[_username].expireDate != 0
|
|
1357
|
+
) {
|
|
1358
|
+
revert();
|
|
1359
|
+
} else {
|
|
1360
|
+
return true;
|
|
1361
|
+
}
|
|
1362
|
+
} else {
|
|
1363
|
+
if (identityDetails[_username].expireDate == 0) {
|
|
1364
|
+
revert();
|
|
1365
|
+
} else {
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* @notice Gets the owner address of a registered identity
|
|
1373
|
+
* @dev Returns the current owner address for any valid identity
|
|
1374
|
+
* @param _username The username to query
|
|
1375
|
+
* @return Address of the username owner
|
|
1376
|
+
*/
|
|
1377
|
+
function getOwnerOfIdentity(
|
|
1378
|
+
string memory _username
|
|
1379
|
+
) public view returns (address) {
|
|
1380
|
+
return identityDetails[_username].owner;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/**
|
|
1384
|
+
* @notice Verifies identity exists and returns owner address
|
|
1385
|
+
* @dev Combines strict verification with owner lookup in one call
|
|
1386
|
+
* @param _username The username to verify and get owner for
|
|
1387
|
+
* @return answer Address of the username owner (reverts if username doesn't exist)
|
|
1388
|
+
*/
|
|
1389
|
+
function verifyStrictAndGetOwnerOfIdentity(
|
|
1390
|
+
string memory _username
|
|
1391
|
+
) public view returns (address answer) {
|
|
1392
|
+
if (strictVerifyIfIdentityExist(_username)) {
|
|
1393
|
+
answer = identityDetails[_username].owner;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* @notice Calculates the cost to renew a username registration
|
|
1399
|
+
* @dev Pricing varies based on timing and market demand:
|
|
1400
|
+
* - Free if renewed before expiration (within grace period)
|
|
1401
|
+
* - Variable cost based on highest active offer (minimum 500 Principal Token)
|
|
1402
|
+
* - Fixed 500,000 Principal Token if renewed more than 1 year before expiration
|
|
1403
|
+
* @param _identity The username to calculate renewal price for
|
|
1404
|
+
* @return price The cost in Principal Tokens to renew the username
|
|
1405
|
+
*/
|
|
1406
|
+
function seePriceToRenew(
|
|
1407
|
+
string memory _identity
|
|
1408
|
+
) public view returns (uint256 price) {
|
|
1409
|
+
if (identityDetails[_identity].expireDate >= block.timestamp) {
|
|
1410
|
+
if (usernameOffers[_identity][0].expireDate != 0) {
|
|
1411
|
+
for (
|
|
1412
|
+
uint256 i = 0;
|
|
1413
|
+
i < identityDetails[_identity].offerMaxSlots;
|
|
1414
|
+
i++
|
|
1415
|
+
) {
|
|
1416
|
+
if (
|
|
1417
|
+
usernameOffers[_identity][i].expireDate >
|
|
1418
|
+
block.timestamp &&
|
|
1419
|
+
usernameOffers[_identity][i].offerer != address(0)
|
|
1420
|
+
) {
|
|
1421
|
+
if (usernameOffers[_identity][i].amount > price) {
|
|
1422
|
+
price = usernameOffers[_identity][i].amount;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (price == 0) {
|
|
1428
|
+
price = 500 * 10 ** 18;
|
|
1429
|
+
} else {
|
|
1430
|
+
uint256 principalTokenReward = Evvm(evvmAddress.current)
|
|
1431
|
+
.getRewardAmount();
|
|
1432
|
+
price = ((price * 5) / 1000) > (500000 * principalTokenReward)
|
|
1433
|
+
? (500000 * principalTokenReward)
|
|
1434
|
+
: ((price * 5) / 1000);
|
|
1435
|
+
}
|
|
1436
|
+
} else {
|
|
1437
|
+
price = 500_000 * Evvm(evvmAddress.current).getRewardAmount();
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* @notice Gets the current price to add custom metadata to a username
|
|
1443
|
+
* @dev Price is dynamic based on current EVVM reward amount
|
|
1444
|
+
* @return price Cost in Principal Tokens (10x current reward amount)
|
|
1445
|
+
*/
|
|
1446
|
+
function getPriceToAddCustomMetadata() public view returns (uint256 price) {
|
|
1447
|
+
price = 10 * Evvm(evvmAddress.current).getRewardAmount();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* @notice Gets the current price to remove a single custom metadata entry
|
|
1452
|
+
* @dev Price is dynamic based on current EVVM reward amount
|
|
1453
|
+
* @return price Cost in Principal Tokens (10x current reward amount)
|
|
1454
|
+
*/
|
|
1455
|
+
function getPriceToRemoveCustomMetadata()
|
|
1456
|
+
public
|
|
1457
|
+
view
|
|
1458
|
+
returns (uint256 price)
|
|
1459
|
+
{
|
|
1460
|
+
price = 10 * Evvm(evvmAddress.current).getRewardAmount();
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* @notice Gets the cost to remove all custom metadata entries from a username
|
|
1465
|
+
* @dev Cost scales with the number of metadata entries to remove
|
|
1466
|
+
* @param _identity The username to calculate flush cost for
|
|
1467
|
+
* @return price Total cost in Principal Tokens (10x reward amount per metadata entry)
|
|
1468
|
+
*/
|
|
1469
|
+
function getPriceToFlushCustomMetadata(
|
|
1470
|
+
string memory _identity
|
|
1471
|
+
) public view returns (uint256 price) {
|
|
1472
|
+
price =
|
|
1473
|
+
(10 * Evvm(evvmAddress.current).getRewardAmount()) *
|
|
1474
|
+
identityDetails[_identity].customMetadataMaxSlots;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* @notice Gets the cost to completely remove a username and all its data
|
|
1479
|
+
* @dev Includes cost for metadata removal plus base username deletion fee
|
|
1480
|
+
* @param _identity The username to calculate deletion cost for
|
|
1481
|
+
* @return price Total cost in Principal Tokens (metadata flush cost + 1x reward amount)
|
|
1482
|
+
*/
|
|
1483
|
+
function getPriceToFlushUsername(
|
|
1484
|
+
string memory _identity
|
|
1485
|
+
) public view returns (uint256 price) {
|
|
1486
|
+
price =
|
|
1487
|
+
((10 * Evvm(evvmAddress.current).getRewardAmount()) *
|
|
1488
|
+
identityDetails[_identity].customMetadataMaxSlots) +
|
|
1489
|
+
Evvm(evvmAddress.current).getRewardAmount();
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
//█ User Management Functions ████████████████████████████████████████████████████████████████████
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* @notice Checks if a nonce has been used by a specific user
|
|
1496
|
+
* @dev Prevents replay attacks by tracking used nonces per user
|
|
1497
|
+
* @param _user Address of the user to check
|
|
1498
|
+
* @param _nonce Nonce value to verify
|
|
1499
|
+
* @return True if the nonce has been used, false if still available
|
|
1500
|
+
*/
|
|
1501
|
+
function checkIfNameServiceNonceIsAvailable(
|
|
1502
|
+
address _user,
|
|
1503
|
+
uint256 _nonce
|
|
1504
|
+
) public view returns (bool) {
|
|
1505
|
+
return nameServiceNonce[_user][_nonce];
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
//█ Identity Availability Functions ██████████████████████████████████████████████████████████████
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* @notice Checks if a username is available for registration
|
|
1512
|
+
* @dev A username is available if it was never registered or has been expired for 60+ days
|
|
1513
|
+
* @param _username The username to check availability for
|
|
1514
|
+
* @return True if the username is available for registration
|
|
1515
|
+
*/
|
|
1516
|
+
function isUsernameAvailable(
|
|
1517
|
+
string memory _username
|
|
1518
|
+
) public view returns (bool) {
|
|
1519
|
+
if (identityDetails[_username].expireDate == 0) {
|
|
1520
|
+
return true;
|
|
1521
|
+
} else {
|
|
1522
|
+
return
|
|
1523
|
+
identityDetails[_username].expireDate + 60 days <
|
|
1524
|
+
block.timestamp;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* @notice Gets basic identity information (owner and expiration date)
|
|
1530
|
+
* @dev Returns essential metadata for quick identity verification
|
|
1531
|
+
* @param _username The username to get basic info for
|
|
1532
|
+
* @return Owner address and expiration timestamp
|
|
1533
|
+
*/
|
|
1534
|
+
function getIdentityBasicMetadata(
|
|
1535
|
+
string memory _username
|
|
1536
|
+
) public view returns (address, uint256) {
|
|
1537
|
+
return (
|
|
1538
|
+
identityDetails[_username].owner,
|
|
1539
|
+
identityDetails[_username].expireDate
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* @notice Gets the number of custom metadata entries for a username
|
|
1545
|
+
* @dev Returns the count of metadata slots currently used
|
|
1546
|
+
* @param _username The username to count metadata for
|
|
1547
|
+
* @return Number of custom metadata entries
|
|
1548
|
+
*/
|
|
1549
|
+
function getAmountOfCustomMetadata(
|
|
1550
|
+
string memory _username
|
|
1551
|
+
) public view returns (uint256) {
|
|
1552
|
+
return identityDetails[_username].customMetadataMaxSlots;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* @notice Retrieves all custom metadata entries for a username
|
|
1557
|
+
* @dev Returns an array containing all metadata strings in order
|
|
1558
|
+
* @param _username The username to get metadata for
|
|
1559
|
+
* @return Array of all custom metadata strings
|
|
1560
|
+
*/
|
|
1561
|
+
function getFullCustomMetadataOfIdentity(
|
|
1562
|
+
string memory _username
|
|
1563
|
+
) public view returns (string[] memory) {
|
|
1564
|
+
string[] memory _customMetadata = new string[](
|
|
1565
|
+
identityDetails[_username].customMetadataMaxSlots
|
|
1566
|
+
);
|
|
1567
|
+
for (
|
|
1568
|
+
uint256 i = 0;
|
|
1569
|
+
i < identityDetails[_username].customMetadataMaxSlots;
|
|
1570
|
+
i++
|
|
1571
|
+
) {
|
|
1572
|
+
_customMetadata[i] = identityCustomMetadata[_username][i];
|
|
1573
|
+
}
|
|
1574
|
+
return _customMetadata;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* @notice Gets a specific custom metadata entry by index
|
|
1579
|
+
* @dev Retrieves metadata at a specific slot position
|
|
1580
|
+
* @param _username The username to get metadata from
|
|
1581
|
+
* @param _key The index of the metadata entry to retrieve
|
|
1582
|
+
* @return The metadata string at the specified index
|
|
1583
|
+
*/
|
|
1584
|
+
function getSingleCustomMetadataOfIdentity(
|
|
1585
|
+
string memory _username,
|
|
1586
|
+
uint256 _key
|
|
1587
|
+
) public view returns (string memory) {
|
|
1588
|
+
return identityCustomMetadata[_username][_key];
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* @notice Gets the maximum number of metadata slots available for a username
|
|
1593
|
+
* @dev Returns the total capacity for custom metadata entries
|
|
1594
|
+
* @param _username The username to check metadata capacity for
|
|
1595
|
+
* @return Maximum number of metadata slots
|
|
1596
|
+
*/
|
|
1597
|
+
function getCustomMetadataMaxSlotsOfIdentity(
|
|
1598
|
+
string memory _username
|
|
1599
|
+
) public view returns (uint256) {
|
|
1600
|
+
return identityDetails[_username].customMetadataMaxSlots;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
//█ Username Marketplace Functions ███████████████████████████████████████████████████████████████
|
|
1604
|
+
|
|
1605
|
+
/**
|
|
1606
|
+
* @notice Gets all offers made for a specific username
|
|
1607
|
+
* @dev Returns both active and expired offers that haven't been withdrawn
|
|
1608
|
+
* @param _username The username to get offers for
|
|
1609
|
+
* @return offers Array of all offer metadata structures
|
|
1610
|
+
*/
|
|
1611
|
+
function getOffersOfUsername(
|
|
1612
|
+
string memory _username
|
|
1613
|
+
) public view returns (OfferMetadata[] memory offers) {
|
|
1614
|
+
offers = new OfferMetadata[](identityDetails[_username].offerMaxSlots);
|
|
1615
|
+
|
|
1616
|
+
for (uint256 i = 0; i < identityDetails[_username].offerMaxSlots; i++) {
|
|
1617
|
+
offers[i] = usernameOffers[_username][i];
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* @notice Gets a specific offer for a username by offer ID
|
|
1623
|
+
* @dev Retrieves detailed information about a particular offer
|
|
1624
|
+
* @param _username The username to get the offer from
|
|
1625
|
+
* @param _offerID The ID/index of the specific offer
|
|
1626
|
+
* @return offer The complete offer metadata structure
|
|
1627
|
+
*/
|
|
1628
|
+
function getSingleOfferOfUsername(
|
|
1629
|
+
string memory _username,
|
|
1630
|
+
uint256 _offerID
|
|
1631
|
+
) public view returns (OfferMetadata memory offer) {
|
|
1632
|
+
return usernameOffers[_username][_offerID];
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* @notice Counts the total number of offers made for a username
|
|
1637
|
+
* @dev Iterates through offers to find the actual count of non-empty slots
|
|
1638
|
+
* @param _username The username to count offers for
|
|
1639
|
+
* @return length Total number of offers that have been made
|
|
1640
|
+
*/
|
|
1641
|
+
function getLengthOfOffersUsername(
|
|
1642
|
+
string memory _username
|
|
1643
|
+
) public view returns (uint256 length) {
|
|
1644
|
+
do {
|
|
1645
|
+
length++;
|
|
1646
|
+
} while (usernameOffers[_username][length].expireDate != 0);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
* @notice Gets the expiration date of a username registration
|
|
1651
|
+
* @dev Returns the timestamp when the username registration expires
|
|
1652
|
+
* @param _identity The username to check expiration for
|
|
1653
|
+
* @return The expiration timestamp in seconds since Unix epoch
|
|
1654
|
+
*/
|
|
1655
|
+
function getExpireDateOfIdentity(
|
|
1656
|
+
string memory _identity
|
|
1657
|
+
) public view returns (uint256) {
|
|
1658
|
+
return identityDetails[_identity].expireDate;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
/**
|
|
1662
|
+
* @notice Gets the current price for registering a new username
|
|
1663
|
+
* @dev Price is dynamic and based on current EVVM reward amount (100x reward)
|
|
1664
|
+
* @return The current registration price in Principal Tokens
|
|
1665
|
+
*/
|
|
1666
|
+
function getPricePerRegistration() public view returns (uint256) {
|
|
1667
|
+
return Evvm(evvmAddress.current).getRewardAmount() * 100;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
//█ Administrative Getters ███████████████████████████████████████████████████████████████████████
|
|
1671
|
+
|
|
1672
|
+
/**
|
|
1673
|
+
* @notice Gets the current admin address
|
|
1674
|
+
* @dev Returns the address with administrative privileges
|
|
1675
|
+
* @return The current admin address
|
|
1676
|
+
*/
|
|
1677
|
+
function getAdmin() public view returns (address) {
|
|
1678
|
+
return admin.current;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* @notice Gets complete admin information including pending proposals
|
|
1683
|
+
* @dev Returns current admin, proposed admin, and proposal acceptance deadline
|
|
1684
|
+
* @return currentAdmin Current administrative address
|
|
1685
|
+
* @return proposalAdmin Proposed new admin address (if any)
|
|
1686
|
+
* @return timeToAcceptAdmin Timestamp when proposal can be accepted
|
|
1687
|
+
*/
|
|
1688
|
+
function getAdminFullDetails()
|
|
1689
|
+
public
|
|
1690
|
+
view
|
|
1691
|
+
returns (
|
|
1692
|
+
address currentAdmin,
|
|
1693
|
+
address proposalAdmin,
|
|
1694
|
+
uint256 timeToAcceptAdmin
|
|
1695
|
+
)
|
|
1696
|
+
{
|
|
1697
|
+
return (admin.current, admin.proposal, admin.timeToAccept);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* @notice Gets information about pending token withdrawal proposals
|
|
1702
|
+
* @dev Returns proposed withdrawal amount and acceptance deadline
|
|
1703
|
+
* @return proposalAmountToWithdrawTokens Proposed withdrawal amount in Principal Tokens
|
|
1704
|
+
* @return timeToAcceptAmountToWithdrawTokens Timestamp when proposal can be executed
|
|
1705
|
+
*/
|
|
1706
|
+
function getProposedWithdrawAmountFullDetails()
|
|
1707
|
+
public
|
|
1708
|
+
view
|
|
1709
|
+
returns (
|
|
1710
|
+
uint256 proposalAmountToWithdrawTokens,
|
|
1711
|
+
uint256 timeToAcceptAmountToWithdrawTokens
|
|
1712
|
+
)
|
|
1713
|
+
{
|
|
1714
|
+
return (
|
|
1715
|
+
amountToWithdrawTokens.proposal,
|
|
1716
|
+
amountToWithdrawTokens.timeToAccept
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* @notice Gets the current EVVM contract address
|
|
1722
|
+
* @dev Returns the address of the EVVM contract used for payment processing
|
|
1723
|
+
* @return The current EVVM contract address
|
|
1724
|
+
*/
|
|
1725
|
+
function getEvvmAddress() public view returns (address) {
|
|
1726
|
+
return evvmAddress.current;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
/**
|
|
1730
|
+
* @notice Gets complete EVVM address information including pending proposals
|
|
1731
|
+
* @dev Returns current EVVM address, proposed address, and proposal acceptance deadline
|
|
1732
|
+
* @return currentEvvmAddress Current EVVM contract address
|
|
1733
|
+
* @return proposalEvvmAddress Proposed new EVVM address (if any)
|
|
1734
|
+
* @return timeToAcceptEvvmAddress Timestamp when proposal can be accepted
|
|
1735
|
+
*/
|
|
1736
|
+
function getEvvmAddressFullDetails()
|
|
1737
|
+
public
|
|
1738
|
+
view
|
|
1739
|
+
returns (
|
|
1740
|
+
address currentEvvmAddress,
|
|
1741
|
+
address proposalEvvmAddress,
|
|
1742
|
+
uint256 timeToAcceptEvvmAddress
|
|
1743
|
+
)
|
|
1744
|
+
{
|
|
1745
|
+
return (
|
|
1746
|
+
evvmAddress.current,
|
|
1747
|
+
evvmAddress.proposal,
|
|
1748
|
+
evvmAddress.timeToAccept
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
}
|