strade-stx 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.
Files changed (129) hide show
  1. package/.activity_counter +1 -0
  2. package/.gitattributes +3 -0
  3. package/.vscode/settings.json +4 -0
  4. package/.vscode/tasks.json +19 -0
  5. package/CHANGELOG.md +1 -0
  6. package/Clarinet.toml +56 -0
  7. package/Clarinet.toml.backup +174 -0
  8. package/Clarinet.toml.old +146 -0
  9. package/DEPLOYMENT_RESULTS.md +160 -0
  10. package/README.md +344 -0
  11. package/TODO.md +34 -0
  12. package/contracts/CoreMarketPlace.clar +227 -0
  13. package/contracts/DisputeResolution_clar.clar +265 -0
  14. package/contracts/EscrowService.clar +171 -0
  15. package/contracts/UserProfile.clar +280 -0
  16. package/contracts/ft-trait.clar +24 -0
  17. package/contracts/token.clar +178 -0
  18. package/costs-reports.json +76026 -0
  19. package/deployments/default.mainnet-plan.yaml +67 -0
  20. package/deployments/default.simnet-plan.yaml +105 -0
  21. package/deployments/default.testnet-plan.yaml +67 -0
  22. package/deployments/new-contracts.testnet-plan.yaml +32 -0
  23. package/frontend/README.md +10 -0
  24. package/frontend/components.json +22 -0
  25. package/frontend/dist/assets/index-BacuuL66.css +1 -0
  26. package/frontend/dist/assets/index-jryypd5B.js +194 -0
  27. package/frontend/dist/favicon.png +0 -0
  28. package/frontend/dist/index.html +15 -0
  29. package/frontend/dist/manifest.json +15 -0
  30. package/frontend/dist/vite.svg +1 -0
  31. package/frontend/empty-mock.js +1 -0
  32. package/frontend/eslint.config.js +23 -0
  33. package/frontend/eslint.config.mjs +25 -0
  34. package/frontend/index.html +14 -0
  35. package/frontend/next.config.ts +17 -0
  36. package/frontend/package-lock.json +14740 -0
  37. package/frontend/package.json +56 -0
  38. package/frontend/postcss.config.js +5 -0
  39. package/frontend/postcss.config.mjs +5 -0
  40. package/frontend/public/favicon.png +0 -0
  41. package/frontend/public/file.svg +1 -0
  42. package/frontend/public/globe.svg +1 -0
  43. package/frontend/public/manifest.json +15 -0
  44. package/frontend/public/next.svg +1 -0
  45. package/frontend/public/vercel.svg +1 -0
  46. package/frontend/public/vite.svg +1 -0
  47. package/frontend/public/window.svg +1 -0
  48. package/frontend/src/App.css +42 -0
  49. package/frontend/src/App.tsx +177 -0
  50. package/frontend/src/app/about/page.tsx +208 -0
  51. package/frontend/src/app/favicon.ico +0 -0
  52. package/frontend/src/app/globals.css +129 -0
  53. package/frontend/src/app/help/page.tsx +167 -0
  54. package/frontend/src/app/how-it-works/page.tsx +274 -0
  55. package/frontend/src/app/layout.tsx +55 -0
  56. package/frontend/src/app/marketplace/page.tsx +324 -0
  57. package/frontend/src/app/my-listings/page.tsx +318 -0
  58. package/frontend/src/app/page.tsx +15 -0
  59. package/frontend/src/assets/react.svg +1 -0
  60. package/frontend/src/components/ConfirmDialog.tsx +54 -0
  61. package/frontend/src/components/CreateListingForm.tsx +231 -0
  62. package/frontend/src/components/ErrorBoundary.tsx +73 -0
  63. package/frontend/src/components/FilterPanel.tsx +10 -0
  64. package/frontend/src/components/Footer.tsx +100 -0
  65. package/frontend/src/components/Header.tsx +268 -0
  66. package/frontend/src/components/ImageUpload.tsx +147 -0
  67. package/frontend/src/components/LandingPage.tsx +322 -0
  68. package/frontend/src/components/ListingCard.tsx +154 -0
  69. package/frontend/src/components/LoadingSkeleton.tsx +44 -0
  70. package/frontend/src/components/MobileNav.tsx +89 -0
  71. package/frontend/src/components/NotificationBell.tsx +8 -0
  72. package/frontend/src/components/NotificationPanel.tsx +14 -0
  73. package/frontend/src/components/README.md +14 -0
  74. package/frontend/src/components/SearchBar.tsx +10 -0
  75. package/frontend/src/components/TestnetBanner.tsx +29 -0
  76. package/frontend/src/components/ThemeToggle.tsx +32 -0
  77. package/frontend/src/components/__tests__/Header.test.tsx +70 -0
  78. package/frontend/src/components/__tests__/ListingCard.test.tsx +86 -0
  79. package/frontend/src/components/providers/ThemeProvider.tsx +9 -0
  80. package/frontend/src/components/ui/alert-dialog.tsx +141 -0
  81. package/frontend/src/components/ui/avatar.tsx +53 -0
  82. package/frontend/src/components/ui/badge.tsx +46 -0
  83. package/frontend/src/components/ui/button.tsx +60 -0
  84. package/frontend/src/components/ui/card.tsx +92 -0
  85. package/frontend/src/components/ui/dialog.tsx +143 -0
  86. package/frontend/src/components/ui/dropdown-menu.tsx +257 -0
  87. package/frontend/src/components/ui/input.tsx +21 -0
  88. package/frontend/src/components/ui/label.tsx +24 -0
  89. package/frontend/src/components/ui/select.tsx +187 -0
  90. package/frontend/src/components/ui/sonner.tsx +40 -0
  91. package/frontend/src/components/ui/textarea.tsx +18 -0
  92. package/frontend/src/context/README.md +27 -0
  93. package/frontend/src/index.css +166 -0
  94. package/frontend/src/lib/notificationEvents.ts +10 -0
  95. package/frontend/src/lib/notificationStore.ts +13 -0
  96. package/frontend/src/lib/notifications.ts +13 -0
  97. package/frontend/src/lib/search.ts +28 -0
  98. package/frontend/src/lib/stacks.ts +189 -0
  99. package/frontend/src/lib/utils.ts +6 -0
  100. package/frontend/src/main.tsx +10 -0
  101. package/frontend/src/test/setup.ts +23 -0
  102. package/frontend/src/types.d.ts +9 -0
  103. package/frontend/tsconfig.app.json +28 -0
  104. package/frontend/tsconfig.json +41 -0
  105. package/frontend/tsconfig.node.json +26 -0
  106. package/frontend/vercel.json +4 -0
  107. package/frontend/vite.config.ts +6 -0
  108. package/frontend/vitest.config.ts +17 -0
  109. package/lcov.info +31338 -0
  110. package/mainnetcontracts.md +16 -0
  111. package/package.json +53 -0
  112. package/scripts/auto-activity.sh +9 -0
  113. package/scripts/cancel-pending.ts +67 -0
  114. package/scripts/check-balances.ts +23 -0
  115. package/scripts/distribute-evenly.ts +56 -0
  116. package/scripts/drain-accounts.ts +70 -0
  117. package/scripts/fund-accounts.ts +88 -0
  118. package/scripts/fund-active.ts +59 -0
  119. package/scripts/fund-unfunded.ts +88 -0
  120. package/scripts/generate-activity.ts +181 -0
  121. package/scripts/git-activity-generator.ts +154 -0
  122. package/scripts/mobile-server.ts +123 -0
  123. package/settings/Devnet.toml +155 -0
  124. package/settings/Mainnet.toml +7 -0
  125. package/settings/Testnet.toml +9 -0
  126. package/tests/CoreMarketPlace.fuzz.test.ts +435 -0
  127. package/tests/CoreMarketPlace.test.ts +564 -0
  128. package/tsconfig.json +26 -0
  129. package/vitest.config.js +49 -0
@@ -0,0 +1,564 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { Cl } from "@stacks/transactions";
3
+
4
+ const CONTRACT_NAME = 'CoreMarketPlace';
5
+ const LISTING_NAME = 'Test Item';
6
+ const LISTING_DESCRIPTION = 'A test item for sale';
7
+ const LISTING_PRICE = 1000000; // 1 STX in micro-STX
8
+ const LISTING_DURATION = 144; // ~1 day in blocks
9
+
10
+ // Helper function to create string arguments
11
+ const utf8 = (str: string) => Cl.stringUtf8(str);
12
+
13
+ describe("CoreMarketPlace Contract Tests", () => {
14
+
15
+ it("ensures that contract initializes correctly", () => {
16
+ const { result } = simnet.callReadOnlyFn(
17
+ CONTRACT_NAME,
18
+ 'get-last-listing-id',
19
+ [],
20
+ simnet.deployer
21
+ );
22
+
23
+ expect(result).toBeOk(Cl.uint(0));
24
+ });
25
+
26
+ it("ensures that users can create a listing with valid parameters", () => {
27
+ const seller = simnet.getAccounts().get('wallet_1')!;
28
+
29
+ const { result } = simnet.callPublicFn(
30
+ CONTRACT_NAME,
31
+ 'create-listing',
32
+ [
33
+ utf8(LISTING_NAME),
34
+ utf8(LISTING_DESCRIPTION),
35
+ Cl.uint(LISTING_PRICE),
36
+ Cl.uint(LISTING_DURATION)
37
+ ],
38
+ seller
39
+ );
40
+
41
+ expect(result).toBeOk(Cl.uint(1));
42
+
43
+ // Verify the listing was created correctly
44
+ const { result: listingResult } = simnet.callReadOnlyFn(
45
+ CONTRACT_NAME,
46
+ 'get-listing',
47
+ [Cl.uint(1)],
48
+ seller
49
+ );
50
+
51
+ // Check that the result is Some (not None)
52
+ expect(listingResult).not.toBeNone();
53
+ });
54
+
55
+ it("ensures that creating a listing with zero price fails", () => {
56
+ const seller = simnet.getAccounts().get('wallet_1')!;
57
+
58
+ const { result } = simnet.callPublicFn(
59
+ CONTRACT_NAME,
60
+ 'create-listing',
61
+ [
62
+ utf8(LISTING_NAME),
63
+ utf8(LISTING_DESCRIPTION),
64
+ Cl.uint(0),
65
+ Cl.uint(LISTING_DURATION)
66
+ ],
67
+ seller
68
+ );
69
+
70
+ expect(result).toBeErr(Cl.error(Cl.uint(102))); // ERR_INVALID_PRICE
71
+ });
72
+
73
+ it("ensures that creating a listing with empty name fails", () => {
74
+ const seller = simnet.getAccounts().get('wallet_1')!;
75
+
76
+ const { result } = simnet.callPublicFn(
77
+ CONTRACT_NAME,
78
+ 'create-listing',
79
+ [
80
+ utf8(''),
81
+ utf8(LISTING_DESCRIPTION),
82
+ Cl.uint(LISTING_PRICE),
83
+ Cl.uint(LISTING_DURATION)
84
+ ],
85
+ seller
86
+ );
87
+
88
+ expect(result).toBeErr(Cl.error(Cl.uint(109))); // ERR_INVALID_INPUT
89
+ });
90
+
91
+ it("ensures that creating a listing with invalid duration fails", () => {
92
+ const seller = simnet.getAccounts().get('wallet_1')!;
93
+
94
+ const { result } = simnet.callPublicFn(
95
+ CONTRACT_NAME,
96
+ 'create-listing',
97
+ [
98
+ utf8(LISTING_NAME),
99
+ utf8(LISTING_DESCRIPTION),
100
+ Cl.uint(LISTING_PRICE),
101
+ Cl.uint(0)
102
+ ],
103
+ seller
104
+ );
105
+
106
+ expect(result).toBeErr(Cl.error(Cl.uint(110))); // ERR_INVALID_DURATION
107
+ });
108
+
109
+ it("ensures that creating a listing with duration exceeding max fails", () => {
110
+ const seller = simnet.getAccounts().get('wallet_1')!;
111
+
112
+ const { result } = simnet.callPublicFn(
113
+ CONTRACT_NAME,
114
+ 'create-listing',
115
+ [
116
+ utf8(LISTING_NAME),
117
+ utf8(LISTING_DESCRIPTION),
118
+ Cl.uint(LISTING_PRICE),
119
+ Cl.uint(52561)
120
+ ],
121
+ seller
122
+ );
123
+
124
+ expect(result).toBeErr(Cl.error(Cl.uint(110))); // ERR_INVALID_DURATION
125
+ });
126
+
127
+ it("ensures that seller can update their own listing", () => {
128
+ const seller = simnet.getAccounts().get('wallet_1')!;
129
+ const newPrice = 2000000;
130
+ const newDescription = 'Updated description';
131
+
132
+ // Create listing
133
+ simnet.callPublicFn(
134
+ CONTRACT_NAME,
135
+ 'create-listing',
136
+ [
137
+ utf8(LISTING_NAME),
138
+ utf8(LISTING_DESCRIPTION),
139
+ Cl.uint(LISTING_PRICE),
140
+ Cl.uint(LISTING_DURATION)
141
+ ],
142
+ seller
143
+ );
144
+
145
+ // Update listing
146
+ const { result } = simnet.callPublicFn(
147
+ CONTRACT_NAME,
148
+ 'update-listing',
149
+ [
150
+ Cl.uint(1),
151
+ Cl.uint(newPrice),
152
+ utf8(newDescription)
153
+ ],
154
+ seller
155
+ );
156
+
157
+ expect(result).toBeOk(Cl.bool(true));
158
+ });
159
+
160
+ it("ensures that non-seller cannot update a listing", () => {
161
+ const seller = simnet.getAccounts().get('wallet_1')!;
162
+ const nonSeller = simnet.getAccounts().get('wallet_2')!;
163
+
164
+ // Create listing
165
+ simnet.callPublicFn(
166
+ CONTRACT_NAME,
167
+ 'create-listing',
168
+ [
169
+ utf8(LISTING_NAME),
170
+ utf8(LISTING_DESCRIPTION),
171
+ Cl.uint(LISTING_PRICE),
172
+ Cl.uint(LISTING_DURATION)
173
+ ],
174
+ seller
175
+ );
176
+
177
+ // Try to update as non-seller
178
+ const { result } = simnet.callPublicFn(
179
+ CONTRACT_NAME,
180
+ 'update-listing',
181
+ [
182
+ Cl.uint(1),
183
+ Cl.uint(2000000),
184
+ utf8('Hacked description')
185
+ ],
186
+ nonSeller
187
+ );
188
+
189
+ expect(result).toBeErr(Cl.error(Cl.uint(107))); // ERR_NOT_SELLER
190
+ });
191
+
192
+ it("ensures that seller can cancel their own listing", () => {
193
+ const seller = simnet.getAccounts().get('wallet_1')!;
194
+
195
+ // Create listing
196
+ simnet.callPublicFn(
197
+ CONTRACT_NAME,
198
+ 'create-listing',
199
+ [
200
+ utf8(LISTING_NAME),
201
+ utf8(LISTING_DESCRIPTION),
202
+ Cl.uint(LISTING_PRICE),
203
+ Cl.uint(LISTING_DURATION)
204
+ ],
205
+ seller
206
+ );
207
+
208
+ // Cancel listing
209
+ const { result } = simnet.callPublicFn(
210
+ CONTRACT_NAME,
211
+ 'cancel-listing',
212
+ [Cl.uint(1)],
213
+ seller
214
+ );
215
+
216
+ expect(result).toBeOk(Cl.bool(true));
217
+ });
218
+
219
+ it("ensures that non-seller cannot cancel a listing", () => {
220
+ const seller = simnet.getAccounts().get('wallet_1')!;
221
+ const nonSeller = simnet.getAccounts().get('wallet_2')!;
222
+
223
+ // Create listing
224
+ simnet.callPublicFn(
225
+ CONTRACT_NAME,
226
+ 'create-listing',
227
+ [
228
+ utf8(LISTING_NAME),
229
+ utf8(LISTING_DESCRIPTION),
230
+ Cl.uint(LISTING_PRICE),
231
+ Cl.uint(LISTING_DURATION)
232
+ ],
233
+ seller
234
+ );
235
+
236
+ // Try to cancel as non-seller
237
+ const { result } = simnet.callPublicFn(
238
+ CONTRACT_NAME,
239
+ 'cancel-listing',
240
+ [Cl.uint(1)],
241
+ nonSeller
242
+ );
243
+
244
+ expect(result).toBeErr(Cl.error(Cl.uint(107))); // ERR_NOT_SELLER
245
+ });
246
+
247
+ it("ensures that buyer can purchase an active listing", () => {
248
+ const seller = simnet.getAccounts().get('wallet_1')!;
249
+ const buyer = simnet.getAccounts().get('wallet_2')!;
250
+
251
+ // Create listing
252
+ simnet.callPublicFn(
253
+ CONTRACT_NAME,
254
+ 'create-listing',
255
+ [
256
+ utf8(LISTING_NAME),
257
+ utf8(LISTING_DESCRIPTION),
258
+ Cl.uint(LISTING_PRICE),
259
+ Cl.uint(LISTING_DURATION)
260
+ ],
261
+ seller
262
+ );
263
+
264
+ // Purchase listing
265
+ const { result } = simnet.callPublicFn(
266
+ CONTRACT_NAME,
267
+ 'purchase-listing',
268
+ [Cl.uint(1)],
269
+ buyer
270
+ );
271
+
272
+ expect(result).toBeOk(Cl.bool(true));
273
+ });
274
+
275
+ it("ensures that purchasing a non-existent listing fails", () => {
276
+ const buyer = simnet.getAccounts().get('wallet_2')!;
277
+
278
+ const { result } = simnet.callPublicFn(
279
+ CONTRACT_NAME,
280
+ 'purchase-listing',
281
+ [Cl.uint(999)],
282
+ buyer
283
+ );
284
+
285
+ expect(result).toBeErr(Cl.error(Cl.uint(111))); // ERR_INVALID_LISTING_ID
286
+ });
287
+
288
+ it("ensures that purchasing a cancelled listing fails", () => {
289
+ const seller = simnet.getAccounts().get('wallet_1')!;
290
+ const buyer = simnet.getAccounts().get('wallet_2')!;
291
+
292
+ // Create listing
293
+ simnet.callPublicFn(
294
+ CONTRACT_NAME,
295
+ 'create-listing',
296
+ [
297
+ utf8(LISTING_NAME),
298
+ utf8(LISTING_DESCRIPTION),
299
+ Cl.uint(LISTING_PRICE),
300
+ Cl.uint(LISTING_DURATION)
301
+ ],
302
+ seller
303
+ );
304
+
305
+ // Cancel listing
306
+ simnet.callPublicFn(
307
+ CONTRACT_NAME,
308
+ 'cancel-listing',
309
+ [Cl.uint(1)],
310
+ seller
311
+ );
312
+
313
+ // Try to purchase
314
+ const { result } = simnet.callPublicFn(
315
+ CONTRACT_NAME,
316
+ 'purchase-listing',
317
+ [Cl.uint(1)],
318
+ buyer
319
+ );
320
+
321
+ expect(result).toBeErr(Cl.error(Cl.uint(106))); // ERR_INVALID_STATUS
322
+ });
323
+
324
+ it("ensures that purchasing an already sold listing fails", () => {
325
+ const seller = simnet.getAccounts().get('wallet_1')!;
326
+ const buyer1 = simnet.getAccounts().get('wallet_2')!;
327
+ const buyer2 = simnet.getAccounts().get('wallet_3')!;
328
+
329
+ // Create listing
330
+ simnet.callPublicFn(
331
+ CONTRACT_NAME,
332
+ 'create-listing',
333
+ [
334
+ utf8(LISTING_NAME),
335
+ utf8(LISTING_DESCRIPTION),
336
+ Cl.uint(LISTING_PRICE),
337
+ Cl.uint(LISTING_DURATION)
338
+ ],
339
+ seller
340
+ );
341
+
342
+ // First purchase
343
+ simnet.callPublicFn(
344
+ CONTRACT_NAME,
345
+ 'purchase-listing',
346
+ [Cl.uint(1)],
347
+ buyer1
348
+ );
349
+
350
+ // Second purchase attempt
351
+ const { result } = simnet.callPublicFn(
352
+ CONTRACT_NAME,
353
+ 'purchase-listing',
354
+ [Cl.uint(1)],
355
+ buyer2
356
+ );
357
+
358
+ expect(result).toBeErr(Cl.error(Cl.uint(106))); // ERR_INVALID_STATUS
359
+ });
360
+
361
+ it("ensures that purchasing an expired listing fails", () => {
362
+ const seller = simnet.getAccounts().get('wallet_1')!;
363
+ const buyer = simnet.getAccounts().get('wallet_2')!;
364
+ const shortDuration = 5;
365
+
366
+ // Create listing with short duration
367
+ simnet.callPublicFn(
368
+ CONTRACT_NAME,
369
+ 'create-listing',
370
+ [
371
+ utf8(LISTING_NAME),
372
+ utf8(LISTING_DESCRIPTION),
373
+ Cl.uint(LISTING_PRICE),
374
+ Cl.uint(shortDuration)
375
+ ],
376
+ seller
377
+ );
378
+
379
+ // Mine blocks to expire the listing
380
+ simnet.mineEmptyBlocks(shortDuration + 1);
381
+
382
+ // Try to purchase expired listing
383
+ const { result } = simnet.callPublicFn(
384
+ CONTRACT_NAME,
385
+ 'purchase-listing',
386
+ [Cl.uint(1)],
387
+ buyer
388
+ );
389
+
390
+ expect(result).toBeErr(Cl.error(Cl.uint(105))); // ERR_LISTING_EXPIRED
391
+ });
392
+
393
+ it("ensures that updating a cancelled listing fails", () => {
394
+ const seller = simnet.getAccounts().get('wallet_1')!;
395
+
396
+ // Create listing
397
+ simnet.callPublicFn(
398
+ CONTRACT_NAME,
399
+ 'create-listing',
400
+ [
401
+ utf8(LISTING_NAME),
402
+ utf8(LISTING_DESCRIPTION),
403
+ Cl.uint(LISTING_PRICE),
404
+ Cl.uint(LISTING_DURATION)
405
+ ],
406
+ seller
407
+ );
408
+
409
+ // Cancel listing
410
+ simnet.callPublicFn(
411
+ CONTRACT_NAME,
412
+ 'cancel-listing',
413
+ [Cl.uint(1)],
414
+ seller
415
+ );
416
+
417
+ // Try to update cancelled listing
418
+ const { result } = simnet.callPublicFn(
419
+ CONTRACT_NAME,
420
+ 'update-listing',
421
+ [
422
+ Cl.uint(1),
423
+ Cl.uint(2000000),
424
+ utf8('New description')
425
+ ],
426
+ seller
427
+ );
428
+
429
+ expect(result).toBeErr(Cl.error(Cl.uint(106))); // ERR_INVALID_STATUS
430
+ });
431
+
432
+ it("ensures that multiple listings can be created and tracked correctly", () => {
433
+ const seller1 = simnet.getAccounts().get('wallet_1')!;
434
+ const seller2 = simnet.getAccounts().get('wallet_2')!;
435
+
436
+ // Create first listing
437
+ const { result: result1 } = simnet.callPublicFn(
438
+ CONTRACT_NAME,
439
+ 'create-listing',
440
+ [
441
+ utf8('Item 1'),
442
+ utf8('First item'),
443
+ Cl.uint(LISTING_PRICE),
444
+ Cl.uint(LISTING_DURATION)
445
+ ],
446
+ seller1
447
+ );
448
+
449
+ // Create second listing
450
+ const { result: result2 } = simnet.callPublicFn(
451
+ CONTRACT_NAME,
452
+ 'create-listing',
453
+ [
454
+ utf8('Item 2'),
455
+ utf8('Second item'),
456
+ Cl.uint(LISTING_PRICE * 2),
457
+ Cl.uint(LISTING_DURATION)
458
+ ],
459
+ seller2
460
+ );
461
+
462
+ // Create third listing
463
+ const { result: result3 } = simnet.callPublicFn(
464
+ CONTRACT_NAME,
465
+ 'create-listing',
466
+ [
467
+ utf8('Item 3'),
468
+ utf8('Third item'),
469
+ Cl.uint(LISTING_PRICE * 3),
470
+ Cl.uint(LISTING_DURATION)
471
+ ],
472
+ seller1
473
+ );
474
+
475
+ expect(result1).toBeOk(Cl.uint(1));
476
+ expect(result2).toBeOk(Cl.uint(2));
477
+ expect(result3).toBeOk(Cl.uint(3));
478
+
479
+ // Verify last listing ID
480
+ const { result: lastId } = simnet.callReadOnlyFn(
481
+ CONTRACT_NAME,
482
+ 'get-last-listing-id',
483
+ [],
484
+ seller1
485
+ );
486
+
487
+ expect(lastId).toBeOk(Cl.uint(3));
488
+ });
489
+
490
+ it("ensures that get-listing returns none for invalid listing ID", () => {
491
+ const user = simnet.getAccounts().get('wallet_1')!;
492
+
493
+ const { result } = simnet.callReadOnlyFn(
494
+ CONTRACT_NAME,
495
+ 'get-listing',
496
+ [Cl.uint(999)],
497
+ user
498
+ );
499
+
500
+ expect(result).toBeNone();
501
+ });
502
+
503
+ it("ensures that updating with invalid price fails", () => {
504
+ const seller = simnet.getAccounts().get('wallet_1')!;
505
+
506
+ // Create listing
507
+ simnet.callPublicFn(
508
+ CONTRACT_NAME,
509
+ 'create-listing',
510
+ [
511
+ utf8(LISTING_NAME),
512
+ utf8(LISTING_DESCRIPTION),
513
+ Cl.uint(LISTING_PRICE),
514
+ Cl.uint(LISTING_DURATION)
515
+ ],
516
+ seller
517
+ );
518
+
519
+ // Try to update with invalid price
520
+ const { result } = simnet.callPublicFn(
521
+ CONTRACT_NAME,
522
+ 'update-listing',
523
+ [
524
+ Cl.uint(1),
525
+ Cl.uint(0),
526
+ utf8('New description')
527
+ ],
528
+ seller
529
+ );
530
+
531
+ expect(result).toBeErr(Cl.error(Cl.uint(102))); // ERR_INVALID_PRICE
532
+ });
533
+
534
+ it("ensures that updating with empty description fails", () => {
535
+ const seller = simnet.getAccounts().get('wallet_1')!;
536
+
537
+ // Create listing
538
+ simnet.callPublicFn(
539
+ CONTRACT_NAME,
540
+ 'create-listing',
541
+ [
542
+ utf8(LISTING_NAME),
543
+ utf8(LISTING_DESCRIPTION),
544
+ Cl.uint(LISTING_PRICE),
545
+ Cl.uint(LISTING_DURATION)
546
+ ],
547
+ seller
548
+ );
549
+
550
+ // Try to update with empty description
551
+ const { result } = simnet.callPublicFn(
552
+ CONTRACT_NAME,
553
+ 'update-listing',
554
+ [
555
+ Cl.uint(1),
556
+ Cl.uint(2000000),
557
+ utf8('')
558
+ ],
559
+ seller
560
+ );
561
+
562
+ expect(result).toBeErr(Cl.error(Cl.uint(109))); // ERR_INVALID_INPUT
563
+ });
564
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+
2
+ {
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "useDefineForClassFields": true,
6
+ "module": "ESNext",
7
+ "lib": ["ESNext"],
8
+ "skipLibCheck": true,
9
+
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+
16
+ "strict": true,
17
+ "noImplicitAny": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noFallthroughCasesInSwitch": true
21
+ },
22
+ "include": [
23
+ "node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src",
24
+ "tests"
25
+ ]
26
+ }
@@ -0,0 +1,49 @@
1
+
2
+ /// <reference types="vitest" />
3
+
4
+ import { defineConfig } from "vite";
5
+ import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest";
6
+
7
+ /*
8
+ In this file, Vitest is configured so that it works seamlessly with Clarinet and the Simnet.
9
+
10
+ The `vitest-environment-clarinet` will initialise the clarinet-sdk
11
+ and make the `simnet` object available globally in the test files.
12
+
13
+ `vitestSetupFilePath` points to a file in the `@hirosystems/clarinet-sdk` package that does two things:
14
+ - run `before` hooks to initialize the simnet and `after` hooks to collect costs and coverage reports.
15
+ - load custom vitest matchers to work with Clarity values (such as `expect(...).toBeUint()`)
16
+
17
+ The `getClarinetVitestsArgv()` will parse options passed to the command `vitest run --`
18
+ - vitest run -- --manifest ./Clarinet.toml # pass a custom path
19
+ - vitest run -- --coverage --costs # collect coverage and cost reports
20
+ */
21
+
22
+ export default defineConfig({
23
+ test: {
24
+ environment: "clarinet", // use vitest-environment-clarinet
25
+ pool: "forks",
26
+ poolOptions: {
27
+ threads: { singleThread: true },
28
+ forks: { singleFork: true },
29
+ },
30
+ setupFiles: [
31
+ vitestSetupFilePath,
32
+ // custom setup files can be added here
33
+ ],
34
+ environmentOptions: {
35
+ clarinet: {
36
+ ...getClarinetVitestsArgv(),
37
+ // add or override options
38
+ },
39
+ },
40
+ exclude: [
41
+ '**/node_modules/**',
42
+ '**/dist/**',
43
+ '**/frontend/**', // Exclude frontend tests from root config
44
+ '**/.{idea,git,cache,output,temp}/**',
45
+ '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*'
46
+ ],
47
+ },
48
+ });
49
+