@evvm/testnet-contracts 1.0.6 → 2.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.
@@ -20,7 +20,7 @@ pragma solidity ^0.8.0;
20
20
  ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
21
21
 
22
22
  * @title EVVM (Ethereum Virtual Machine Virtualization) Core Contract
23
- * @author jistro.eth ariutokintumi.eth
23
+ * @author Mate labs
24
24
  * @notice Core payment processing and token management system for the EVVM ecosystem
25
25
  * @dev This contract serves as the central hub for:
26
26
  * - Multi-token payment processing with signature verification
@@ -61,7 +61,7 @@ pragma solidity ^0.8.0;
61
61
  ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
62
62
 
63
63
  * @title EVVM (Ethereum Virtual Machine Virtualization) Core Contract
64
- * @author jistro.eth ariutokintumi.eth
64
+ * @author Mate labs
65
65
  * @notice Core payment processing and token management system for the EVVM ecosystem
66
66
  * @dev This contract serves as the central hub for:
67
67
  * - Multi-token payment processing with signature verification
@@ -7,7 +7,7 @@ import {EvvmStructs} from "./EvvmStructs.sol";
7
7
 
8
8
  /**
9
9
  * @title EvvmStorage
10
- * @author jistro.eth
10
+ * @author Mate labs
11
11
  * @dev Storage layout contract for EVVM proxy pattern implementation.
12
12
  * This contract inherits all structures from EvvmStructs and
13
13
  * defines the storage layout that will be used by the proxy pattern.
@@ -27,7 +27,7 @@ pragma solidity ^0.8.0;
27
27
  ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
28
28
  *
29
29
  * @title EVVM Name Service Contract
30
- * @author jistro.eth ariutokintumi.eth
30
+ * @author Mate labs
31
31
  * @notice This contract manages username registration and domain name services for the EVVM ecosystem
32
32
  * @dev Provides a comprehensive domain name system with features including:
33
33
  *
@@ -12,7 +12,7 @@ MM .M `88888P' dP dP dP dP dP `88888P8 dP `88888P' dP
12
12
  MMMMMMMMMMMM
13
13
 
14
14
  * @title Staking Mate contract for Roll A Mate Protocol
15
- * @author jistro.eth ariutokintumi.eth
15
+ * @author Mate labs
16
16
  */
17
17
 
18
18
  import {Staking} from "@evvm/testnet-contracts/contracts/staking/Staking.sol";
@@ -24,7 +24,7 @@ pragma solidity ^0.8.0;
24
24
  ██║ ███████╗███████║ ██║ ██║ ╚████║███████╗ ██║
25
25
  ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
26
26
  * @title Staking Mate contract
27
- * @author jistro.eth ariutokintumi.eth
27
+ * @author Mate labs
28
28
  * @notice This contract manages the staking mechanism for the EVVM ecosystem
29
29
  * @dev Handles presale staking, public staking, and service staking with time locks and signature verification
30
30
  *
@@ -109,6 +109,32 @@ contract Staking {
109
109
  uint256 timeToAccept;
110
110
  }
111
111
 
112
+ /**
113
+ * @dev Struct to store service staking metadata during the staking process
114
+ * @param service Address of the service or contract account
115
+ * @param timestamp Timestamp when the prepareServiceStaking was called
116
+ * @param amountOfStaking Amount of staking tokens to be staked
117
+ * @param amountServiceBeforeStaking Service's Principal Token balance before staking
118
+ * @param amountStakingBeforeStaking Staking contract's Principal Token balance before staking
119
+ */
120
+ struct ServiceStakingMetadata {
121
+ address service;
122
+ uint256 timestamp;
123
+ uint256 amountOfStaking;
124
+ uint256 amountServiceBeforeStaking;
125
+ uint256 amountStakingBeforeStaking;
126
+ }
127
+
128
+ /**
129
+ * @dev Struct to encapsulate account metadata for staking operations
130
+ * @param Address Address of the account
131
+ * @param IsAService Boolean indicating if the account is a smart contract (service) account
132
+ */
133
+ struct AccountMetadata {
134
+ address Address;
135
+ bool IsAService;
136
+ }
137
+
112
138
  /// @dev Address of the EVVM core contract
113
139
  address private EVVM_ADDRESS;
114
140
 
@@ -119,6 +145,10 @@ contract Staking {
119
145
  /// @dev Price of one staking main token (5083 main token = 1 staking)
120
146
  uint256 private constant PRICE_OF_STAKING = 5083 * (10 ** 18);
121
147
 
148
+ /// @dev Address representing the principal Principal Token
149
+ address private constant PRINCIPAL_TOKEN_ADDRESS =
150
+ 0x0000000000000000000000000000000000000001;
151
+
122
152
  /// @dev Admin address management with proposal system
123
153
  AddressTypeProposal private admin;
124
154
  /// @dev Golden Fisher address management with proposal system
@@ -133,10 +163,8 @@ contract Staking {
133
163
  BoolTypeProposal private allowPresaleStaking;
134
164
  /// @dev Flag to enable/disable public staking
135
165
  BoolTypeProposal private allowPublicStaking;
136
-
137
- /// @dev Address representing the principal Principal Token
138
- address private constant PRINCIPAL_TOKEN_ADDRESS =
139
- 0x0000000000000000000000000000000000000001;
166
+ /// @dev Variable to store service staking metadata
167
+ ServiceStakingMetadata private serviceStakingData;
140
168
 
141
169
  /// @dev One-time setup breaker for estimator and EVVM addresses
142
170
  bytes1 private breakerSetupEstimatorAndEvvm;
@@ -150,13 +178,28 @@ contract Staking {
150
178
  /// @dev Mapping to store complete staking history for each user
151
179
  mapping(address => HistoryMetadata[]) private userHistory;
152
180
 
153
- /// @dev Modifier to restrict access to admin functions
181
+ /// @dev Modifier to verify access to admin functions
154
182
  modifier onlyOwner() {
155
183
  if (msg.sender != admin.actual) revert ErrorsLib.SenderIsNotAdmin();
156
184
 
157
185
  _;
158
186
  }
159
187
 
188
+ /// @dev Modifier to verify access to a contract or service account
189
+ modifier onlyCA() {
190
+ uint256 size;
191
+ address callerAddress = msg.sender;
192
+
193
+ assembly {
194
+ /// @dev check the size of the opcode of the address
195
+ size := extcodesize(callerAddress)
196
+ }
197
+
198
+ if (size == 0) revert ErrorsLib.AddressIsNotAService();
199
+
200
+ _;
201
+ }
202
+
160
203
  /**
161
204
  * @notice Contract constructor
162
205
  * @dev Initializes the staking contract with admin and golden fisher addresses
@@ -210,10 +253,10 @@ contract Staking {
210
253
  if (msg.sender != goldenFisher.actual)
211
254
  revert ErrorsLib.SenderIsNotGoldenFisher();
212
255
 
213
- stakingUserProcess(
214
- goldenFisher.actual,
215
- amountOfStaking,
256
+ stakingBaseProcess(
257
+ AccountMetadata({Address: goldenFisher.actual, IsAService: false}),
216
258
  isStaking,
259
+ amountOfStaking,
217
260
  0,
218
261
  Evvm(EVVM_ADDRESS).getNextCurrentSyncNonce(msg.sender),
219
262
  false,
@@ -263,10 +306,10 @@ contract Staking {
263
306
  if (!allowPresaleStaking.flag)
264
307
  revert ErrorsLib.PresaleStakingDisabled();
265
308
 
266
- stakingUserProcess(
267
- user,
268
- 1,
309
+ stakingBaseProcess(
310
+ AccountMetadata({Address: user, IsAService: false}),
269
311
  isStaking,
312
+ 1,
270
313
  priorityFee_EVVM,
271
314
  nonce_EVVM,
272
315
  priorityFlag_EVVM,
@@ -351,10 +394,10 @@ contract Staking {
351
394
  if (checkIfStakeNonceUsed(user, nonce))
352
395
  revert ErrorsLib.StakingNonceAlreadyUsed();
353
396
 
354
- stakingUserProcess(
355
- user,
356
- amountOfStaking,
397
+ stakingBaseProcess(
398
+ AccountMetadata({Address: user, IsAService: false}),
357
399
  isStaking,
400
+ amountOfStaking,
358
401
  priorityFee_EVVM,
359
402
  nonce_EVVM,
360
403
  priorityFlag_EVVM,
@@ -365,146 +408,120 @@ contract Staking {
365
408
  }
366
409
 
367
410
  /**
368
- * @notice Allows smart contracts (services) to stake on behalf of users
369
- * @dev Verifies that the service address has contract code and handles service-specific logic
370
- * @param user Address of the user who owns the stake
371
- * @param service Address of the smart contract performing the staking
372
- * @param isStaking True for staking, false for unstaking
373
- * @param amountOfStaking Amount of staking tokens to stake/unstake
374
- * @param nonce Unique nonce for this staking operation
375
- * @param signature Signature proving authorization for service staking
376
- * @param priorityFee_EVVM Priority fee for the EVVM transaction (only for staking)
377
- * @param nonce_EVVM Nonce for the EVVM contract transaction (only for staking)
378
- * @param priorityFlag_EVVM Priority flag for EVVM transaction (only for staking)
379
- * @param signature_EVVM Signature for the EVVM contract transaction (only for staking)
411
+ * @notice Prepares a service/contract account for staking by recording pre-staking state
412
+ * @dev First step in the service staking process. Must be followed by payment via caPay and confirmServiceStaking in the same transaction
413
+ * @param amountOfStaking Amount of staking tokens the service intends to stake
414
+ *
415
+ * Service Staking Process:
416
+ * 1. Call prepareServiceStaking(amount) - Records balances and metadata
417
+ * 2. Use EVVM.caPay() to transfer the required Principal Tokens to this contract
418
+ * 3. Call confirmServiceStaking() - Validates payment and completes staking
419
+ *
420
+ * @dev All three steps MUST occur in the same transaction or the staking will fail
421
+ * @dev CRITICAL WARNING: If the process is not completed properly (especially if caPay is called
422
+ * but confirmServiceStaking is not), the Principal Tokens will remain locked in the staking
423
+ * contract with no way to recover them. The service will lose the tokens permanently.
424
+ * @dev Only callable by contract accounts (services), not EOAs
380
425
  */
381
- function publicServiceStaking(
382
- address user,
383
- address service,
384
- bool isStaking,
385
- uint256 amountOfStaking,
386
- uint256 nonce,
387
- bytes memory signature,
388
- uint256 priorityFee_EVVM,
389
- uint256 nonce_EVVM,
390
- bool priorityFlag_EVVM,
391
- bytes memory signature_EVVM
392
- ) external {
393
- if (!allowPublicStaking.flag) revert ErrorsLib.PublicStakingDisabled();
394
-
395
- uint256 size;
426
+ function prepareServiceStaking(uint256 amountOfStaking) external onlyCA {
427
+ serviceStakingData = ServiceStakingMetadata({
428
+ service: msg.sender,
429
+ timestamp: block.timestamp,
430
+ amountOfStaking: amountOfStaking,
431
+ amountServiceBeforeStaking: Evvm(EVVM_ADDRESS).getBalance(
432
+ msg.sender,
433
+ PRINCIPAL_TOKEN_ADDRESS
434
+ ),
435
+ amountStakingBeforeStaking: Evvm(EVVM_ADDRESS).getBalance(
436
+ address(this),
437
+ PRINCIPAL_TOKEN_ADDRESS
438
+ )
439
+ });
440
+ }
396
441
 
397
- assembly {
398
- /// @dev check the size of the opcode of the address
399
- size := extcodesize(service)
400
- }
442
+ /**
443
+ * @notice Confirms and completes the service staking operation after payment verification
444
+ * @dev Final step in service staking. Validates that payment was made correctly and completes the staking process
445
+ *
446
+ * Validation checks:
447
+ * - Service balance decreased by the exact staking cost
448
+ * - Staking contract balance increased by the exact staking cost
449
+ * - Operation occurs in the same transaction as prepareServiceStaking
450
+ * - Caller matches the service that initiated the preparation
451
+ *
452
+ * @dev Only callable by the same contract that called prepareServiceStaking
453
+ * @dev Must be called in the same transaction as prepareServiceStaking
454
+ */
455
+ function confirmServiceStaking() external onlyCA {
456
+ uint256 totalStakingRequired = PRICE_OF_STAKING *
457
+ serviceStakingData.amountOfStaking;
401
458
 
402
- if (size == 0) revert ErrorsLib.AddressIsNotAService();
459
+ uint256 actualServiceBalance = Evvm(EVVM_ADDRESS).getBalance(
460
+ msg.sender,
461
+ PRINCIPAL_TOKEN_ADDRESS
462
+ );
403
463
 
404
- if (isStaking) {
405
- if (
406
- !SignatureUtils.verifyMessageSignedForPublicServiceStake(
407
- Evvm(EVVM_ADDRESS).getEvvmID(),
408
- user,
409
- service,
410
- isStaking,
411
- amountOfStaking,
412
- nonce,
413
- signature
414
- )
415
- ) revert ErrorsLib.InvalidSignatureOnStaking();
416
- } else {
417
- if (service != user) revert ErrorsLib.UserAndServiceMismatch();
418
- }
464
+ uint256 actualStakingBalance = Evvm(EVVM_ADDRESS).getBalance(
465
+ address(this),
466
+ PRINCIPAL_TOKEN_ADDRESS
467
+ );
419
468
 
420
- if (checkIfStakeNonceUsed(user, nonce))
421
- revert ErrorsLib.StakingNonceAlreadyUsed();
469
+ if (
470
+ serviceStakingData.amountServiceBeforeStaking -
471
+ totalStakingRequired !=
472
+ actualServiceBalance &&
473
+ serviceStakingData.amountStakingBeforeStaking +
474
+ totalStakingRequired !=
475
+ actualStakingBalance
476
+ )
477
+ revert ErrorsLib.ServiceDoesNotFulfillCorrectStakingAmount(
478
+ totalStakingRequired
479
+ );
422
480
 
423
- stakingServiceProcess(
424
- user,
425
- service,
426
- isStaking,
427
- amountOfStaking,
428
- isStaking ? priorityFee_EVVM : 0,
429
- isStaking ? nonce_EVVM : 0,
430
- isStaking ? priorityFlag_EVVM : false,
431
- isStaking ? signature_EVVM : bytes("")
432
- );
481
+ if (serviceStakingData.timestamp != block.timestamp)
482
+ revert ErrorsLib.ServiceDoesNotStakeInSameTx();
433
483
 
434
- stakingNonce[user][nonce] = true;
435
- }
484
+ if (serviceStakingData.service != msg.sender)
485
+ revert ErrorsLib.AddressMismatch();
436
486
 
437
- /**
438
- * @notice Internal function to process service staking operations
439
- * @dev Wrapper function that calls the base staking process for service operations
440
- * @param user Address of the user who owns the stake
441
- * @param service Address of the smart contract performing the staking
442
- * @param isStaking True for staking, false for unstaking
443
- * @param amountOfStaking Amount of staking tokens to stake/unstake
444
- * @param priorityFee_EVVM Priority fee for the EVVM transaction
445
- * @param nonce_EVVM Nonce for the EVVM contract transaction
446
- * @param priorityFlag_EVVM Priority flag for EVVM transaction
447
- * @param signature_EVVM Signature for the EVVM contract transaction
448
- */
449
- function stakingServiceProcess(
450
- address user,
451
- address service,
452
- bool isStaking,
453
- uint256 amountOfStaking,
454
- uint256 priorityFee_EVVM,
455
- uint256 nonce_EVVM,
456
- bool priorityFlag_EVVM,
457
- bytes memory signature_EVVM
458
- ) internal {
459
487
  stakingBaseProcess(
460
- user,
461
- service,
462
- isStaking,
463
- amountOfStaking,
464
- priorityFee_EVVM,
465
- nonce_EVVM,
466
- priorityFlag_EVVM,
467
- signature_EVVM
488
+ AccountMetadata({Address: msg.sender, IsAService: true}),
489
+ true,
490
+ serviceStakingData.amountOfStaking,
491
+ 0,
492
+ 0,
493
+ false,
494
+ ""
468
495
  );
469
496
  }
470
497
 
471
498
  /**
472
- * @notice Internal function to process user staking operations
473
- * @dev Wrapper function that calls the base staking process for user operations
474
- * @param user Address of the user performing the staking operation
475
- * @param amountOfStaking Amount of staking tokens to stake/unstake
476
- * @param isStaking True for staking, false for unstaking
477
- * @param priorityFee_EVVM Priority fee for the EVVM transaction
478
- * @param nonce_EVVM Nonce for the EVVM contract transaction
479
- * @param priorityFlag_EVVM Priority flag for EVVM transaction
480
- * @param signature_EVVM Signature for the EVVM contract transaction
499
+ * @notice Allows a service/contract account to unstake their staking tokens
500
+ * @dev Simplified unstaking process for services - no signature or payment required, just direct unstaking
501
+ * @param amountOfStaking Amount of staking tokens to unstake
502
+ *
503
+ * @dev The service will receive Principal Tokens equal to: amountOfStaking * PRICE_OF_STAKING
504
+ * @dev Subject to the same time locks as regular unstaking (21 days for full unstake)
505
+ * @dev Only callable by contract accounts (services), not EOAs
481
506
  */
482
- function stakingUserProcess(
483
- address user,
484
- uint256 amountOfStaking,
485
- bool isStaking,
486
- uint256 priorityFee_EVVM,
487
- uint256 nonce_EVVM,
488
- bool priorityFlag_EVVM,
489
- bytes memory signature_EVVM
490
- ) internal {
507
+ function serviceUnstaking(uint256 amountOfStaking) external onlyCA {
491
508
  stakingBaseProcess(
492
- user,
493
- user,
494
- isStaking,
509
+ AccountMetadata({Address: msg.sender, IsAService: true}),
510
+ false,
495
511
  amountOfStaking,
496
- priorityFee_EVVM,
497
- nonce_EVVM,
498
- priorityFlag_EVVM,
499
- signature_EVVM
512
+ 0,
513
+ 0,
514
+ false,
515
+ ""
500
516
  );
501
517
  }
502
518
 
503
519
  /**
504
520
  * @notice Core staking logic that handles both service and user staking operations
505
521
  * @dev Processes payments, updates history, handles time locks, and manages EVVM integration
506
- * @param userAccount Address of the user paying for the transaction
507
- * @param stakingAccount Address that will receive the stake/unstake (can be same as userAccount)
522
+ * @param account Metadata of the account performing the staking operation
523
+ * - Address: Address of the account
524
+ * - IsAService: Boolean indicating if the account is a smart contract (service) account
508
525
  * @param isStaking True for staking (requires payment), false for unstaking (provides refund)
509
526
  * @param amountOfStaking Amount of staking tokens to stake/unstake
510
527
  * @param priorityFee_EVVM Priority fee for EVVM transaction
@@ -513,8 +530,7 @@ contract Staking {
513
530
  * @param signature_EVVM Signature for EVVM contract transaction
514
531
  */
515
532
  function stakingBaseProcess(
516
- address userAccount,
517
- address stakingAccount,
533
+ AccountMetadata memory account,
518
534
  bool isStaking,
519
535
  uint256 amountOfStaking,
520
536
  uint256 priorityFee_EVVM,
@@ -526,61 +542,61 @@ contract Staking {
526
542
 
527
543
  if (isStaking) {
528
544
  if (
529
- getTimeToUserUnlockStakingTime(stakingAccount) > block.timestamp
530
- ) revert ErrorsLib.UserMustWaitToStakeAgain();
531
-
532
- makePay(
533
- userAccount,
534
- (PRICE_OF_STAKING * amountOfStaking),
535
- priorityFee_EVVM,
536
- priorityFlag_EVVM,
537
- nonce_EVVM,
538
- signature_EVVM
539
- );
545
+ getTimeToUserUnlockStakingTime(account.Address) >
546
+ block.timestamp
547
+ ) revert ErrorsLib.AddressMustWaitToStakeAgain();
540
548
 
541
- Evvm(EVVM_ADDRESS).pointStaker(stakingAccount, 0x01);
549
+ if (!account.IsAService)
550
+ makePay(
551
+ account.Address,
552
+ (PRICE_OF_STAKING * amountOfStaking),
553
+ priorityFee_EVVM,
554
+ priorityFlag_EVVM,
555
+ nonce_EVVM,
556
+ signature_EVVM
557
+ );
558
+
559
+ Evvm(EVVM_ADDRESS).pointStaker(account.Address, 0x01);
542
560
 
543
- auxSMsteBalance = userHistory[stakingAccount].length == 0
561
+ auxSMsteBalance = userHistory[account.Address].length == 0
544
562
  ? amountOfStaking
545
- : userHistory[stakingAccount][
546
- userHistory[stakingAccount].length - 1
563
+ : userHistory[account.Address][
564
+ userHistory[account.Address].length - 1
547
565
  ].totalStaked + amountOfStaking;
548
566
  } else {
549
- if (amountOfStaking == getUserAmountStaked(stakingAccount)) {
567
+ if (amountOfStaking == getUserAmountStaked(account.Address)) {
550
568
  if (
551
- getTimeToUserUnlockFullUnstakingTime(stakingAccount) >
569
+ getTimeToUserUnlockFullUnstakingTime(account.Address) >
552
570
  block.timestamp
553
- ) revert ErrorsLib.UserMustWaitToFullUnstake();
571
+ ) revert ErrorsLib.AddressMustWaitToFullUnstake();
554
572
 
555
- Evvm(EVVM_ADDRESS).pointStaker(stakingAccount, 0x00);
573
+ Evvm(EVVM_ADDRESS).pointStaker(account.Address, 0x00);
556
574
  }
557
575
 
558
- // Only for user unstaking, not service
559
- if (userAccount == stakingAccount && priorityFee_EVVM != 0) {
576
+ if (priorityFee_EVVM != 0 && !account.IsAService)
560
577
  makePay(
561
- userAccount,
578
+ account.Address,
562
579
  priorityFee_EVVM,
563
580
  0,
564
581
  priorityFlag_EVVM,
565
582
  nonce_EVVM,
566
583
  signature_EVVM
567
584
  );
568
- }
569
585
 
570
586
  auxSMsteBalance =
571
- userHistory[stakingAccount][
572
- userHistory[stakingAccount].length - 1
587
+ userHistory[account.Address][
588
+ userHistory[account.Address].length - 1
573
589
  ].totalStaked -
574
590
  amountOfStaking;
575
591
 
576
592
  makeCaPay(
577
593
  PRINCIPAL_TOKEN_ADDRESS,
578
- stakingAccount,
594
+ account.Address,
579
595
  (PRICE_OF_STAKING * amountOfStaking)
580
596
  );
581
597
  }
582
598
 
583
- userHistory[stakingAccount].push(
599
+ userHistory[account.Address].push(
584
600
  HistoryMetadata({
585
601
  transactionType: isStaking
586
602
  ? bytes32(uint256(1))
@@ -591,7 +607,10 @@ contract Staking {
591
607
  })
592
608
  );
593
609
 
594
- if (Evvm(EVVM_ADDRESS).isAddressStaker(msg.sender)) {
610
+ if (
611
+ Evvm(EVVM_ADDRESS).isAddressStaker(msg.sender) &&
612
+ !account.IsAService
613
+ ) {
595
614
  makeCaPay(
596
615
  PRINCIPAL_TOKEN_ADDRESS,
597
616
  msg.sender,
@@ -769,16 +788,29 @@ contract Staking {
769
788
  admin.timeToAccept = 0;
770
789
  }
771
790
 
791
+ /**
792
+ * @notice Proposes a new golden fisher address with 1-day time delay
793
+ * @dev Part of the time-delayed governance system for golden fisher changes
794
+ * @param _goldenFisher Address of the proposed new golden fisher
795
+ */
772
796
  function proposeGoldenFisher(address _goldenFisher) external onlyOwner {
773
797
  goldenFisher.proposal = _goldenFisher;
774
798
  goldenFisher.timeToAccept = block.timestamp + 1 days;
775
799
  }
776
800
 
801
+ /**
802
+ * @notice Rejects the current golden fisher proposal
803
+ * @dev Only current admin can reject the pending golden fisher proposal
804
+ */
777
805
  function rejectProposalGoldenFisher() external onlyOwner {
778
806
  goldenFisher.proposal = address(0);
779
807
  goldenFisher.timeToAccept = 0;
780
808
  }
781
809
 
810
+ /**
811
+ * @notice Accepts the golden fisher proposal after the time delay has passed
812
+ * @dev Can only be called by the current admin after the 1-day time delay
813
+ */
782
814
  function acceptNewGoldenFisher() external onlyOwner {
783
815
  if (goldenFisher.timeToAccept > block.timestamp) {
784
816
  revert();
@@ -788,6 +820,11 @@ contract Staking {
788
820
  goldenFisher.timeToAccept = 0;
789
821
  }
790
822
 
823
+ /**
824
+ * @notice Proposes a new time delay for staking after unstaking with 1-day time delay
825
+ * @dev Part of the time-delayed governance system for staking unlock time changes
826
+ * @param _secondsToUnlockStaking New number of seconds users must wait after unstaking before staking again
827
+ */
791
828
  function proposeSetSecondsToUnlockStaking(
792
829
  uint256 _secondsToUnlockStaking
793
830
  ) external onlyOwner {
@@ -795,11 +832,19 @@ contract Staking {
795
832
  secondsToUnlockStaking.timeToAccept = block.timestamp + 1 days;
796
833
  }
797
834
 
835
+ /**
836
+ * @notice Rejects the current staking unlock time proposal
837
+ * @dev Only current admin can reject the pending staking unlock time proposal
838
+ */
798
839
  function rejectProposalSetSecondsToUnlockStaking() external onlyOwner {
799
840
  secondsToUnlockStaking.proposal = 0;
800
841
  secondsToUnlockStaking.timeToAccept = 0;
801
842
  }
802
843
 
844
+ /**
845
+ * @notice Accepts the staking unlock time proposal after the time delay has passed
846
+ * @dev Can only be called by the current admin after the 1-day time delay
847
+ */
803
848
  function acceptSetSecondsToUnlockStaking() external onlyOwner {
804
849
  if (secondsToUnlockStaking.timeToAccept > block.timestamp) {
805
850
  revert();
@@ -809,6 +854,11 @@ contract Staking {
809
854
  secondsToUnlockStaking.timeToAccept = 0;
810
855
  }
811
856
 
857
+ /**
858
+ * @notice Proposes a new time delay for full unstaking operations with 1-day time delay
859
+ * @dev Part of the time-delayed governance system for full unstaking time changes
860
+ * @param _secondsToUnllockFullUnstaking New number of seconds users must wait for full unstaking (default: 21 days)
861
+ */
812
862
  function prepareSetSecondsToUnllockFullUnstaking(
813
863
  uint256 _secondsToUnllockFullUnstaking
814
864
  ) external onlyOwner {
@@ -816,11 +866,19 @@ contract Staking {
816
866
  secondsToUnllockFullUnstaking.timeToAccept = block.timestamp + 1 days;
817
867
  }
818
868
 
869
+ /**
870
+ * @notice Cancels the current full unstaking time proposal
871
+ * @dev Only current admin can cancel the pending full unstaking time proposal
872
+ */
819
873
  function cancelSetSecondsToUnllockFullUnstaking() external onlyOwner {
820
874
  secondsToUnllockFullUnstaking.proposal = 0;
821
875
  secondsToUnllockFullUnstaking.timeToAccept = 0;
822
876
  }
823
877
 
878
+ /**
879
+ * @notice Confirms the full unstaking time proposal after the time delay has passed
880
+ * @dev Can only be called by the current admin after the 1-day time delay
881
+ */
824
882
  function confirmSetSecondsToUnllockFullUnstaking() external onlyOwner {
825
883
  if (secondsToUnllockFullUnstaking.timeToAccept > block.timestamp) {
826
884
  revert();
@@ -831,14 +889,26 @@ contract Staking {
831
889
  secondsToUnllockFullUnstaking.timeToAccept = 0;
832
890
  }
833
891
 
892
+ /**
893
+ * @notice Prepares to toggle the public staking flag with 1-day time delay
894
+ * @dev Initiates the time-delayed process to enable/disable public staking
895
+ */
834
896
  function prepareChangeAllowPublicStaking() external onlyOwner {
835
897
  allowPublicStaking.timeToAccept = block.timestamp + 1 days;
836
898
  }
837
899
 
900
+ /**
901
+ * @notice Cancels the pending public staking flag change
902
+ * @dev Only current admin can cancel the pending public staking toggle
903
+ */
838
904
  function cancelChangeAllowPublicStaking() external onlyOwner {
839
905
  allowPublicStaking.timeToAccept = 0;
840
906
  }
841
907
 
908
+ /**
909
+ * @notice Confirms and executes the public staking flag toggle after the time delay has passed
910
+ * @dev Toggles between enabled/disabled state for public staking after 1-day delay
911
+ */
842
912
  function confirmChangeAllowPublicStaking() external onlyOwner {
843
913
  if (allowPublicStaking.timeToAccept > block.timestamp) {
844
914
  revert();
@@ -849,14 +919,26 @@ contract Staking {
849
919
  });
850
920
  }
851
921
 
922
+ /**
923
+ * @notice Prepares to toggle the presale staking flag with 1-day time delay
924
+ * @dev Initiates the time-delayed process to enable/disable presale staking
925
+ */
852
926
  function prepareChangeAllowPresaleStaking() external onlyOwner {
853
927
  allowPresaleStaking.timeToAccept = block.timestamp + 1 days;
854
928
  }
855
929
 
930
+ /**
931
+ * @notice Cancels the pending presale staking flag change
932
+ * @dev Only current admin can cancel the pending presale staking toggle
933
+ */
856
934
  function cancelChangeAllowPresaleStaking() external onlyOwner {
857
935
  allowPresaleStaking.timeToAccept = 0;
858
936
  }
859
937
 
938
+ /**
939
+ * @notice Confirms and executes the presale staking flag toggle after the time delay has passed
940
+ * @dev Toggles between enabled/disabled state for presale staking after 1-day delay
941
+ */
860
942
  function confirmChangeAllowPresaleStaking() external onlyOwner {
861
943
  if (allowPresaleStaking.timeToAccept > block.timestamp) {
862
944
  revert();
@@ -867,16 +949,29 @@ contract Staking {
867
949
  });
868
950
  }
869
951
 
952
+ /**
953
+ * @notice Proposes a new estimator contract address with 1-day time delay
954
+ * @dev Part of the time-delayed governance system for estimator contract changes
955
+ * @param _estimator Address of the proposed new estimator contract
956
+ */
870
957
  function proposeEstimator(address _estimator) external onlyOwner {
871
958
  estimator.proposal = _estimator;
872
959
  estimator.timeToAccept = block.timestamp + 1 days;
873
960
  }
874
961
 
962
+ /**
963
+ * @notice Rejects the current estimator contract proposal
964
+ * @dev Only current admin can reject the pending estimator contract proposal
965
+ */
875
966
  function rejectProposalEstimator() external onlyOwner {
876
967
  estimator.proposal = address(0);
877
968
  estimator.timeToAccept = 0;
878
969
  }
879
970
 
971
+ /**
972
+ * @notice Accepts the estimator contract proposal after the time delay has passed
973
+ * @dev Can only be called by the current admin after the 1-day time delay
974
+ */
880
975
  function acceptNewEstimator() external onlyOwner {
881
976
  if (estimator.timeToAccept > block.timestamp) {
882
977
  revert();
@@ -14,6 +14,10 @@ library ErrorsLib {
14
14
  error PublicStakingDisabled();
15
15
  error AddressIsNotAService();
16
16
  error UserAndServiceMismatch();
17
- error UserMustWaitToStakeAgain();
18
- error UserMustWaitToFullUnstake();
17
+ error AddressMustWaitToStakeAgain();
18
+ error AddressMustWaitToFullUnstake();
19
+ error ServiceDoesNotFulfillCorrectStakingAmount(uint256 requiredAmount);
20
+ error ServiceDoesNotStakeInSameTx();
21
+ error AddressMismatch();
19
22
  }
23
+
@@ -22,7 +22,7 @@ pragma solidity ^0.8.0;
22
22
  ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
23
23
 
24
24
  * @title Treasury Contract
25
- * @author jistro.eth
25
+ * @author Mate labs
26
26
  * @notice Treasury for managing deposits and withdrawals in the EVVM ecosystem
27
27
  * @dev Secure vault for ETH and ERC20 tokens with EVVM integration and input validation
28
28
  */
@@ -4,7 +4,7 @@
4
4
  pragma solidity ^0.8.0;
5
5
  /**
6
6
  * @title Erc191TestBuilder
7
- * @author jistro.eth
7
+ * @author Mate labs
8
8
  * @notice this library is used to build ERC191 messages for foundry test scripts
9
9
  * more info in
10
10
  * https://book.getfoundry.sh/cheatcodes/create-wallet
@@ -3,9 +3,59 @@
3
3
 
4
4
  pragma solidity ^0.8.0;
5
5
 
6
+ /**
7
+ * @title SignatureRecover
8
+ * @author Mate labs
9
+ * @notice Library for ECDSA signature verification and signer recovery in the EVVM ecosystem
10
+ * @dev Provides utilities for verifying signatures against expected signers and recovering addresses from signatures.
11
+ * Uses ERC-191 standard message signing format with proper message hashing and signature validation.
12
+ *
13
+ * Key Features:
14
+ * - EVVM-specific signature verification with structured message format
15
+ * - ERC-191 compliant signature format (\x19Ethereum Signed Message)
16
+ * - Safe signature splitting and validation
17
+ * - Address recovery from message signatures
18
+ *
19
+ * Security Features:
20
+ * - Validates signature length (must be 65 bytes)
21
+ * - Ensures proper v value (27 or 28)
22
+ * - Uses keccak256 hashing with ERC-191 message prefix
23
+ * - Prevents signature malleability attacks
24
+ *
25
+ * Usage Pattern:
26
+ * ```solidity
27
+ * bool isValid = SignatureRecover.signatureVerification(
28
+ * evvmID,
29
+ * "functionName",
30
+ * string.concat(param1, ",", param2, ",", param3),
31
+ * signature,
32
+ * expectedAddress
33
+ * );
34
+ * ```
35
+ */
36
+
6
37
  import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
7
38
 
8
39
  library SignatureRecover {
40
+ /**
41
+ * @notice Verifies that a signature matches the expected signer for EVVM operations
42
+ * @dev Constructs a standardized message format and verifies the signature against the expected signer
43
+ *
44
+ * Message Format: "{evvmID},{functionName},{inputs}"
45
+ * This creates a unique signature for each EVVM operation preventing replay attacks across:
46
+ * - Different EVVM instances (via evvmID)
47
+ * - Different functions (via functionName)
48
+ * - Different parameters (via inputs)
49
+ *
50
+ * @param evvmID Unique identifier for the EVVM instance (prevents cross-chain replay)
51
+ * @param functionName Name of the function being called (prevents cross-function replay)
52
+ * @param inputs Comma-separated string of function inputs. Must be constructed using
53
+ * string.concat(param1, ",", param2, ",", param3)
54
+ * where all parameters are converted to strings
55
+ * @param signature The ECDSA signature to verify (65 bytes: r(32) + s(32) + v(1))
56
+ * @param expectedSigner Address that should have signed the message
57
+ * @return bool True if the signature is valid and matches the expected signer, false otherwise
58
+ */
9
59
  function signatureVerification(
10
60
  string memory evvmID,
11
61
  string memory functionName,
@@ -20,7 +70,22 @@ library SignatureRecover {
20
70
  ) == expectedSigner;
21
71
  }
22
72
 
23
-
73
+ /**
74
+ * @notice Recovers the signer address from a message and its signature
75
+ * @dev Uses ERC-191 standard message signing format with proper prefix and length encoding
76
+ *
77
+ * The message is hashed using the ERC-191 standard format:
78
+ * keccak256("\x19Ethereum Signed Message:\n" + messageLength + message)
79
+ *
80
+ * This ensures compatibility with standard Ethereum wallets and signing tools like:
81
+ * - MetaMask personal_sign
82
+ * - web3.eth.personal.sign
83
+ * - ethers.js signMessage
84
+ *
85
+ * @param message The original message that was signed (plain text)
86
+ * @param signature The ECDSA signature (65 bytes: r + s + v)
87
+ * @return address The recovered signer address, or zero address if signature is invalid
88
+ */
24
89
  function recoverSigner(
25
90
  string memory message,
26
91
  bytes memory signature
@@ -36,6 +101,25 @@ library SignatureRecover {
36
101
  return ecrecover(messageHash, v, r, s);
37
102
  }
38
103
 
104
+ /**
105
+ * @notice Splits a signature into its component parts (r, s, v)
106
+ * @dev Extracts r, s, and v values from a 65-byte signature and validates the format
107
+ *
108
+ * Signature Format (65 bytes total):
109
+ * - r: bytes 0-31 (32 bytes) - First part of ECDSA signature
110
+ * - s: bytes 32-63 (32 bytes) - Second part of ECDSA signature
111
+ * - v: byte 64 (1 byte) - Recovery identifier (27 or 28)
112
+ *
113
+ * Security validations:
114
+ * - Ensures signature is exactly 65 bytes
115
+ * - Normalizes v value to 27/28 if needed
116
+ * - Validates v is either 27 or 28 (standard Ethereum values)
117
+ *
118
+ * @param signature The complete signature bytes to split (must be 65 bytes)
119
+ * @return r The r component of the signature (bytes32)
120
+ * @return s The s component of the signature (bytes32)
121
+ * @return v The recovery identifier (uint8, either 27 or 28)
122
+ */
39
123
  function splitSignature(
40
124
  bytes memory signature
41
125
  ) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
@@ -0,0 +1,116 @@
1
+ // SPDX-License-Identifier: EVVM-NONCOMMERCIAL-1.0
2
+ // Full license terms available at: https://www.evvm.info/docs/EVVMNoncommercialLicense
3
+ pragma solidity ^0.8.0;
4
+
5
+ /**
6
+ * @title StakingServiceHooks
7
+ * @author Mate labs
8
+ * @notice Abstract contract providing simplified staking functionality for service contracts
9
+ * @dev This contract provides pre-built hooks for service contracts to easily interact with the EVVM staking system.
10
+ * It handles the complex 3-step staking process atomically to prevent token loss.
11
+ *
12
+ * Key Features:
13
+ * - Atomic service staking: Combines prepareServiceStaking, caPay, and confirmServiceStaking in one call
14
+ * - Simplified unstaking for services
15
+ * - Automatic address management for Staking and EVVM contracts
16
+ * - Safe state management to prevent token loss
17
+ *
18
+ * Usage:
19
+ * - Inherit from this contract in your service contract
20
+ * - Call makeStakeService(amount) to stake tokens safely
21
+ * - Call makeUnstakeService(amount) to unstake tokens
22
+ *
23
+ * IMPORTANT: The service contract must have sufficient Principal Token balance before calling makeStakeService
24
+ */
25
+
26
+ import {Staking} from "@evvm/testnet-contracts/contracts/staking/Staking.sol";
27
+ import {Evvm} from "@evvm/testnet-contracts/contracts/evvm/Evvm.sol";
28
+
29
+ abstract contract StakingServiceHooks {
30
+ /// @dev Address of the Staking contract that handles staking operations
31
+ address stakingHookAddress;
32
+ /// @dev Address of the EVVM core contract that handles token operations
33
+ address evvmHookAddress;
34
+
35
+ /**
36
+ * @notice Initializes the service hooks with the staking contract address
37
+ * @dev Automatically retrieves and stores the EVVM contract address from the staking contract
38
+ * @param _stakingAddress Address of the deployed Staking contract
39
+ */
40
+ constructor(address _stakingAddress) {
41
+ stakingHookAddress = _stakingAddress;
42
+ evvmHookAddress = Staking(stakingHookAddress).getEvvmAddress();
43
+ }
44
+
45
+ /**
46
+ * @notice Performs atomic staking operation for the service contract
47
+ * @dev Executes the complete 3-step staking process in a single transaction to prevent token loss:
48
+ * 1. Prepares service staking (records pre-staking state)
49
+ * 2. Transfers Principal Tokens to staking contract via caPay
50
+ * 3. Confirms staking completion
51
+ *
52
+ * @param amountToStake Number of staking tokens to stake (not Principal Tokens)
53
+ *
54
+ * Requirements:
55
+ * - Service must have sufficient Principal Token balance (amountToStake * PRICE_OF_STAKING)
56
+ * - Service must not be in cooldown period from previous unstaking
57
+ * - All operations must succeed in the same transaction
58
+ *
59
+ * @dev CRITICAL: This function ensures atomicity - if any step fails, the entire transaction reverts
60
+ * preventing the loss of Principal Tokens that could occur with manual step-by-step execution
61
+ */
62
+ function makeStakeService(uint256 amountToStake) public {
63
+ Staking(stakingHookAddress).prepareServiceStaking(amountToStake);
64
+ Evvm(evvmHookAddress).caPay(
65
+ address(stakingHookAddress),
66
+ 0x0000000000000000000000000000000000000001,
67
+ Staking(stakingHookAddress).priceOfStaking() * amountToStake
68
+ );
69
+ Staking(stakingHookAddress).confirmServiceStaking();
70
+ }
71
+
72
+ /**
73
+ * @notice Performs unstaking operation for the service contract
74
+ * @dev Allows the service to withdraw staked tokens and receive Principal Tokens back
75
+ *
76
+ * @param amountToUnstake Number of staking tokens to unstake
77
+ *
78
+ * The service will receive: amountToUnstake * PRICE_OF_STAKING Principal Tokens
79
+ *
80
+ * Requirements:
81
+ * - Service must have at least amountToUnstake staking tokens staked
82
+ * - If unstaking all tokens, must wait 21 days since last zero balance
83
+ * - Cannot unstake more than currently staked amount
84
+ *
85
+ * @dev Unstaking is subject to the same time locks as regular user unstaking
86
+ */
87
+ function makeUnstakeService(uint256 amountToUnstake) public {
88
+ Staking(stakingHookAddress).serviceUnstaking(amountToUnstake);
89
+ }
90
+
91
+ /**
92
+ * @notice Internal function to update the staking contract address
93
+ * @dev Updates both staking and EVVM addresses. Should be used when staking contract is upgraded
94
+ * @param newStakingAddress Address of the new Staking contract
95
+ *
96
+ * @dev This function should be called by inheriting contracts when they need to migrate
97
+ * to a new version of the staking contract. It automatically updates the EVVM address too.
98
+ */
99
+ function _changeStakingHookAddress(address newStakingAddress) internal {
100
+ stakingHookAddress = newStakingAddress;
101
+ evvmHookAddress = Staking(stakingHookAddress).getEvvmAddress();
102
+ }
103
+
104
+ /**
105
+ * @notice Internal function to manually update the EVVM contract address
106
+ * @dev Updates only the EVVM address. Use when EVVM contract is upgraded independently
107
+ * @param newEvvmAddress Address of the new EVVM contract
108
+ *
109
+ * @dev This function should be used sparingly, typically only when the EVVM contract
110
+ * is upgraded but the staking contract remains the same. In most cases, prefer
111
+ * using _changeStakingHookAddress which updates both addresses automatically.
112
+ */
113
+ function changeEvvmHookAddress(address newEvvmAddress) internal {
114
+ evvmHookAddress = newEvvmAddress;
115
+ }
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evvm/testnet-contracts",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "EVVM Testnet Contracts - Smart contracts and tools for scalable, modular, and cross-chain EVM virtualization",
5
5
  "files": [
6
6
  "contracts/**/*.sol",
@@ -22,7 +22,7 @@
22
22
  "defi"
23
23
  ],
24
24
  "author": "EVVM Organization",
25
- "license": "MIT",
25
+ "license": "EVVM-NONCOMMERCIAL-1.0",
26
26
  "repository": {
27
27
  "type": "git",
28
28
  "url": "git+https://github.com/EVVM-org/Testnet-Contracts.git"