@evvm/testnet-contracts 2.2.3 → 3.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.
Files changed (71) hide show
  1. package/LICENSE +145 -118
  2. package/README.md +162 -39
  3. package/contracts/core/Core.sol +1394 -0
  4. package/contracts/core/lib/CoreStorage.sol +171 -0
  5. package/contracts/nameService/NameService.sol +666 -586
  6. package/contracts/nameService/lib/IdentityValidation.sol +18 -3
  7. package/contracts/p2pSwap/P2PSwap.sol +439 -285
  8. package/contracts/staking/Estimator.sol +128 -40
  9. package/contracts/staking/Staking.sol +329 -322
  10. package/contracts/treasury/Treasury.sol +48 -37
  11. package/contracts/treasuryTwoChains/TreasuryExternalChainStation.sol +585 -198
  12. package/contracts/treasuryTwoChains/TreasuryHostChainStation.sol +425 -174
  13. package/contracts/treasuryTwoChains/lib/PayloadUtils.sol +2 -4
  14. package/interfaces/{IEvvm.sol → ICore.sol} +67 -29
  15. package/interfaces/IEstimator.sol +1 -1
  16. package/interfaces/INameService.sol +58 -52
  17. package/interfaces/IP2PSwap.sol +18 -17
  18. package/interfaces/IStaking.sol +22 -17
  19. package/interfaces/ITreasury.sol +2 -1
  20. package/interfaces/ITreasuryExternalChainStation.sol +15 -9
  21. package/interfaces/ITreasuryHostChainStation.sol +14 -11
  22. package/interfaces/IUserValidator.sol +6 -0
  23. package/library/Erc191TestBuilder.sol +350 -297
  24. package/library/EvvmService.sol +38 -27
  25. package/library/errors/CoreError.sol +116 -0
  26. package/library/errors/CrossChainTreasuryError.sol +36 -0
  27. package/library/errors/NameServiceError.sol +79 -0
  28. package/library/errors/StakingError.sol +79 -0
  29. package/library/errors/TreasuryError.sol +33 -0
  30. package/library/primitives/SignatureRecover.sol +33 -0
  31. package/library/structs/CoreStructs.sol +146 -0
  32. package/library/structs/ExternalChainStationStructs.sol +92 -0
  33. package/library/structs/HostChainStationStructs.sol +77 -0
  34. package/library/structs/NameServiceStructs.sol +47 -0
  35. package/library/structs/P2PSwapStructs.sol +127 -0
  36. package/library/structs/StakingStructs.sol +67 -0
  37. package/library/utils/AdvancedStrings.sol +84 -5
  38. package/library/utils/CAUtils.sol +29 -0
  39. package/library/utils/SignatureUtil.sol +34 -0
  40. package/library/utils/governance/Admin.sol +66 -0
  41. package/library/utils/governance/ProposalStructs.sol +49 -0
  42. package/library/utils/service/CoreExecution.sol +177 -0
  43. package/library/utils/service/StakingServiceUtils.sol +30 -3
  44. package/library/utils/signature/CoreHashUtils.sol +73 -0
  45. package/library/utils/signature/NameServiceHashUtils.sol +156 -0
  46. package/library/utils/signature/P2PSwapHashUtils.sol +65 -0
  47. package/library/utils/signature/StakingHashUtils.sol +41 -0
  48. package/library/utils/signature/TreasuryCrossChainHashUtils.sol +40 -0
  49. package/package.json +2 -1
  50. package/contracts/evvm/Evvm.sol +0 -1327
  51. package/contracts/evvm/lib/ErrorsLib.sol +0 -18
  52. package/contracts/evvm/lib/EvvmStorage.sol +0 -62
  53. package/contracts/evvm/lib/EvvmStructs.sol +0 -90
  54. package/contracts/evvm/lib/SignatureUtils.sol +0 -120
  55. package/contracts/nameService/lib/ErrorsLib.sol +0 -21
  56. package/contracts/nameService/lib/NameServiceStructs.sol +0 -69
  57. package/contracts/nameService/lib/SignatureUtils.sol +0 -245
  58. package/contracts/p2pSwap/lib/P2PSwapStructs.sol +0 -59
  59. package/contracts/p2pSwap/lib/SignatureUtils.sol +0 -98
  60. package/contracts/staking/lib/ErrorsLib.sol +0 -22
  61. package/contracts/staking/lib/SignatureUtils.sol +0 -39
  62. package/contracts/staking/lib/StakingStructs.sol +0 -94
  63. package/contracts/treasury/lib/ErrorsLib.sol +0 -11
  64. package/contracts/treasuryTwoChains/lib/ErrorsLib.sol +0 -48
  65. package/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol +0 -80
  66. package/contracts/treasuryTwoChains/lib/HostChainStationStructs.sol +0 -87
  67. package/contracts/treasuryTwoChains/lib/SignatureUtils.sol +0 -79
  68. package/library/utils/GovernanceUtils.sol +0 -81
  69. package/library/utils/nonces/AsyncNonce.sol +0 -32
  70. package/library/utils/nonces/SyncNonce.sol +0 -27
  71. package/library/utils/service/EvvmPayments.sol +0 -79
@@ -2,6 +2,22 @@
2
2
  // Full license terms available at: https://www.evvm.org/docs/EVVMNoncommercialLicense
3
3
 
4
4
  pragma solidity ^0.8.0;
5
+
6
+ import {
7
+ P2PSwapHashUtils as Hash
8
+ } from "@evvm/testnet-contracts/library/utils/signature/P2PSwapHashUtils.sol";
9
+ import {
10
+ P2PSwapStructs as Structs
11
+ } from "@evvm/testnet-contracts/library/structs/P2PSwapStructs.sol";
12
+
13
+ import {Staking} from "@evvm/testnet-contracts/contracts/staking/Staking.sol";
14
+ import {EvvmService} from "@evvm/testnet-contracts/library/EvvmService.sol";
15
+ import {CoreStructs} from "@evvm/testnet-contracts/interfaces/ICore.sol";
16
+
17
+ import {
18
+ AdvancedStrings
19
+ } from "@evvm/testnet-contracts/library/utils/AdvancedStrings.sol";
20
+
5
21
  /**
6
22
  /$$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$
7
23
  | $$__ $$/$$__ $| $$__ $$/$$__ $$
@@ -15,38 +31,14 @@ pragma solidity ^0.8.0;
15
31
  | $$
16
32
  |__/
17
33
 
18
- * @title P2P Swap Service
34
+ * @title EVVM P2P Swap
19
35
  * @author Mate labs
20
- * @notice Peer-to-peer decentralized exchange for token trading within the EVVM ecosystem
21
- * @dev Implements order book-style trading with dynamic market creation, fee distribution,
22
- * and integration with EVVM's staking and payment systems. Supports both proportional
23
- * and fixed fee models with time-delayed governance for parameter updates.
24
- *
25
- * Key Features:
26
- * - Dynamic market creation for any token pair
27
- * - Order management (create, cancel, execute)
28
- * - Configurable fee structure with multi-party distribution
29
- * - Service staking capabilities via StakingServiceHooks inheritance
30
- * - ERC-191 signature verification for all operations
31
- * - Time-delayed administrative governance
32
- *
33
- * Fee Distribution:
34
- * - Seller: 50% (configurable)
35
- * - Service: 40% (configurable)
36
- * - Staker Rewards: 10% (configurable)
36
+ * @notice Peer-to-peer decentralized exchange for token trading within EVVM.
37
+ * @dev Supports order book-style trading with customizable fee models.
38
+ * Integrates with Core.sol for asset locking and settlements, and Staking.sol for validator rewards.
37
39
  */
38
40
 
39
- import {Staking} from "@evvm/testnet-contracts/contracts/staking/Staking.sol";
40
- import {SignatureUtils} from "@evvm/testnet-contracts/contracts/p2pSwap/lib/SignatureUtils.sol";
41
- import {AdvancedStrings} from "@evvm/testnet-contracts/library/utils/AdvancedStrings.sol";
42
- import {P2PSwapStructs} from "@evvm/testnet-contracts/contracts/p2pSwap/lib/P2PSwapStructs.sol";
43
- import {EvvmStructs} from "@evvm/testnet-contracts/interfaces/IEvvm.sol";
44
- import {EvvmService} from "@evvm/testnet-contracts/library/EvvmService.sol";
45
-
46
- contract P2PSwap is
47
- EvvmService,
48
- P2PSwapStructs
49
- {
41
+ contract P2PSwap is EvvmService, Structs {
50
42
  address owner;
51
43
  address owner_proposal;
52
44
  uint256 owner_timeToAccept;
@@ -83,12 +75,10 @@ contract P2PSwap is
83
75
  mapping(address => uint256) balancesOfContract;
84
76
 
85
77
  constructor(
86
- address _evvmAddress,
78
+ address _coreAddress,
87
79
  address _stakingAddress,
88
80
  address _owner
89
- )
90
- EvvmService(_evvmAddress, _stakingAddress)
91
- {
81
+ ) EvvmService(_coreAddress, _stakingAddress) {
92
82
  owner = _owner;
93
83
  maxLimitFillFixedFee = 0.001 ether;
94
84
  percentageFee = 500;
@@ -99,40 +89,49 @@ contract P2PSwap is
99
89
  });
100
90
  }
101
91
 
92
+ /**
93
+ * @notice Creates a new limit order in a specific trading market.
94
+ * @dev Locks tokenA in Core.sol and opens an order slot.
95
+ * Markets are automatically created for new token pairs.
96
+ * @param user Seller address.
97
+ * @param metadata Order details (tokens, amounts, nonce).
98
+ * @param signature Seller's authorization signature.
99
+ * @param priorityFeePay Optional priority fee for the executor.
100
+ * @param noncePay Nonce for the Core payment (locks tokenA).
101
+ * @param signaturePay Signature for the Core payment.
102
+ * @return market The ID of the market.
103
+ * @return orderId The ID of the order within that market.
104
+ */
102
105
  function makeOrder(
103
106
  address user,
104
107
  MetadataMakeOrder memory metadata,
105
108
  bytes memory signature,
106
- uint256 _priorityFee_Evvm,
107
- uint256 _nonce_Evvm,
108
- bool _priority_Evvm,
109
- bytes memory _signature_Evvm
109
+ uint256 priorityFeePay,
110
+ uint256 noncePay,
111
+ bytes memory signaturePay
110
112
  ) external returns (uint256 market, uint256 orderId) {
111
- if (
112
- !SignatureUtils.verifyMessageSignedForMakeOrder(
113
- evvm.getEvvmID(),
114
- user,
115
- metadata.nonce,
113
+ core.validateAndConsumeNonce(
114
+ user,
115
+ Hash.hashDataForMakeOrder(
116
116
  metadata.tokenA,
117
117
  metadata.tokenB,
118
118
  metadata.amountA,
119
- metadata.amountB,
120
- signature
121
- )
122
- ) {
123
- revert("Invalid signature");
124
- }
125
-
126
- verifyAsyncNonce(user, metadata.nonce);
119
+ metadata.amountB
120
+ ),
121
+ address(0),
122
+ metadata.nonce,
123
+ true,
124
+ signature
125
+ );
127
126
 
128
127
  requestPay(
129
128
  user,
130
129
  metadata.tokenA,
131
130
  metadata.amountA,
132
- _priorityFee_Evvm,
133
- _nonce_Evvm,
134
- _priority_Evvm,
135
- _signature_Evvm
131
+ priorityFeePay,
132
+ noncePay,
133
+ true,
134
+ signaturePay
136
135
  );
137
136
 
138
137
  market = findMarket(metadata.tokenA, metadata.tokenB);
@@ -163,67 +162,85 @@ contract P2PSwap is
163
162
  metadata.amountB
164
163
  );
165
164
 
166
- if (evvm.isAddressStaker(msg.sender)) {
167
- if (_priorityFee_Evvm > 0) {
165
+ if (core.isAddressStaker(msg.sender)) {
166
+ if (priorityFeePay > 0) {
168
167
  // send the executor the priorityFee
169
- makeCaPay(msg.sender, metadata.tokenA, _priorityFee_Evvm);
168
+ makeCaPay(msg.sender, metadata.tokenA, priorityFeePay);
170
169
  }
171
-
172
- // send some mate token reward to the executor (independent of the priorityFee the user attached)
173
- makeCaPay(
174
- msg.sender,
175
- MATE_TOKEN_ADDRESS,
176
- _priorityFee_Evvm > 0
177
- ? (evvm.getRewardAmount() * 3)
178
- : (evvm.getRewardAmount() * 2)
179
- );
180
170
  }
181
171
 
182
- markAsyncNonceAsUsed(user, metadata.nonce);
172
+ // send some mate token reward to the executor (independent of the priorityFee the user attached)
173
+ _rewardExecutor(msg.sender, priorityFeePay > 0 ? 3 : 2);
183
174
  }
184
175
 
176
+ /**
177
+ * @notice Cancels existing order and refunds locked tokens
178
+ * @dev Validates ownership, refunds tokenA, deletes order
179
+ *
180
+ * Cancellation Flow:
181
+ * 1. Validates signature via Core.sol
182
+ * 2. Validates user is order owner
183
+ * 3. Processes optional priority fee
184
+ * 4. Refunds locked tokenA to user
185
+ * 5. Deletes order (sets seller to address(0))
186
+ * 6. Rewards staker if applicable
187
+ *
188
+ * Core.sol Integration:
189
+ * - Validates signature with State.validateAndConsumeNonce
190
+ * - Uses async nonce (isAsyncExec = true)
191
+ * - Hash includes tokenA, tokenB, orderId
192
+ * - Prevents replay attacks and double cancellation
193
+ *
194
+ * Core.sol Integration:
195
+ * - Refunds tokenA via makeCaPay (order.amountA)
196
+ * - Priority fee via requestPay (if > 0)
197
+ * - Staker reward: 2-3x MATE via _rewardExecutor
198
+ * - makeCaPay handles staker priority fee distribution
199
+ *
200
+ * Security:
201
+ * - Only order owner can cancel
202
+ * - Atomic refund + deletion
203
+ * - Market slot becomes available for reuse
204
+ *
205
+ * @param user Address that owns the order
206
+ * @param metadata Cancel details (tokens, orderId, nonce)
207
+ * @param priorityFeePay Optional priority fee for staker
208
+ * @param noncePay Nonce for EVVM payment transaction
209
+ * @param signaturePay Signature for EVVM payment
210
+ */
185
211
  function cancelOrder(
186
212
  address user,
187
213
  MetadataCancelOrder memory metadata,
188
- uint256 _priorityFee_Evvm,
189
- uint256 _nonce_Evvm,
190
- bool _priority_Evvm,
191
- bytes memory _signature_Evvm
214
+ uint256 priorityFeePay,
215
+ uint256 noncePay,
216
+ bytes memory signaturePay
192
217
  ) external {
193
- if (
194
- !SignatureUtils.verifyMessageSignedForCancelOrder(
195
- evvm.getEvvmID(),
196
- user,
197
- metadata.nonce,
218
+ core.validateAndConsumeNonce(
219
+ user,
220
+ Hash.hashDataForCancelOrder(
198
221
  metadata.tokenA,
199
222
  metadata.tokenB,
200
- metadata.orderId,
201
- metadata.signature
202
- )
203
- ) {
204
- revert("Invalid signature");
205
- }
223
+ metadata.orderId
224
+ ),
225
+ metadata.originExecutor,
226
+ metadata.nonce,
227
+ true,
228
+ metadata.signature
229
+ );
206
230
 
207
231
  uint256 market = findMarket(metadata.tokenA, metadata.tokenB);
208
232
 
209
- verifyAsyncNonce(user, metadata.nonce);
210
-
211
- if (
212
- market == 0 ||
213
- ordersInsideMarket[market][metadata.orderId].seller != user
214
- ) {
215
- revert("Invalid order");
216
- }
233
+ _validateOrderOwnership(market, metadata.orderId, user);
217
234
 
218
- if (_priorityFee_Evvm > 0) {
235
+ if (priorityFeePay > 0) {
219
236
  requestPay(
220
237
  user,
221
238
  MATE_TOKEN_ADDRESS,
222
239
  0,
223
- _priorityFee_Evvm,
224
- _nonce_Evvm,
225
- _priority_Evvm,
226
- _signature_Evvm
240
+ priorityFeePay,
241
+ noncePay,
242
+ true,
243
+ signaturePay
227
244
  );
228
245
  }
229
246
 
@@ -233,62 +250,85 @@ contract P2PSwap is
233
250
  ordersInsideMarket[market][metadata.orderId].amountA
234
251
  );
235
252
 
236
- ordersInsideMarket[market][metadata.orderId].seller = address(0);
253
+ _clearOrderAndUpdateMarket(market, metadata.orderId);
237
254
 
238
- if (evvm.isAddressStaker(msg.sender)) {
239
- makeCaPay(
240
- msg.sender,
241
- MATE_TOKEN_ADDRESS,
242
- _priorityFee_Evvm > 0
243
- ? ((evvm.getRewardAmount() * 3) + _priorityFee_Evvm)
244
- : (evvm.getRewardAmount() * 2)
245
- );
255
+ if (core.isAddressStaker(msg.sender) && priorityFeePay > 0) {
256
+ makeCaPay(msg.sender, MATE_TOKEN_ADDRESS, priorityFeePay);
246
257
  }
247
- marketMetadata[market].ordersAvailable--;
248
- markAsyncNonceAsUsed(user, metadata.nonce);
258
+ _rewardExecutor(msg.sender, priorityFeePay > 0 ? 3 : 2);
249
259
  }
250
260
 
261
+ /**
262
+ * @notice Fills order using proportional fee model
263
+ * @dev Fee = amountB * percentageFee / 10,000
264
+ *
265
+ * Proportional Fee Execution Flow:
266
+ * 1. Validates signature via Core.sol
267
+ * 2. Validates market and order exist
268
+ * 3. Calculates fee: (amountB * percentageFee) / 10,000
269
+ * 4. Validates amountOfTokenBToFill >= amountB + fee
270
+ * 5. Collects tokenB + fee via Evvm.requestPay
271
+ * 6. Handles overpayment refund if any
272
+ * 7. Distributes payments (seller, service, staker)
273
+ * 8. Transfers tokenA to buyer via Evvm.makeCaPay
274
+ * 9. Rewards staker (4-5x MATE)
275
+ * 10. Deletes order
276
+ *
277
+ * Core.sol Integration:
278
+ * - Validates signature with State.validateAndConsumeNonce
279
+ * - Uses async nonce (isAsyncExec = true)
280
+ * - Hash includes tokenA, tokenB, orderId
281
+ * - Prevents double filling
282
+ *
283
+ * Core.sol Integration:
284
+ * - Collects tokenB via requestPay (amountB + fee)
285
+ * - Distributes via makeDisperseCaPay:
286
+ * * Seller: amountB + (fee * seller%)
287
+ * * Staker: priorityFee + (fee * staker%)
288
+ * * Service: fee * service% (accumulated)
289
+ * - Transfers tokenA to buyer via makeCaPay
290
+ * - Staker reward: 4-5x MATE via _rewardExecutor
291
+ *
292
+ * Fee Calculation:
293
+ * - Base: amountB (order requirement)
294
+ * - Fee: (amountB * percentageFee) / 10,000
295
+ * - Total: amountB + fee
296
+ * - Example: 5% fee = 500 / 10,000
297
+ *
298
+ * @param user Address filling the order (buyer)
299
+ * @param metadata Dispatch details (tokens, orderId, amount)
300
+ * @param priorityFeePay Optional priority fee for staker
301
+ * @param noncePay Nonce for EVVM payment transaction
302
+ * @param signaturePay Signature for EVVM payment
303
+ */
251
304
  function dispatchOrder_fillPropotionalFee(
252
305
  address user,
253
306
  MetadataDispatchOrder memory metadata,
254
- uint256 _priorityFee_Evvm,
255
- uint256 _nonce_Evvm,
256
- bool _priority_Evvm,
257
- bytes memory _signature_Evvm
307
+ uint256 priorityFeePay,
308
+ uint256 noncePay,
309
+ bytes memory signaturePay
258
310
  ) external {
259
- if (
260
- !SignatureUtils.verifyMessageSignedForDispatchOrder(
261
- evvm.getEvvmID(),
262
- user,
263
- metadata.nonce,
311
+ core.validateAndConsumeNonce(
312
+ user,
313
+ Hash.hashDataForDispatchOrder(
264
314
  metadata.tokenA,
265
315
  metadata.tokenB,
266
- metadata.orderId,
267
- metadata.signature
268
- )
269
- ) {
270
- revert("Invalid signature");
271
- }
316
+ metadata.orderId
317
+ ),
318
+ metadata.originExecutor,
319
+ metadata.nonce,
320
+ true,
321
+ metadata.signature
322
+ );
272
323
 
273
324
  uint256 market = findMarket(metadata.tokenA, metadata.tokenB);
274
325
 
275
- verifyAsyncNonce(user, metadata.nonce);
276
-
277
- if (
278
- market == 0 ||
279
- ordersInsideMarket[market][metadata.orderId].seller == address(0)
280
- ) {
281
- revert("Invalid order");
282
- }
326
+ Order storage order = _validateMarketAndOrder(market, metadata.orderId);
283
327
 
284
- uint256 fee = calculateFillPropotionalFee(
285
- ordersInsideMarket[market][metadata.orderId].amountB
286
- );
328
+ uint256 fee = calculateFillPropotionalFee(order.amountB);
329
+ uint256 requiredAmount = order.amountB + fee;
287
330
 
288
- if (
289
- metadata.amountOfTokenBToFill <
290
- ordersInsideMarket[market][metadata.orderId].amountB + fee
291
- ) {
331
+ if (metadata.amountOfTokenBToFill < requiredAmount) {
292
332
  revert("Insuficient amountOfTokenToFill");
293
333
  }
294
334
 
@@ -296,120 +336,127 @@ contract P2PSwap is
296
336
  user,
297
337
  metadata.tokenB,
298
338
  metadata.amountOfTokenBToFill,
299
- _priorityFee_Evvm,
300
- _nonce_Evvm,
301
- _priority_Evvm,
302
- _signature_Evvm
339
+ priorityFeePay,
340
+ noncePay,
341
+ true,
342
+ signaturePay
303
343
  );
304
344
 
305
345
  // si es mas del fee + el monto de la orden hacemos caPay al usuario del sobranate
306
- if (
307
- metadata.amountOfTokenBToFill >
308
- ordersInsideMarket[market][metadata.orderId].amountB + fee
309
- ) {
310
- makeCaPay(
311
- user,
312
- metadata.tokenB,
313
- metadata.amountOfTokenBToFill -
314
- (ordersInsideMarket[market][metadata.orderId].amountB + fee)
315
- );
316
- }
317
-
318
- EvvmStructs.DisperseCaPayMetadata[]
319
- memory toData = new EvvmStructs.DisperseCaPayMetadata[](2);
320
-
321
- uint256 sellerAmount = ordersInsideMarket[market][metadata.orderId]
322
- .amountB + ((fee * rewardPercentage.seller) / 10_000);
323
- uint256 executorAmount = _priorityFee_Evvm +
324
- ((fee * rewardPercentage.mateStaker) / 10_000);
325
-
326
- // pay seller
327
- toData[0] = EvvmStructs.DisperseCaPayMetadata(
328
- sellerAmount,
329
- ordersInsideMarket[market][metadata.orderId].seller
330
- );
331
- // pay executor
332
- toData[1] = EvvmStructs.DisperseCaPayMetadata(
333
- executorAmount,
334
- msg.sender
346
+ bool didRefund = _handleOverpaymentRefund(
347
+ user,
348
+ metadata.tokenB,
349
+ metadata.amountOfTokenBToFill,
350
+ requiredAmount
335
351
  );
336
352
 
337
- balancesOfContract[metadata.tokenB] +=
338
- (fee * rewardPercentage.service) /
339
- 10_000;
340
-
341
- makeDisperseCaPay(
342
- toData,
353
+ // distribute payments to seller and executor
354
+ _distributePayments(
343
355
  metadata.tokenB,
344
- toData[0].amount + toData[1].amount
356
+ order.amountB,
357
+ fee,
358
+ order.seller,
359
+ msg.sender,
360
+ priorityFeePay
345
361
  );
346
362
 
347
363
  // pay user with token A
348
- makeCaPay(
349
- user,
350
- metadata.tokenA,
351
- ordersInsideMarket[market][metadata.orderId].amountA
352
- );
364
+ makeCaPay(user, metadata.tokenA, order.amountA);
353
365
 
354
- if (evvm.isAddressStaker(msg.sender)) {
355
- makeCaPay(
356
- msg.sender,
357
- MATE_TOKEN_ADDRESS,
358
- metadata.amountOfTokenBToFill >
359
- ordersInsideMarket[market][metadata.orderId].amountB + fee
360
- ? evvm.getRewardAmount() * 5
361
- : evvm.getRewardAmount() * 4
362
- );
363
- }
366
+ _rewardExecutor(msg.sender, didRefund ? 5 : 4);
364
367
 
365
- ordersInsideMarket[market][metadata.orderId].seller = address(0);
366
- marketMetadata[market].ordersAvailable--;
367
- markAsyncNonceAsUsed(user, metadata.nonce);
368
+ _clearOrderAndUpdateMarket(market, metadata.orderId);
368
369
  }
369
370
 
371
+ /**
372
+ * @notice Fills order using fixed/capped fee model
373
+ * @dev Fee = min(proportionalFee, maxLimitFillFixedFee)
374
+ * with -10% tolerance
375
+ *
376
+ * Fixed Fee Execution Flow:
377
+ * 1. Validates signature via Core.sol
378
+ * 2. Validates market and order exist
379
+ * 3. Calculates capped fee and 10% tolerance
380
+ * 4. Validates amountOfTokenBToFill >= amountB + fee - 10%
381
+ * 5. Collects tokenB + amount via Evvm.requestPay
382
+ * 6. Calculates final fee based on actual payment
383
+ * 7. Handles overpayment refund if any
384
+ * 8. Distributes payments (seller, service, staker)
385
+ * 9. Transfers tokenA to buyer via Evvm.makeCaPay
386
+ * 10. Rewards staker (4-5x MATE)
387
+ * 11. Deletes order
388
+ *
389
+ * Core.sol Integration:
390
+ * - Validates signature with State.validateAndConsumeNonce
391
+ * - Uses async nonce (isAsyncExec = true)
392
+ * - Hash includes tokenA, tokenB, orderId
393
+ * - Prevents double filling
394
+ *
395
+ * Core.sol Integration:
396
+ * - Collects tokenB via requestPay (variable amount)
397
+ * - Distributes via makeDisperseCaPay:
398
+ * * Seller: amountB + (finalFee * seller%)
399
+ * * Staker: priorityFee + (finalFee * staker%)
400
+ * * Service: finalFee * service% (accumulated)
401
+ * - Transfers tokenA to buyer via makeCaPay
402
+ * - Staker reward: 4-5x MATE via _rewardExecutor
403
+ *
404
+ * Fee Calculation:
405
+ * - Base: amountB (order requirement)
406
+ * - ProportionalFee: (amountB * percentageFee) / 10,000
407
+ * - Fee: min(proportionalFee, maxLimitFillFixedFee)
408
+ * - Tolerance: fee * 10% (fee10)
409
+ * - MinRequired: amountB + fee - fee10
410
+ * - FullRequired: amountB + fee
411
+ * - FinalFee: Based on actual payment amount
412
+ *
413
+ * Tolerance Range:
414
+ * - Accepts payment between [amountB + 90% fee] and
415
+ * [amountB + 100% fee]
416
+ * - Calculates actual fee from payment received
417
+ * - Enables flexible fee payment for users
418
+ *
419
+ * @param user Address filling the order (buyer)
420
+ * @param metadata Dispatch details (tokens, orderId, amount)
421
+ * @param priorityFeePay Optional priority fee for staker
422
+ * @param noncePay Nonce for EVVM payment transaction
423
+ * @param signaturePay Signature for EVVM payment
424
+ * @param maxFillFixedFee Max fee cap (for testing)
425
+ */
370
426
  function dispatchOrder_fillFixedFee(
371
427
  address user,
372
428
  MetadataDispatchOrder memory metadata,
373
- uint256 _priorityFee_Evvm,
374
- uint256 _nonce_Evvm,
375
- bool _priority_Evvm,
376
- bytes memory _signature_Evvm,
429
+ uint256 priorityFeePay,
430
+ uint256 noncePay,
431
+ bytes memory signaturePay,
377
432
  uint256 maxFillFixedFee ///@dev for testing purposes
378
433
  ) external {
379
- if (
380
- !SignatureUtils.verifyMessageSignedForDispatchOrder(
381
- evvm.getEvvmID(),
382
- user,
383
- metadata.nonce,
434
+ core.validateAndConsumeNonce(
435
+ user,
436
+ Hash.hashDataForDispatchOrder(
384
437
  metadata.tokenA,
385
438
  metadata.tokenB,
386
- metadata.orderId,
387
- metadata.signature
388
- )
389
- ) {
390
- revert("Invalid signature");
391
- }
439
+ metadata.orderId
440
+ ),
441
+ metadata.originExecutor,
442
+ metadata.nonce,
443
+ true,
444
+ metadata.signature
445
+ );
392
446
 
393
447
  uint256 market = findMarket(metadata.tokenA, metadata.tokenB);
394
448
 
395
- verifyAsyncNonce(user, metadata.nonce);
396
-
397
- if (
398
- market == 0 ||
399
- ordersInsideMarket[market][metadata.orderId].seller == address(0)
400
- ) {
401
- revert("Invalid order");
402
- }
449
+ Order storage order = _validateMarketAndOrder(market, metadata.orderId);
403
450
 
404
451
  (uint256 fee, uint256 fee10) = calculateFillFixedFee(
405
- ordersInsideMarket[market][metadata.orderId].amountB,
452
+ order.amountB,
406
453
  maxFillFixedFee
407
454
  );
408
455
 
409
- if (
410
- metadata.amountOfTokenBToFill <
411
- ordersInsideMarket[market][metadata.orderId].amountB + fee - fee10
412
- ) {
456
+ uint256 minRequired = order.amountB + fee - fee10;
457
+ uint256 fullRequired = order.amountB + fee;
458
+
459
+ if (metadata.amountOfTokenBToFill < minRequired) {
413
460
  revert("Insuficient amountOfTokenBToFill");
414
461
  }
415
462
 
@@ -417,82 +464,44 @@ contract P2PSwap is
417
464
  user,
418
465
  metadata.tokenB,
419
466
  metadata.amountOfTokenBToFill,
420
- _priorityFee_Evvm,
421
- _nonce_Evvm,
422
- _priority_Evvm,
423
- _signature_Evvm
467
+ priorityFeePay,
468
+ noncePay,
469
+ true,
470
+ signaturePay
424
471
  );
425
472
 
426
- uint256 finalFee = metadata.amountOfTokenBToFill >=
427
- ordersInsideMarket[market][metadata.orderId].amountB +
428
- fee -
429
- fee10 &&
430
- metadata.amountOfTokenBToFill <
431
- ordersInsideMarket[market][metadata.orderId].amountB + fee
432
- ? metadata.amountOfTokenBToFill -
433
- ordersInsideMarket[market][metadata.orderId].amountB
434
- : fee;
473
+ uint256 finalFee = _calculateFinalFee(
474
+ metadata.amountOfTokenBToFill,
475
+ order.amountB,
476
+ fee,
477
+ fee10
478
+ );
435
479
 
436
480
  // si es mas del fee + el monto de la orden hacemos caPay al usuario del sobranate
437
- if (
438
- metadata.amountOfTokenBToFill >
439
- ordersInsideMarket[market][metadata.orderId].amountB + fee
440
- ) {
441
- makeCaPay(
442
- user,
443
- metadata.tokenB,
444
- metadata.amountOfTokenBToFill -
445
- (ordersInsideMarket[market][metadata.orderId].amountB + fee)
446
- );
447
- }
448
-
449
- EvvmStructs.DisperseCaPayMetadata[]
450
- memory toData = new EvvmStructs.DisperseCaPayMetadata[](2);
451
-
452
- toData[0] = EvvmStructs.DisperseCaPayMetadata(
453
- ordersInsideMarket[market][metadata.orderId].amountB +
454
- ((finalFee * rewardPercentage.seller) / 10_000),
455
- ordersInsideMarket[market][metadata.orderId].seller
456
- );
457
- toData[1] = EvvmStructs.DisperseCaPayMetadata(
458
- _priorityFee_Evvm +
459
- ((finalFee * rewardPercentage.mateStaker) / 10_000),
460
- msg.sender
481
+ bool didRefund = _handleOverpaymentRefund(
482
+ user,
483
+ metadata.tokenB,
484
+ metadata.amountOfTokenBToFill,
485
+ fullRequired
461
486
  );
462
487
 
463
- balancesOfContract[metadata.tokenB] +=
464
- (finalFee * rewardPercentage.service) /
465
- 10_000;
466
-
467
- makeDisperseCaPay(
468
- toData,
488
+ // distribute payments to seller and executor
489
+ _distributePayments(
469
490
  metadata.tokenB,
470
- toData[0].amount + toData[1].amount
491
+ order.amountB,
492
+ finalFee,
493
+ order.seller,
494
+ msg.sender,
495
+ priorityFeePay
471
496
  );
472
497
 
473
- makeCaPay(
474
- user,
475
- metadata.tokenA,
476
- ordersInsideMarket[market][metadata.orderId].amountA
477
- );
498
+ makeCaPay(user, metadata.tokenA, order.amountA);
478
499
 
479
- if (evvm.isAddressStaker(msg.sender)) {
480
- makeCaPay(
481
- msg.sender,
482
- MATE_TOKEN_ADDRESS,
483
- metadata.amountOfTokenBToFill >
484
- ordersInsideMarket[market][metadata.orderId].amountB + fee
485
- ? evvm.getRewardAmount() * 5
486
- : evvm.getRewardAmount() * 4
487
- );
488
- }
500
+ _rewardExecutor(msg.sender, didRefund ? 5 : 4);
489
501
 
490
- ordersInsideMarket[market][metadata.orderId].seller = address(0);
491
- marketMetadata[market].ordersAvailable--;
492
- markAsyncNonceAsUsed(user, metadata.nonce);
502
+ _clearOrderAndUpdateMarket(market, metadata.orderId);
493
503
  }
494
504
 
495
- //devolver el 0.05% del monto de la orden
496
505
  function calculateFillPropotionalFee(
497
506
  uint256 amount
498
507
  ) internal view returns (uint256 fee) {
@@ -512,6 +521,151 @@ contract P2PSwap is
512
521
  }
513
522
  }
514
523
 
524
+ /**
525
+ * @dev Calculates the final fee for fixed fee dispatch considering tolerance range
526
+ * @param amountPaid Amount paid by user
527
+ * @param orderAmount Base order amount
528
+ * @param fee Full fee amount
529
+ * @param fee10 10% tolerance of fee
530
+ * @return finalFee The calculated final fee
531
+ */
532
+ function _calculateFinalFee(
533
+ uint256 amountPaid,
534
+ uint256 orderAmount,
535
+ uint256 fee,
536
+ uint256 fee10
537
+ ) internal pure returns (uint256 finalFee) {
538
+ uint256 minRequired = orderAmount + fee - fee10;
539
+ uint256 fullRequired = orderAmount + fee;
540
+
541
+ if (amountPaid >= minRequired && amountPaid < fullRequired) {
542
+ finalFee = amountPaid - orderAmount;
543
+ } else {
544
+ finalFee = fee;
545
+ }
546
+ }
547
+
548
+ //◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢
549
+ // Internal helper functions to avoid Stack too deep
550
+ //◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢
551
+
552
+ /**
553
+ * @dev Validates that a market and order exist and are valid
554
+ * @param market The market ID
555
+ * @param orderId The order ID within the market
556
+ * @return order The order data if valid
557
+ */
558
+ function _validateMarketAndOrder(
559
+ uint256 market,
560
+ uint256 orderId
561
+ ) internal view returns (Order storage order) {
562
+ if (market == 0) {
563
+ revert("Invalid order");
564
+ }
565
+ order = ordersInsideMarket[market][orderId];
566
+ if (order.seller == address(0)) {
567
+ revert("Invalid order");
568
+ }
569
+ }
570
+
571
+ /**
572
+ * @dev Validates that a market exists and the user is the seller of the order
573
+ * @param market The market ID
574
+ * @param orderId The order ID
575
+ * @param user The expected seller address
576
+ */
577
+ function _validateOrderOwnership(
578
+ uint256 market,
579
+ uint256 orderId,
580
+ address user
581
+ ) internal view {
582
+ if (market == 0 || ordersInsideMarket[market][orderId].seller != user) {
583
+ revert("Invalid order");
584
+ }
585
+ }
586
+
587
+ /**
588
+ * @dev Rewards the executor (staker) with MATE tokens based on operation complexity
589
+ * @param executor The address of the executor
590
+ * @param multiplier The reward multiplier (2, 3, 4, or 5)
591
+ */
592
+ function _rewardExecutor(address executor, uint256 multiplier) internal {
593
+ if (core.isAddressStaker(executor)) {
594
+ makeCaPay(
595
+ executor,
596
+ MATE_TOKEN_ADDRESS,
597
+ core.getRewardAmount() * multiplier
598
+ );
599
+ }
600
+ }
601
+
602
+ /**
603
+ * @dev Clears an order and updates market metadata
604
+ * @param market The market ID
605
+ * @param orderId The order ID to clear
606
+ */
607
+ function _clearOrderAndUpdateMarket(
608
+ uint256 market,
609
+ uint256 orderId
610
+ ) internal {
611
+ ordersInsideMarket[market][orderId].seller = address(0);
612
+ marketMetadata[market].ordersAvailable--;
613
+ }
614
+
615
+ /**
616
+ * @dev Handles refund to user if they overpaid
617
+ * @param user The user address to refund
618
+ * @param token The token address
619
+ * @param amountPaid The amount the user paid
620
+ * @param amountRequired The required amount (order amount + fee)
621
+ * @return didRefund Whether a refund was made
622
+ */
623
+ function _handleOverpaymentRefund(
624
+ address user,
625
+ address token,
626
+ uint256 amountPaid,
627
+ uint256 amountRequired
628
+ ) internal returns (bool didRefund) {
629
+ if (amountPaid > amountRequired) {
630
+ makeCaPay(user, token, amountPaid - amountRequired);
631
+ return true;
632
+ }
633
+ return false;
634
+ }
635
+
636
+ /**
637
+ * @dev Distributes payment to seller and executor, and accumulates service fee
638
+ * @param token The token address for payment
639
+ * @param orderAmount The base order amount
640
+ * @param fee The fee amount to distribute
641
+ * @param seller The seller address
642
+ * @param executor The executor address
643
+ * @param priorityFee The priority fee for executor
644
+ */
645
+ function _distributePayments(
646
+ address token,
647
+ uint256 orderAmount,
648
+ uint256 fee,
649
+ address seller,
650
+ address executor,
651
+ uint256 priorityFee
652
+ ) internal {
653
+ uint256 sellerAmount = orderAmount +
654
+ ((fee * rewardPercentage.seller) / 10_000);
655
+ uint256 executorAmount = priorityFee +
656
+ ((fee * rewardPercentage.mateStaker) / 10_000);
657
+
658
+ CoreStructs.DisperseCaPayMetadata[]
659
+ memory toData = new CoreStructs.DisperseCaPayMetadata[](2);
660
+
661
+ toData[0] = CoreStructs.DisperseCaPayMetadata(sellerAmount, seller);
662
+ toData[1] = CoreStructs.DisperseCaPayMetadata(executorAmount, executor);
663
+
664
+ balancesOfContract[token] += (fee * rewardPercentage.service) / 10_000;
665
+
666
+ makeDisperseCaPay(toData, token, sellerAmount + executorAmount);
667
+ }
668
+
515
669
  function createMarket(
516
670
  address tokenA,
517
671
  address tokenB